{
  "format_version": 3,
  "policy": {
    "external_id": "32f8e6e9835838c61b8969dbea87709b",
    "name": "Container Configuration Hardening - Level 1",
    "version": "1.1.1",
    "description": "Per-container configuration checks evaluated server-side against the inventory collected on each heartbeat. Covers image identity (tag pinning, digest pinning, registry source) and network configuration (host-network isolation). Requires OpenSCM >= 0.5.0 and the Linux agent with Docker or Podman installed. Lightweight: every test reads from cached container metadata — no agent round-trip — so the whole policy evaluates in milliseconds regardless of fleet size.",
    "author": "OpenSCM"
  },
  "tests": [
    {
      "external_id": "93868d255cfbf3e2736a41fde2c937f1",
      "name": "Container image is not tagged ':latest'",
      "description": "Containers should reference an explicit image tag (or be pinned by digest), never the floating ':latest' tag. ':latest' silently rebases the image when the registry is updated, breaking reproducibility and making it impossible to audit what code was actually running at a point in time.",
      "rational": "Pinned tags or digests give auditors a deterministic answer to 'what binary was running on host X on day Y'. ':latest' makes that question unanswerable.",
      "remediation": "Re-deploy the container with an explicit version tag, e.g. `nginx:1.27-alpine` instead of `nginx:latest`. Even better, pin by digest: `nginx@sha256:...`.",
      "severity": "Medium",
      "filter": "all",
      "app_filter": null,
      "conditions": [
        {
          "type": "condition",
          "element": "IMAGE",
          "input": "",
          "selement": "TAG",
          "condition": "NOT EQUALS",
          "sinput": "latest"
        }
      ],
      "applicability": [
        {
          "type": "applicability",
          "element": "CONTAINER",
          "input": "",
          "selement": "EXISTS",
          "condition": null,
          "sinput": null
        }
      ]
    },
    {
      "external_id": "3254d344876f148e37de775f4ef8d309",
      "name": "Container image source is declared (registry host explicit)",
      "description": "The image reference should include an explicit registry host (e.g. `registry.corp.example.com/nginx:1.27` or `docker.io/library/nginx:1.27`). When the registry portion is omitted, Docker falls back to docker.io implicitly — fine in dev, but in production this hides the actual trust boundary.",
      "rational": "Explicit registry references prevent dependency confusion (a public image with the same short name as an internal one) and document the supply chain.",
      "remediation": "Re-tag the image with its full reference: `docker tag nginx:1.27 registry.corp.example.com/nginx:1.27` and re-deploy.",
      "severity": "Low",
      "filter": "all",
      "app_filter": null,
      "conditions": [
        {
          "type": "condition",
          "element": "IMAGE",
          "input": "",
          "selement": "SOURCE",
          "condition": "CONTAINS",
          "sinput": "."
        }
      ],
      "applicability": [
        {
          "type": "applicability",
          "element": "CONTAINER",
          "input": "",
          "selement": "EXISTS",
          "condition": null,
          "sinput": null
        }
      ]
    },
    {
      "external_id": "01ddcb40649e8a9e5839ed93e7ac669e",
      "name": "Container does not share the host's network namespace",
      "description": "Containers should not run with `--network=host`. Host networking bypasses the container's network isolation entirely — the container sees and can bind every interface and port on the host, defeating the namespacing that's normally the primary security boundary.",
      "rational": "CIS Docker Benchmark 5.9. Sharing the host network is rarely necessary and removes the strongest isolation property of containers.",
      "remediation": "Re-deploy without `--network=host`. Use the default bridge network or a user-defined network instead, and publish only the specific ports the container needs.",
      "severity": "High",
      "filter": "all",
      "app_filter": null,
      "conditions": [
        {
          "type": "condition",
          "element": "NETWORK",
          "input": "",
          "selement": "MODE",
          "condition": "NOT EQUALS",
          "sinput": "host"
        }
      ],
      "applicability": [
        {
          "type": "applicability",
          "element": "CONTAINER",
          "input": "",
          "selement": "EXISTS",
          "condition": null,
          "sinput": null
        }
      ]
    },
    {
      "external_id": "6c0cc017cc0d2c3a914a693a4e7574c7",
      "name": "Container is reachable via a network (mode is not 'none')",
      "description": "Containers running with `--network=none` are completely network-isolated. That's the right setting for sandboxed batch jobs but a configuration error for anything that's supposed to serve traffic. Surface them so operators can confirm intent.",
      "rational": "Mostly an inventory / drift signal — distinguishes intentional sandboxes from misconfigured services. An auditor reviewing 'why does this container have no metrics' has an immediate answer.",
      "remediation": "If the container should serve traffic, re-deploy without `--network=none`. If it's an intentional sandbox, document it and consider excluding this finding for that specific container.",
      "severity": "Informational",
      "filter": "all",
      "app_filter": null,
      "conditions": [
        {
          "type": "condition",
          "element": "NETWORK",
          "input": "",
          "selement": "MODE",
          "condition": "NOT EQUALS",
          "sinput": "none"
        }
      ],
      "applicability": [
        {
          "type": "applicability",
          "element": "CONTAINER",
          "input": "",
          "selement": "EXISTS",
          "condition": null,
          "sinput": null
        }
      ]
    },
    {
      "external_id": "9356def648e89439748f211282c943ad",
      "name": "Container image name does not contain 'test' or 'dev'",
      "description": "Heuristic check that catches images obviously not intended for production — `myapp:test`, `something-dev`, `tester/image`, etc. Tightens the supply chain by flagging images that the build pipeline didn't promote through a release stage.",
      "rational": "Cheap drift signal. Won't catch determined misuse, but catches a surprising number of 'oops, deployed the wrong tag' incidents.",
      "remediation": "Re-deploy with a production-named image. If the image name legitimately contains 'test' or 'dev' (e.g. an image for testing infrastructure that runs in production), exclude this finding for the container.",
      "severity": "Low",
      "filter": "all",
      "app_filter": null,
      "conditions": [
        {
          "type": "condition",
          "element": "IMAGE",
          "input": "",
          "selement": "NAME",
          "condition": "NOT CONTAINS",
          "sinput": "test"
        },
        {
          "type": "condition",
          "element": "IMAGE",
          "input": "",
          "selement": "NAME",
          "condition": "NOT CONTAINS",
          "sinput": "dev"
        }
      ],
      "applicability": [
        {
          "type": "applicability",
          "element": "CONTAINER",
          "input": "",
          "selement": "EXISTS",
          "condition": null,
          "sinput": null
        }
      ]
    },
    {
      "external_id": "f7237d9718711889d1256191379dabf6",
      "name": "Container is not running with --privileged",
      "description": "Privileged containers bypass nearly every isolation property the kernel provides — they can mount host filesystems, load kernel modules, and access raw devices. The flag should be reserved for very narrow infrastructure use cases.",
      "rational": "CIS Docker Benchmark 5.4. A privileged container is functionally equivalent to a root shell on the host.",
      "remediation": "Re-deploy the container without `--privileged`. If specific capabilities are genuinely needed, add only those via `--cap-add` instead.",
      "severity": "High",
      "filter": "all",
      "app_filter": null,
      "conditions": [
        {
          "type": "condition",
          "element": "PRIVILEGED",
          "input": "",
          "selement": "NOT EXISTS",
          "condition": null,
          "sinput": null
        }
      ],
      "applicability": [
        {
          "type": "applicability",
          "element": "CONTAINER",
          "input": "",
          "selement": "EXISTS",
          "condition": null,
          "sinput": null
        }
      ]
    },
    {
      "external_id": "c964d02137ec20ed361efdabd072b320",
      "name": "Container is not running as root",
      "description": "Containers should run as a non-root user. The image's USER directive or the container's `--user` flag should specify a non-root account so a process compromise doesn't immediately yield root inside the container's namespace.",
      "rational": "CIS Docker Benchmark 4.1. Defence in depth — limits the blast radius of a process escape.",
      "remediation": "Set USER in the image's Dockerfile, or pass `--user 1000:1000` (or any non-root uid:gid) at run time.",
      "severity": "Medium",
      "filter": "all",
      "app_filter": null,
      "conditions": [
        {
          "type": "condition",
          "element": "RUN_USER",
          "input": "",
          "selement": "CONTENT",
          "condition": "NOT EQUALS",
          "sinput": "root"
        },
        {
          "type": "condition",
          "element": "RUN_USER",
          "input": "",
          "selement": "CONTENT",
          "condition": "NOT EQUALS",
          "sinput": "0"
        }
      ],
      "applicability": [
        {
          "type": "applicability",
          "element": "CONTAINER",
          "input": "",
          "selement": "EXISTS",
          "condition": null,
          "sinput": null
        }
      ]
    },
    {
      "external_id": "62d0e45147c3a76e206bb5cf13c14782",
      "name": "Container does not mount the Docker socket",
      "description": "Bind-mounting `/var/run/docker.sock` into a container effectively gives that container root on the host — it can spawn arbitrary new containers (including privileged ones with the host filesystem mounted) via the daemon API.",
      "rational": "CIS Docker Benchmark 5.5. The single most common container-escape misconfiguration.",
      "remediation": "Remove the `-v /var/run/docker.sock:/var/run/docker.sock` flag. If the container genuinely needs to manage other containers, expose docker over TCP+TLS to a restricted account instead.",
      "severity": "High",
      "filter": "all",
      "app_filter": null,
      "conditions": [
        {
          "type": "condition",
          "element": "MOUNT",
          "input": "/var/run/docker.sock",
          "selement": "NOT EXISTS",
          "condition": null,
          "sinput": null
        }
      ],
      "applicability": [
        {
          "type": "applicability",
          "element": "CONTAINER",
          "input": "",
          "selement": "EXISTS",
          "condition": null,
          "sinput": null
        }
      ]
    },
    {
      "external_id": "3ce7bf8efcc91f528c0757a744965537",
      "name": "Container does not expose the SSH port",
      "description": "Containers should not publish port 22. SSH-into-container is a deployment anti-pattern; if a shell into the container is needed, use `docker exec` (which authenticates via the daemon socket rather than over the network).",
      "rational": "Defence in depth — reduces the network attack surface and prevents drift toward treating containers as long-lived VMs.",
      "remediation": "Remove `-p 22:22` (or equivalent EXPOSE / PUBLISH) from the container's run-time arguments.",
      "severity": "Medium",
      "filter": "all",
      "app_filter": null,
      "conditions": [
        {
          "type": "condition",
          "element": "EXPOSED_PORT",
          "input": "22/tcp",
          "selement": "NOT EXISTS",
          "condition": null,
          "sinput": null
        }
      ],
      "applicability": [
        {
          "type": "applicability",
          "element": "CONTAINER",
          "input": "",
          "selement": "EXISTS",
          "condition": null,
          "sinput": null
        }
      ]
    },
    {
      "external_id": "9543c251bd92a2a031cdc8749b8d7847",
      "name": "Container has a read-only root filesystem",
      "description": "Containers that don't need to write to their root filesystem should be started with `--read-only`. Combined with explicit `tmpfs` mounts for the paths that DO need to be writable (typically `/tmp` and `/run`), this dramatically constrains what a process compromise can persist.",
      "rational": "CIS Docker Benchmark 5.12. Cheap defence in depth for stateless workloads.",
      "remediation": "Re-deploy with `--read-only` plus `--tmpfs /tmp` (and any other paths the application actually writes to).",
      "severity": "Low",
      "filter": "all",
      "app_filter": null,
      "conditions": [
        {
          "type": "condition",
          "element": "READ_ONLY_FS",
          "input": "",
          "selement": "EXISTS",
          "condition": null,
          "sinput": null
        }
      ],
      "applicability": [
        {
          "type": "applicability",
          "element": "CONTAINER",
          "input": "",
          "selement": "EXISTS",
          "condition": null,
          "sinput": null
        }
      ]
    },
    {
      "external_id": "2f71e38964cb5e6affa75ae691d3f221",
      "name": "Container defines a HEALTHCHECK",
      "description": "Containers without a HEALTHCHECK directive can't be load-balanced or restarted on liveness failure by orchestrators — Docker only knows whether the entrypoint process is alive, not whether the application inside it is actually serving requests.",
      "rational": "Observability hygiene. A test for HEALTHCHECK *presence* (not its content), so it catches the common 'we just forgot' case.",
      "remediation": "Add a HEALTHCHECK directive to the image's Dockerfile (`HEALTHCHECK --interval=30s CMD curl -f http://localhost/healthz || exit 1`) or pass `--health-cmd` at run time.",
      "severity": "Informational",
      "filter": "all",
      "app_filter": null,
      "conditions": [
        {
          "type": "condition",
          "element": "HEALTH_CHECK",
          "input": "",
          "selement": "EXISTS",
          "condition": null,
          "sinput": null
        }
      ],
      "applicability": [
        {
          "type": "applicability",
          "element": "CONTAINER",
          "input": "",
          "selement": "EXISTS",
          "condition": null,
          "sinput": null
        }
      ]
    }
  ]
}
