Notes on CIS hardening of Docker

Skipping stuff that should be secure by default, stuff that seems to be trumped by availability concerns, operational policy-based controls, and probably some other things…

Host configuration

  • Separate /var/lib/docker partition
  • Host hardening
  • Only trusted users in docker group
  • Auditing:
    • /usr/bin/docker
    • /var/lib/docker
    • /etc/docker
    • /usr/lib/systemd/system/docker.service
    • /usr/lib/systemd/system/docker.socket
    • /etc/default/docker
    • /etc/docker/daemon.json
    • /usr/bin/docker-containerd
    • /usr/bin/containerd-shim-runc-v1
    • /usr/bin/containerd-shim-runc-v2
    • /usr/bin/docker-runc

Daemon configuration (daemon.json)

  • Run daemon rootless if possible (not supported with swarm)
  • Disable inter-container networking ("icc": false)
  • Set default ulimit ("default-ulimits": {"nofile": {"Soft": 100, "Hard": 200}, "nproc": {"Soft": 1024, "Hard": 2048}})
  • [L2] Enable user namespace support ("userns-remap": "default")
  • [L2] User authorization plugin (ex: authz)
  • [L2] Configure centralized+remote logging ("log-driver": "syslog", "log-opts": {"syslog-address": "tcp://192.0.2.1"})
  • Restrict containers from acquiring new privs ("no-new-privileges": true)
  • Enable live restore ("live-restore": true) (not supported with swarm)
  • Disable userland proxy ("userland-proxy": false)

Docker daemon config files (ownership/privs)

  • docker.service/docker.socket: root:root 644
  • /etc/docker: root:root 755
  • /etc/docker/certs.d/*: root:root 444
  • /var/run/docker.sock: root:docker 660
  • /etc/docker/daemon.json: root:root 644
  • /etc/default/docker: root:root 644
  • /etc/sysconfig/docker: root:root 644
  • /run/containerd/containerd.sock: root:root 660

Container images and build file configs

  • Create a user for the container (USER)
  • Do not install unnecessary packages in the container
  • Scan+rebuild images for security patches
  • Don’t use update instructions alone in Dockerfiles
    • “Use update instructions together with install instructions and version pinning for packages while installing them. This will prevent caching and force the extraction of the required versions.”
  • Use COPY instead of ADD unless there are specific needs for ADD (extracting an archive / retrieving from a URL) (ADD or COPY)
  • Don’t store any kind of secrets in Dockerfiles
  • [L2] Verify authenticity of packages before installing them into images

Container runtime configuration

  • Enable AppArmor/SELinux as appropriate
  • Restrict Linux kernel capabilities within containers (cap_add, cap_drop)
    • Specifically drop NEW_RAW (unless specifically required) as that could allow a Bad Guy in a container to create spoofed network traffic ex:
      cap_drop:
      - ALL
      cap_add:
      - CHOWN
  • Don’t map containers to privileged ports (below 1024) unless absolutely required (like for load balancers/proxies) (ports)
  • Set memory limits / CPU priority for containers (mem_limit, cpu_shares)
    mem_limit: 512m
    cpu_shares: 512 # 1024 = 100%, 512 = 50%
  • Mount container’s root filesystem read-only (read-only, tmpfs)
    read_only: true
    tmpfs:
    - /run
    - /tmp
    volumes:
    - /opt/app/data:/run/app/data:rw # bind mount for persistence
  • Bind to a specific host interface (ports)
    ports:
    - 192.0.2.1:8080:80 # short form
    - name: web-secured # long form
      target:443
      published: 8443
      host_ip: 192.0.2.1
  • Don’t share host devices to containers; if required, limit privs (devices)
    devices:
    - "/dev/temp_sda:/dev/temp_sda:r"
  • Set PIDs cgroup limit (pids_limit)
    pids_limit: 10
  • [L2] Don’t use Docker’s default docker0 bridge (use user-defined networks, default for compose)

Docker swarm configuration

  • Minimum number of manager nodes (one, or smallest odd number if management fault tolerance is required) (manager nodes)
  • Bind swarm services to specific host interfaces, separating management and data plane traffic (swarm init)
    docker swarm init \
      --listen-addr 192.0.2.1 \ # inbound management
      --advertise-addr 192.0.2.1 \ # outbound management
      --data-path-addr 172.16.0.1 # data plane
  • Encrypt overlay network traffic (encrypted)
  • Use swarm secrets (secrets)

See also: