GridGuard is a secure digital-twin lab for a power distribution grid. The project joins power-system simulation with DevSecOps infrastructure so the team can generate realistic SCADA-style telemetry, move it across a deliberately modeled OT/IT boundary, attack it, detect it, and document the full red/blue exercise.
The goal is not just to build a demo. The goal is to create a reproducible integration environment where every major piece can be rebuilt, tested, scanned, and defended with the same discipline expected from critical-infrastructure software.
GridGuard models a distribution feeder, exposes live measurements over an industrial protocol, ingests those measurements into a cloud-native telemetry pipeline, and layers monitoring plus security detection around the data flow.
Planned deliverables:
- A working cloud-deployed digital twin with dashboard visibility.
- A documented OT/IT threat model.
- At least two attack scenarios, including false data injection.
- Reproducible infrastructure as code.
- CI/CD gates for quality, security, infrastructure, and container checks.
- A final written report with architecture, attack runs, detection results, and lessons learned.
The overview separates the locally verified telemetry path from the Terraform-defined AWS target. Blue carries telemetry, green carries detection or review, red marks attack or blocked paths, and dashed gray marks delivery guardrails. The Modbus ingestor is the sole service permitted to cross the simulated OT/cloud boundary.
The full architecture guide includes a detailed AWS deployment view, an attack-to-alert data-flow view, and the exact status, trust invariants, omissions, and recorded outcomes behind each figure. Diagram provenance records the official icon sources and render-validation process.
.
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── ci.yml
│ └── deploy.yml
├── infra/
│ ├── images/
│ ├── local/
│ ├── services/
│ └── terraform/environments/
│ ├── aws-sandbox/
│ └── local-dev/
├── scripts/ci/
│ ├── all.sh
│ ├── validate-docs.sh
│ ├── validate-docker.sh
│ ├── validate-python.sh
│ ├── validate-repo-hygiene.sh
│ ├── validate-workflows.sh
│ └── validate-terraform.sh
├── docs/
│ ├── devsecops-track/
│ └── power-track/
├── power-sim/
├── Makefile
└── README.md
Important entry points:
The Power Systems track owns the physics and telemetry source:
- Build and validate an IEEE 13-bus or 34-bus distribution feeder.
- Run time-series simulation with load variation and distributed generation.
- Expose measurements through Modbus TCP first, with DNP3 as a stretch goal.
- Build baseline bad-data detection.
- Design naive and stealthy false data injection attacks.
Source will live in power-sim.
The DevSecOps track owns the cloud pipeline and defenses:
- Build cloud infrastructure with Terraform.
- Preserve OT/cloud network segmentation.
- Ingest telemetry into a time-series database.
- Provision dashboards and observability.
- Add IDS, anomaly detection, SIEM alerting, and secrets management.
- Maintain CI/CD quality and security gates.
Source will live in infra.
The repository contains a working local red/blue lab and a validated real AWS
Kubernetes deployment. A pandapower model of the IEEE 13-node feeder runs a
deterministic 24-hour demand and PV profile, serves measurements over Modbus
TCP, and feeds InfluxDB through the receiver-side ingestor. Grafana dashboards
and alert rules cover the telemetry, while naive and coordinated in-envelope
attack replays exercise the detection path.
The single Amazon EKS cluster in us-east-1 completed end-to-end validation
with two private workers, strict VPC CNI network-policy enforcement, three
default-deny namespaces, all intended allow and deny probes, all three
telemetry scenarios, authenticated AWS Console evidence, and a zero-drift
Terraform plan. The final technical report
and sanitized execution record
contain the evidence. The EKS resources remain live and billable only for final
review; controlled teardown is the next operational action.
Current CI is intentionally future-ready:
- Documentation and repository hygiene checks run immediately.
- Python validation activates once Python files exist.
- Terraform formatting, validation, and native tests run for applicable roots.
- Docker and Compose validation activate once container artifacts exist.
- Gitleaks and Trivy run in GitHub Actions for security coverage.
Clone the repository:
git clone https://github.com/AnouarMohamed/grid-scada-security.git
cd grid-scada-securityRun the full local validation suite:
make ciRun focused checks:
make docs
make python
make terraform
make dockerThe local suite is designed to skip surfaces that do not exist yet, while still becoming strict as new source files are added.
Start the local fake-data stack:
cp .env.example .env
make stack-up
make stack-smoke
make stack-dashboard-smokeThe dashboard smoke test also verifies the Grafana alert rules and required InfluxDB telemetry tags.
Grafana is available on http://127.0.0.1:3000. InfluxDB is available on
http://127.0.0.1:8086. See
docs/07-local-fake-data-pipeline.md for
the full runbook.
Exercise the receiver-side Modbus contract with fixture registers:
make stack-modbus-up
make stack-modbus-smokeSee docs/08-modbus-handoff-contract.md for the register-map contract and simulator handoff rules.
Run the complete local simulator pipeline:
make stack-live-up
make stack-live-smoke
make stack-dashboard-smokeReplay the two attack scenarios:
make stack-naive-up
make stack-naive-smoke
make stack-stealthy-up
make stack-stealthy-smokeSee docs/10-local-red-blue-lab.md for the workflow and expected detector behavior.
The main CI workflow is .github/workflows/ci.yml.
It runs on pull requests, pushes to main, and manual dispatch.
CI jobs:
- Repository Hygiene: line endings, final newline, trailing whitespace, ignored tracked files, oversized files, and secret-ignore sanity checks.
- Documentation: Markdown fence balance and relative-link validation.
- CloudFormation: template parsing and exact plan-only OIDC policy/trust invariants.
- Python Lint, Test, SAST, and Audit: reproducibly pinned tooling, Ruff, Bandit, pip-audit, pytest on Python 3.12 and 3.14, and an 80% aggregate coverage floor.
- Workflow Policy: actionlint and yamllint plus immutable action revision, explicit permissions, and event-trigger checks.
- Terraform Format, Validate, and Test:
terraform fmt, init without a backend, validate, and native tests when a root containstests/. - Docker and Compose Validation: Compose config validation, Docker image builds, vulnerability reporting, and a blocking critical-vulnerability gate.
- Secrets and Dependency Scans: Gitleaks plus Trivy filesystem, secret, and misconfiguration scanning.
- CI Gate: single required status check for branch protection.
Manual deployment is defined in
.github/workflows/deploy.yml. It runs only
Terraform plan for the repository's AWS sandbox root, uses the protected
sandbox GitHub environment, and authenticates through a bounded OIDC role
instead of static access keys. Runtime apply remains a separate local operator
procedure.
The main branch is protected with:
- Require pull requests into
main. - Require an up-to-date, passing
CI Gate. - Enforce the rules for administrators.
- Require review conversations to be resolved.
- Require branches to be up to date before merge.
- Disable force pushes and branch deletion.
The approval count remains zero while this is a solo-maintainer repository. Add at least one required approval when a second maintainer joins.
GridGuard succeeds only when both tracks meet at defined handoff points.
- Telemetry handoff: Power track provides live Modbus telemetry and a register map; DevSecOps confirms ingestion into database and dashboard.
- First attack run: Power track injects an obvious bad value; DevSecOps confirms detection and SIEM alerting.
- Stealthy attack run: both tracks measure whether topology-consistent FDIA is detected and how long detection takes.
- Final review: validate the full system, report, and demo path.
Details are in docs/04-integration-checkpoints.md.
- Keep the OT/IT boundary visible in architecture, network policy, and tests.
- Never commit credentials, tokens, private keys,
.envfiles, state files, or local cloud configuration. - Prefer OIDC and short-lived cloud credentials for CI/CD.
- Treat Terraform state as sensitive.
- Add detection logic with repeatable attack logs, not one-off manual demos.
- Keep every attack scenario grounded in realistic power-system security behavior.
Near-term:
- Separately design the apply role and environment approval without broadening the verified plan-only identity.
- Configure private operator access and the secret procedure; then enable runtime with zero tasks before scaling services to one.
- Calibrate the balanced feeder approximation against published IEEE reference results or promote it to an unbalanced model.
- Add a residual/state-estimation detector beyond envelope checks.
Mid-term:
- Add Suricata or Zeek rules for suspicious OT traffic.
- Add Wazuh or another SIEM target for detection events.
- Turn the coordinated in-envelope replay into a state-estimator-derived FDIA.
Later:
- Add mTLS between services.
- Add SIEM alert wiring.
- Add Terraform plan artifacts to pull requests.
- Add SBOM generation and signed image publishing.
- Add automated end-to-end red/blue smoke tests.
Before opening or merging a change:
- Read the relevant track execution plan.
- Keep changes scoped to the correct track directory.
- Run
make ci. - Update docs when interfaces change, especially Modbus registers, infrastructure assumptions, or attack-run behavior.
- Use pull requests into
mainonce branch protection is enabled.
For shared vocabulary, see docs/00-glossary.md.