Container hardening guides¶
Every guide below takes one popular Docker image, grades its default docker run on IronClaw's
seven-dimension containment scale, and shows the exact ironctl scan --fix flags that close the gap.
The grades are not hand-waved: each default grade comes from a read-only inspect of a running
container started from the real image with plain docker run defaults, its entrypoint overridden
with sleep purely to keep it alive. The scan itself executes nothing inside the container. It is
the same data behind that image's isolation scorecard.
Two patterns show up across the set:
- Datastores and sandboxes reach grade A. A database or a code sandbox that only its co-located
services talk to can take
--network=noneand hit 100/100. Root, capabilities, and a read-only rootfs are the whole game. - Network services hit an honest ceiling at grade B. A broker, cache, proxy, or secrets server
exists to be connected to, so
--network=nonewould score the last points but break the service. The honest ceiling is 89/100, grade B, with the network dimension held at a WARN and contained with a scoped private network instead. We say so on every page rather than inflate the number.
Hardening once is the easy half. To keep the grade you just earned, put the same
grader on every pull request with the
GitHub Action: one uses: IronSecCo/ironclaw@v1 line, about
30 seconds, no account and no credentials.
Reach grade A (100/100)¶
| Service | Default | Hardened | The gap that closes |
|---|---|---|---|
| Postgres | 48/100 D | 100/100 A | root, full caps, writable rootfs |
| MySQL | 48/100 D | 100/100 A | root, full caps, writable rootfs |
| MariaDB | 48/100 D | 100/100 A | root, full caps, writable rootfs |
| MongoDB | 48/100 D | 100/100 A | root, full caps, writable rootfs |
| Redis | 48/100 D | 100/100 A | root, full caps, writable rootfs |
| Cassandra | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B multi-node) |
| ClickHouse | 48/100 D | 100/100 A | root, full caps, writable rootfs |
| Elasticsearch | 63/100 C | 100/100 A | full caps, writable rootfs (89/B multi-node) |
| InfluxDB | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B if fleet pushes metrics) |
| Neo4j | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B if clustered or remote clients) |
| CouchDB | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B if clustered or remote clients) |
| QuestDB | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B if a fleet pushes metrics) |
| CockroachDB | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B if clustered or remote clients) |
| TimescaleDB | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B with replicas or remote clients) |
| Valkey | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B as a shared network cache) |
| Loki | 63/100 C | 100/100 A | full caps, writable rootfs (89/B if a fleet pushes logs) |
| Tempo | 63/100 C | 100/100 A | full caps, writable rootfs (89/B if a fleet pushes traces) |
| Meilisearch | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B if remote clients) |
| YugabyteDB | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B multi-node cluster) |
| Dragonfly | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B as a shared network cache) |
| OpenSearch | 63/100 C | 100/100 A | full caps, writable rootfs (89/B multi-node cluster) |
| Qdrant | 48/100 D | 100/100 A | root, full caps, writable rootfs |
| ArangoDB | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B if clustered or remote clients) |
| Solr | 63/100 C | 100/100 A | full caps, writable rootfs (89/B multi-node cluster or remote clients) |
| VictoriaMetrics | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B if a fleet pushes metrics) |
| ScyllaDB | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B multi-node cluster) |
| Dgraph | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B if clustered or remote clients) |
| Weaviate | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B if remote clients) |
| RethinkDB | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B if clustered or remote clients) |
| TiDB | 48/100 D | 100/100 A | root, full caps, writable rootfs (89/B production cluster or remote clients) |
| Untrusted Node.js | 48/100 D | 100/100 A | run untrusted code in a real sandbox |
Honest ceiling: grade B (89/100)¶
These services must accept client connections, so the network dimension holds at a WARN by design.
| Service | Default | Hardened | Why 89 is the ceiling |
|---|---|---|---|
| Kafka | 63/100 C | 89/100 B | broker: producers and consumers must connect |
| RabbitMQ | 48/100 D | 89/100 B | broker: every service connects to the queue |
| Memcached | 63/100 C | 89/100 B | cache: clients read and write over the network |
| nginx | 48/100 D | 89/100 B | proxy: it exists to forward traffic |
| Vault | 48/100 D | 89/100 B | secrets server: apps must reach the API |
| Consul | 48/100 D | 89/100 B | service mesh: peers and clients must connect |
| ZooKeeper | 48/100 D | 89/100 B | coordination server: peers and clients must connect |
| MinIO | 48/100 D | 89/100 B | object store: S3 clients must reach the API |
| Grafana | 63/100 C | 89/100 B | dashboard: browsers must reach the UI |
| Prometheus | 63/100 C | 89/100 B | metrics: it scrapes targets and serves its API |
| Traefik | 48/100 D | 89/100 B | proxy: it exists to accept and forward traffic |
| Jenkins | 63/100 C | 89/100 B | CI server: agents and browsers must reach it |
| SonarQube | 63/100 C | 89/100 B | code-quality server: scanners and browsers must reach it |
| Keycloak | 63/100 C | 89/100 B | identity provider: every app must reach its endpoints |
| NATS | 48/100 D | 89/100 B | broker: publishers and subscribers must connect |
| Gitea | 48/100 D | 89/100 B | git server: developers and CI must reach it |
| HAProxy | 63/100 C | 89/100 B | load balancer: it exists to accept and forward traffic |
| Typesense | 48/100 D | 89/100 B | search server: your app must reach its query API |
| Immich | 48/100 D | 89/100 B | photo server: browsers and mobile apps must reach it |
| pgAdmin | 63/100 C | 89/100 B | admin console: your browser must reach the UI |
| Redpanda | 63/100 C | 89/100 B | broker: producers and consumers must connect |
| EMQX | 63/100 C | 89/100 B | MQTT broker: publishers and subscribers must connect |
| Kong | 63/100 C | 89/100 B | API gateway: clients and upstreams connect through it |
| Envoy | 48/100 D | 89/100 B | proxy: it exists to accept and forward traffic |
| Pulsar | 63/100 C | 89/100 B | broker: producers and consumers must connect |
| NSQ | 48/100 D | 89/100 B | broker: publishers and subscribers must connect |
The pattern, one command¶
Every grade on this page comes from the same tool. Point it at any running container, a
docker-compose.yml service, or a Kubernetes manifest, and it grades the real thing, then prints the
fixes:
# install (Homebrew)
brew install ironsecco/ironclaw/ironclaw
# grade it, then print the exact hardening flags
ironctl scan my-container
ironctl scan my-container --fix
Want it in CI? The same engine ships as a GitHub Action on the Marketplace that scores every pull request and posts the grade as a sticky comment.
Keep going¶
- Scan any container in 10 seconds →: the full
ironctl scanreference. - Container Isolation Scores →: default-config grades for the most-pulled public images.
- The State of Container Isolation, 2026 →: the survey the whole directory is built from.
- Run untrusted code in a real sandbox →: IronClaw wraps every AI-agent session in a gVisor/Kata boundary with
network=noneby default.