Segment your ICS network in minutes, not months.
An open-source next-generation firewall purpose-built for ICS/OT network segmentation. containd is a single-container appliance that combines zone-based firewalling, ICS protocol deep packet inspection, embedded network services, and a full management UI. Designed for industrial control system operators who need OT-aware security without assembling dozens of point tools.
curl -fsSLO https://raw.githubusercontent.com/tonylturner/containd/main/scripts/quickstart.sh
sh quickstart.shquickstart.sh downloads the starter compose, creates .env from .env.example, generates a real CONTAIND_JWT_SECRET, auto-adjusts the starter subnets if they would overlap with existing Docker networks, and starts the combined appliance (containd all) for you.
The published starter compose wires the management plane to the local engine automatically, enables enforcement by default, and gives containd a stable Docker-managed lab topology: wan, dmz, and lan1 through lan6 mapped to eth0 through eth7.
Docker/Compose owns the available interfaces and their Docker-level IP wiring. Use containd to bind zones, tighten policy, and configure services inside that topology. If you want different lab subnets or interface addresses, edit .env before you start the stack.
containd is designed for container-to-container segmentation inside Docker-based labs. Linux Docker hosts are supported, and Docker Desktop is also a valid lab target because the traffic being segmented lives inside Docker's Linux VM. On Windows, use Docker Desktop with the WSL2 backend. containd is not a native host firewall for macOS or Windows networking.
For a customizable starter directory instead of the zero-touch path above:
curl -fsSLO https://raw.githubusercontent.com/tonylturner/containd/main/scripts/bootstrap-starter.sh
sh bootstrap-starter.sh --dir containd-lab --no-startThat writes docker-compose.yml, .env.example, and .env, auto-adjusts conflicting default starter subnets on fresh setup, lets you customize .env, and then you can start with docker compose up -d.
| Service | URL |
|---|---|
| Web UI / API | http://localhost:8080 |
| HTTPS | https://localhost:8443 |
| SSH console | ssh -p 2222 containd@localhost |
Default credentials: containd / containd -- change on first login.
- Log in and change the default password.
- Create your first zones (
WAN,DMZ,LAN/OT). - Bind interfaces to those zones.
- Use the Policy Wizard or Firewall Rules page to create initial access policy.
- Review the candidate config diff and commit it so the runtime actually changes.
Dashboard with live network topology, service health, traffic pulse, and zone status
Traditional IT firewalls don't understand ICS protocols. They can't distinguish a legitimate Modbus register read from a malicious write, or detect anomalous DNP3 function codes. OT environments need protocol-aware segmentation, but deploying and integrating separate tools for firewalling, DPI, IDS, asset inventory, and network services is complex and fragile. containd puts all of this in a single container with one config model and one UI.
Firewall -- Zone-based with nftables enforcement, NAT (SNAT + DNAT), default-deny posture, and optional eBPF XDP/TC acceleration.
ICS/OT Deep Packet Inspection -- Native Go decoders for Modbus, DNP3, CIP/EtherNet/IP, S7comm, IEC 61850 MMS, BACnet, OPC UA, plus DNS, TLS/JA3, HTTP, SSH, RDP, SMB, SNMP, NTP. Per-protocol enable/disable, learn-then-enforce workflow, function code and register-level visibility.
ICS Security -- Asset auto-discovery from traffic, learn mode for auto-generating allowlist rules, protocol anomaly detection, built-in ICS malware signatures, Sigma-compatible IDS rules, PCAP offline analysis.
Embedded Services -- DNS (Unbound), NTP (OpenNTPD), DHCP, forward proxy (Envoy), reverse proxy (Nginx), VPN (WireGuard + OpenVPN), antivirus (ClamAV via ICAP).
Management -- Web UI with dashboard, topology, firewall rules, routing, NAT, services, monitoring, and diagnostics. SSH console with appliance-style CLI. REST API, Prometheus metrics, syslog forwarding, event export (CEF/JSON/Syslog).
Config Lifecycle -- Candidate/running configs with commit-confirmed and auto-rollback, deterministic JSON export/import, schedule and identity predicates on rules, ICS policy templates for rapid deployment.
Single Go binary, three logical planes:
- Data plane -- nftables/conntrack, NFQUEUE selective DPI steering, TCP reassembly, per-flow verdict caching, IDS/IPS
- Control plane -- SQLite persistence, policy compilation, service management, audit logging
- Management plane -- REST API, web UI, SSH console, auth/RBAC
Run modes: containd all (combined), containd mgmt, containd engine.
The published starter compose (deploy/docker-compose.yml) and the source-build dev compose (deploy/docker-compose.dev.yml) both create 8 isolated Docker networks:
| Network | Subnet | Interface |
|---|---|---|
| WAN | 192.168.240.0/24 | eth0 |
| DMZ | 192.168.241.0/24 | eth1 |
| LAN1 | 192.168.242.0/24 | eth2 |
| LAN2 | 192.168.243.0/24 | eth3 |
| LAN3 | 192.168.244.0/24 | eth4 |
| LAN4 | 192.168.245.0/24 | eth5 |
| LAN5 | 192.168.246.0/24 | eth6 |
| LAN6 | 192.168.247.0/24 | eth7 |
By default, the starter compose pins containd to .2 on each subnet, keeps WAN as the default-gateway network, enables nftables enforcement, and runs the appliance as root inside the container so nftables, routing, and TUN operations work across Linux, Docker Desktop, and WSL-backed lab environments. Edit .env to change those subnets or addresses for your lab. For Docker-based deployments, treat Compose as the owner of network attachment and IP layout; use the containd UI/API to bind interfaces to zones and enforce segmentation inside that layout.
docker run -d \
--name containd \
--user 0 \
--cap-add NET_ADMIN --cap-add NET_RAW \
--cap-add NET_BIND_SERVICE \
--device /dev/net/tun:/dev/net/tun \
-p 8080:8080 -p 8443:8443 -p 2222:2222 \
-v containd-data:/data \
-e CONTAIND_JWT_SECRET=$(openssl rand -hex 32) \
ghcr.io/tonylturner/containd:latestCombined mode auto-connects the management plane to the local dataplane engine. Set CONTAIND_ENGINE_URL only when you intentionally split mgmt and engine.
go build -o containd ./cmd/containd
cd ui && npm ci && npm run build && cd ..
CONTAIND_UI_DIR=ui/out ./containd all- Default-deny firewall posture
- Distroless container image; the published starter runs as
rootinside the container so nftables, routing, and TUN operations work reliably across Docker lab runtimes - JWT auth with session invalidation, admin/view-only roles, MustChangePassword on first login, and optional app-based TOTP MFA for local accounts
- TLS 1.2+ with hardened cipher suites, HSTS enabled by default
- CORS wildcard rejection, same-origin protection for cookie-authenticated browser writes, SameSite=Strict cookies, request body limits, path traversal protection
- Rate limiting on auth endpoints, nftables injection prevention
- Cosign-signed container images with CycloneDX SBOM attestation
- Trivy vulnerability scanning in CI (zero HIGH/CRITICAL)
See SECURITY.md for vulnerability reporting and hardening guidance, Secure by Design for the CISA alignment roadmap, and Advisories and CSAF for the public advisory process.
Full docs are embedded in the appliance (Help icon in UI) and built from docs/mkdocs/:
- Architecture | Dataplane | eBPF
- Docker Compose Deployment | Customizing Lab Compose | Windows / WSL Notes | Host Deploy
- CLI Reference | Config Format
- ICS DPI | IDS Rules | Policy Model
- Services | API Reference | Secure by Design | Advisories and CSAF
- Logging and Evidence | Update Policy
- SBOM | Third-Party Licenses
OpenAPI 3.0 specification for the REST API.
Contributions welcome -- ICS protocol decoders, policy templates, documentation, and bug reports are all valuable. See CONTRIBUTING.md.
Apache License 2.0 -- see LICENSE.