Related Fundamentals: CIS controls enforce the security best practices described in Container Security Best Practices. Understanding the scope distinction in Container Scope vs Kernel Scope helps clarify which CIS controls apply at each layer.
Implementing CIS Docker Benchmark Controls
The Center for Internet Security (CIS) Docker Benchmark specifies 94 controls for securing Docker containers. CleanStart images pass CIS compliance automatically through hardened base images and automated checks.
CIS Controls Overview
Control Groups
Group | Count | Focus | CleanStart Support |
|---|---|---|---|
Host Configuration | 5 | Docker daemon, kernel | Pre-configured |
Docker Daemon | 13 | Configuration, logging | Pre-configured |
Docker Images | 10 | Build, registry, base | Automated checks |
Containers | 42 | Runtime, capabilities | Policy enforcement |
Kubernetes | 24 | Orchestration | Policy enforcement |
CleanStart handles host/daemon/image controls automatically; containers enforce via policies.
Critical CIS Controls
Image Security (CIS 4.x)
# CIS 4.1: Ensure a user is created for running containers# CleanStart ensures all images have non-root user FROM ubuntu:24.04RUN useradd -m -u 1000 appuserUSER appuser # Run as non-rootContainer Runtime (CIS 5.x)
# CIS 5.1: Verify CPU limits are setspec: containers: - name: myapp resources: limits: cpu: "1" memory: "512Mi" requests: cpu: "100m" memory: "128Mi" # CIS 5.2: Set memory limits# (shown above) # CIS 5.3: Set network traffic rulesnetworkPolicy: podSelector: matchLabels: app: myapp policyTypes: - Ingress - Egress ingress: - from: - namespaceSelector: matchLabels: name: production ports: - protocol: TCP port: 8080 egress: - to: - namespaceSelector: matchLabels: name: production ports: - protocol: TCP port: 443Container Capabilities (CIS 5.28)
# CIS 5.28: Disable unnecessary kernel capabilitiesspec: securityContext: capabilities: drop: - ALL # Drop all capabilities add: - NET_BIND_SERVICE # Only add what's needed readOnlyRootFilesystem: true # CIS 5.4 runAsNonRoot: true # CIS 5.1 runAsUser: 1000 # CIS 5.2CleanStart CIS Automation
Automated CIS Checks
# Check image for CIS compliancecleanimg-init --cis-check --image myapp:1.0.0 # Output:# CIS 4.1: Non-root user configured# CIS 4.2: Container base image scanned# CIS 4.3: Image signed (missing signature)# CIS 4.4: Metadata labels present# CIS 4.5: Health check defined# Summary: 19/20 controls passed (95%)# Recommendation: Add image signatureDeploy with CIS Enforcement
# Kubernetes admission controller enforces CISapiVersion: constraints.gatekeeper.sh/v1beta1kind: K8sCISDockerBenchmarkmetadata: name: cis-enforcementspec: match: kinds: - apiGroups: [""] kinds: ["Pod"] parameters: severity: high enforceAll: true exemptNamespaces: ["kube-system"]CIS Host and Daemon Controls
Docker Daemon Security (Pre-Configured)
CleanStart assumes these are configured on the host:
# CIS 2.1: Run daemon as non-rootps aux | grep dockerd# root 1234 0.0 0.5 1234567 56789 ? Ss 14:00 0:00 /usr/bin/dockerd # CIS 2.2: Restrict Docker socket permissionsls -l /var/run/docker.sock# srw-rw---- 1 root docker 0 Oct 4 14:00 /var/run/docker.sock # CIS 2.3: Enable user namespace remappingcat /etc/docker/daemon.json | jq '.userns_mode'# "host" (or use remapping for security) # CIS 2.4: Enable Apparmordocker inspect myapp | jq '.[0].AppArmorProfile'# "docker-default" # CIS 2.5: Enable SELinuxgetenforce# Enforcing (if available)Image Hardening Specifics
Minimal Base Image
# CIS 4.2: Use minimal base imagesFROM alpine:3.19 # ~5MB, minimal attack surface# Instead of: FROM ubuntu:24.04 # ~70MB, more surface # CIS 4.3: Scan base image for vulnerabilities# (CleanStart does this automatically) # CIS 4.4: Add security labelsLABEL security.cis="4.2"LABEL maintainer="security@company.com"LABEL description="Minimal, hardened app image" # CIS 4.5: Add health checkHEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD /usr/local/bin/healthcheck.sh || exit 1Non-Root User Enforcement
# CIS 4.1: Create non-root userRUN useradd -m -u 1000 appuserUSER appuser # All subsequent commands run as appuser (UID 1000)# NOT root (UID 0) # Verify in DockerfileRUN whoami # Output: appuserRead-Only Root Filesystem
# CIS 5.4: Configure read-only root filesystem# Requires application to write only to /tmp or /var/tmp FROM alpine:3.19 # Create writable directoriesRUN mkdir -p /tmp /var/tmpRUN chmod 1777 /tmp /var/tmp # Application runs with read-only root# Pod spec enforces: readOnlyRootFilesystem: trueRuntime CIS Enforcement
Pod Security Standards (Kubernetes)
# Kubernetes Pod Security Standards (replaces deprecated PSP)apiVersion: policy.k8s.io/v1alpha1kind: PodSecurityPolicymetadata: name: cis-restrictedspec: privileged: false # CIS 5.25 allowPrivilegeEscalation: false # CIS 5.7 requiredDropCapabilities: - ALL # CIS 5.28 volumes: - 'configMap' - 'emptyDir' - 'secret' hostNetwork: false # CIS 5.21 hostIPC: false # CIS 5.22 hostPID: false # CIS 5.23 runAsUser: rule: 'MustRunAsNonRoot' # CIS 5.1 seLinux: rule: 'MustRunAs' # CIS 5.24 fsGroup: rule: 'RunAsAny' readOnlyRootFilesystem: true # CIS 5.4Network Policies (CIS 5.3)
# Restrict network traffic (Kubernetes)apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: myapp-network-policyspec: podSelector: matchLabels: app: myapp policyTypes: - Ingress - Egress ingress: - from: - podSelector: matchLabels: app: frontend ports: - protocol: TCP port: 8080 egress: - to: - podSelector: matchLabels: app: database ports: - protocol: TCP port: 5432 - to: # Allow DNS - namespaceSelector: {} ports: - protocol: UDP port: 53CIS Compliance Mapping
CIS Control | CleanStart Implementation |
|---|---|
4.1 Non-root user | Automated in Dockerfile |
4.2 Minimal base | Alpine/distroless images |
4.3 Vulnerability scanning | Automated with Grype |
4.4 Metadata labels | Automated injection |
5.1 CPU limits | Kubernetes policy |
5.4 Read-only filesystem | Pod config template |
5.7 Privilege escalation | Security context policy |
5.28 Capabilities | Drop ALL in pod spec |
Testing CIS Compliance
Manual CIS Audit
# Download CIS Docker Benchmark reference# Run manual checks # Check 4.1: Non-root userdocker run myapp:1.0.0 whoami# Output: appuser (not root) # Check 4.2: Minimal basedocker images myapp:1.0.0 --format "{{.Size}}"# Should be < 50MB # Check 4.5: Health checkdocker inspect myapp:1.0.0 | jq '.Config.Healthcheck'# Should show health check configurationAutomated Audit Tools
# Use CIS-CAT or Docker Bench Security # Docker Bench Security (free)docker run --rm --net host --pid host \ --userns host --cap-add audit_control \ -v /var/lib:/var/lib \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /etc:/etc:ro \ docker/docker-bench-security # Output shows CIS controls and pass/failCommon CIS Failures and Fixes
Failure: Running as Root
# BADFROM ubuntu:24.04COPY app /appENTRYPOINT ["/app/myapp"]# Runs as root by default # GOODFROM ubuntu:24.04RUN useradd -m -u 1000 appuserCOPY --chown=appuser:appuser app /appUSER appuser # Must be before ENTRYPOINTENTRYPOINT ["/app/myapp"]Failure: Unnecessary Capabilities
# BADspec: securityContext: capabilities: add: - ALL # Adds all capabilities # GOODspec: securityContext: capabilities: drop: - ALL # Drop all first add: - NET_BIND_SERVICE # Add only what's neededFailure: No Resource Limits
# BADspec: containers: - name: myapp image: myapp:1.0.0 # No limits - can consume all resources # GOODspec: containers: - name: myapp image: myapp:1.0.0 resources: limits: cpu: "2" memory: "1Gi" requests: cpu: "100m" memory: "128Mi"CIS Benchmark Compliance Report
# Generate CIS compliance reportcleanimg-init --cis-report --image myapp:1.0.0 --format html --output cis-report.html # Report shows:# - Each control (4.1, 4.2, etc.)# - Pass/fail status# - Remediation steps# - Risk rating# - Compliance percentage (95/100 = 95%)See Also
STIG Hardening: stig-hardening.md — DISA security hardening. OpenSCAP: openscap-reference.md — Automated compliance scanning. FedRAMP: ../regulatory/fedramp-high.md — Government compliance.
