Docker cyber range
A portable, isolated range that runs on one laptop or an approved cloud sandbox. Six segmented networks model a realistic IT + OT enterprise. Every target stays inside the range.
Allowed flows: external → proxy only; corp-user → server-net via proxy; sec-mgmt collects logs from all; IT reaches OT only through the IDMZ. Direct corp-user → ot-control is always blocked.
| Container | Role | Segment | Image | Port binding | CPU | RAM | Notes |
|---|---|---|---|---|---|---|---|
| student-ws | Student workstation | corp-user | training/student-ws:latest | 127.0.0.1:6080→6080 (noVNC) | 1.0 | 1g | Tools & browser; entry point for exercises. |
| proxy | Reverse proxy | corp-user | nginx:alpine | 127.0.0.1:8443→443 | 0.25 | 256m | Front-ends web services; TLS practice. |
| linux-srv | Linux server | server-net | debian:stable-slim | internal only | 0.5 | 512m | General services host. |
| webapp | Web application | server-net | bkimminich/juice-shop | 127.0.0.1:3000→3000 | 1.0 | 1g | Vulnerable training app (e.g., Juice Shop) — isolated only. |
| api-svc | API service | server-net | training/api-svc:latest | 127.0.0.1:8081→8080 | 0.5 | 512m | REST API target for API-security labs. |
| db | Database | server-net | postgres:16-alpine | internal only | 0.5 | 512m | Synthetic data only; no real records. |
| dns | DNS service | server-net | internetsystemsconsortium/bind9 | internal 53/udp | 0.25 | 256m | Name resolution for the range. |
| log-srv | Logging server | sec-mgmt | training/rsyslog:latest | internal 514/udp | 0.5 | 512m | Central log collector. |
| siem | SIEM / log analysis | sec-mgmt | training/siem:latest | 127.0.0.1:5601→5601 | 2.0 | 4g | Detection & hunting (training build). |
| sensor | Network-monitoring sensor | sec-mgmt | training/suricata:latest | internal only | 1.0 | 1g | Traffic capture / IDS training. |
| idp | Identity / auth service | sec-mgmt | training/idp:latest | 127.0.0.1:8080→8080 | 0.5 | 1g | Directory / SSO simulation. |
| jump | Secure jump server | sec-mgmt | training/jump:latest | 127.0.0.1:2222→22 | 0.25 | 256m | Controlled access to sensitive segments. |
| historian | Historian (optional) | idmz | training/historian:latest | internal only | 0.5 | 512m | Time-series data store for OT labs. |
| plc-sim | PLC / process simulator | ot-control | training/openplc:latest | internal 502/tcp | 0.5 | 512m | Simulated industrial process — never a real one. |
| hmi-sim | HMI simulator | ot-control | training/hmi:latest | 127.0.0.1:8090→8080 | 0.5 | 512m | Operator interface simulation. |
| attacker | Simulated attacker workstation | external | kalilinux/kali-rolling | none (exec in) | 1.0 | 2g | Kali-style toolset; range-internal only. |
- 1Confirm Docker Engine 24+ and Compose v2 are installed and the host has 16 GB RAM free.
- 2Confirm the host is NOT on a production network; the range must be isolated.
- 3Clone the range folder and review docker-compose.yml before starting anything.
- 4Start the IT half first: `docker compose --profile it up -d`.
- 5Verify health: `docker compose ps` — every service reports healthy.
- 6Start the OT half only when the week calls for it: `docker compose --profile ot up -d`.
- 7Snapshot a clean state before the exercise begins.
- 8At the end of class run the reset: `docker compose down -v`.
- Vulnerable applications run only inside the isolated range.
- Bind services to localhost wherever practical; disable unnecessary external exposure.
- No real credentials, personal information, production data, or active malware.
- Synthetic user accounts and synthetic data only.
- Health checks, central logging, and a documented reset script for a clean state.
- Snapshot-and-recovery procedure documented; Windows, macOS and Linux guidance included.
- Offline classroom deployment and approved cloud-sandbox deployment both supported.
docker compose down -v docker compose --profile it up -d docker compose ps
# G3 Cyber Range - reference skeleton (training use, isolated networks only)
name: g3-cyber-range
networks:
corp-user: { driver: bridge, internal: true }
server-net: { driver: bridge, internal: true }
sec-mgmt: { driver: bridge, internal: true }
idmz: { driver: bridge, internal: true }
ot-control: { driver: bridge, internal: true }
external: { driver: bridge, internal: true }
volumes:
db-data:
log-data:
siem-data:
x-hardening: &hardening
security_opt: ["no-new-privileges:true"]
restart: unless-stopped
services:
student-ws:
<<: *hardening
image: training/student-ws:latest
networks: [corp-user]
ports: ["127.0.0.1:6080:6080"]
profiles: ["it"]
healthcheck:
test: ["CMD", "true"]
interval: 30s
webapp:
<<: *hardening
image: bkimminich/juice-shop
networks: [server-net]
ports: ["127.0.0.1:3000:3000"]
profiles: ["it"]
plc-sim:
<<: *hardening
image: training/openplc:latest
networks: [ot-control]
profiles: ["ot"]
# Simulation only. Never connect this service to real equipment.
16-step Docker lab progression
Objective: Operate the range safely from a clean state.
Bring up student-ws, stop it, inspect it, then reset the range and confirm a clean state.
Objective: Distinguish the five core Docker objects.
List images, containers, networks and volumes; explain what each port binding does.
Objective: Find evidence in logs.
Tail proxy and webapp logs during a login attempt; capture the request line.
Objective: Enumerate what is reachable and from where.
From student-ws, scan server-net and record the open ports; repeat from external.
Objective: Tie traffic to layers.
For three flows, name the layer, protocol, data unit and control at each step.
Objective: Read a capture and spot the lure.
Capture on sensor while a simulated phishing click occurs; annotate the PCAP.
Objective: Produce a verified inventory.
Enumerate corp-user and server-net; produce an asset table with evidence.
Objective: Recognize and later remediate app flaws.
Fingerprint webapp; list three candidate weaknesses with CWE IDs. Week 10: patch and re-test.
Objective: Harden without breaking the service.
Apply non-root user, read-only filesystem, dropped capabilities and resource limits; prove the app still works.
Objective: Reconstruct an auth timeline.
From idp and jump logs, reconstruct who accessed what and when.
Objective: Get sources into one place.
Forward proxy, idp and sensor logs to siem; confirm ingestion and build one dashboard.
Objective: Write and test a detection.
Run the provided attack script; write one detection rule and map it to ATT&CK.
Objective: Prove OT is unreachable from IT.
Verify ot-control is reachable only via idmz; document the path and the block.
Objective: Validate rules by testing them.
Test each ACL rule for allow and deny; record the evidence pair.
Objective: Trace a pivot into OT.
Trace the pivot from webapp → jump → historian → plc-sim; build the timeline.
Objective: Recover and prove it.
Restore from snapshot, apply remediation, and re-run steps 4, 12 and 15 to prove closure.
Troubleshooting
Cause: Another service already owns the loopback port.
Fix: Change the left side of the mapping only (e.g. 127.0.0.1:3001:3000); never publish to 0.0.0.0.
Cause: Missing volume, bad entrypoint, or read-only filesystem without tmpfs.
Fix: Run `docker compose logs <svc>`; add the tmpfs mount; re-run.
Cause: Log forwarding blocked between segments.
Fix: Confirm log-srv and siem share sec-mgmt and that UDP 514 is permitted; restart the forwarder.
Cause: A service is attached to two networks.
Fix: Remove the extra network from the service and re-create; re-run Docker lab 13 to verify.
Cause: SIEM plus all targets on an 8 GB host.
Fix: Use the split profiles: `--profile it` or `--profile ot` instead of the full range.
Cause: Volumes persisted between runs.
Fix: Run the documented reset: `docker compose down -v && docker compose up -d`.
Offline classroom: pre-pull images to a local registry or USB, then run with no internet. Cloud sandbox: deploy in an isolated VPC/subnet with no public exposure. Windows: use WSL2 backend. macOS: Docker Desktop; allocate 12 GB+. Linux: Docker Engine + Compose plugin. Snapshot a clean state before each exercise and restore from it afterward.