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/dockerpartition - Host hardening
- Only trusted users in
dockergroup - 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:root644/etc/docker:root:root755/etc/docker/certs.d/*:root:root444/var/run/docker.sock:root:docker660/etc/docker/daemon.json:root:root644/etc/default/docker:root:root644/etc/sysconfig/docker:root:root644/run/containerd/containerd.sock:root:root660
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
COPYinstead ofADDunless there are specific needs forADD(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
- Specifically drop
- 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
docker0bridge (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: