<!-- Copyright (c) 2026 defconxt. All rights reserved. -->
<!-- Licensed under AGPL-3.0 — see LICENSE file for details. -->

# Docker & Container Security Reference

## Secure Docker Compose Pattern

```yaml
services:
  app:
    image: myapp:${VERSION}
    user: "1000:1000"            # non-root
    read_only: true
    tmpfs: [/tmp, /run]
    cap_drop: [ALL]
    cap_add: [NET_BIND_SERVICE]  # only add what's needed
    security_opt: [no-new-privileges:true]
    environment:
      - SECRET_KEY_FILE=/run/secrets/app_key
    secrets: [app_key]
    networks: [internal]
secrets:
  app_key:
    file: ./secrets/app_key.txt
networks:
  internal:
    driver: bridge
    internal: true  # no external access
```

## Pi-hole DNS Sinkhole (Docker)

```yaml
services:
  pihole:
    image: pihole/pihole:latest
    ports: ["53:53/tcp", "53:53/udp", "80:80/tcp"]
    environment:
      TZ: 'UTC'
      FTLCONF_webserver_api_password: '{{ vault_pihole_password }}'
      FTLCONF_dns_upstreams: '1.1.1.1#853;1.0.0.1#853'  # DNS-over-TLS
      FTLCONF_dns_dnssec: 'true'
    volumes: ['pihole_data:/etc/pihole']
    cap_add: [NET_ADMIN]
    restart: unless-stopped
```

## Ansible Docker Deployment Role

```yaml
- name: Install Docker packages
  ansible.builtin.package:
    name: "{{ docker_packages }}"
    state: present
  notify: restart docker

- name: Configure Docker daemon security
  ansible.builtin.copy:
    dest: /etc/docker/daemon.json
    content: |
      {
        "live-restore": true,
        "userland-proxy": false,
        "no-new-privileges": true,
        "log-driver": "json-file",
        "log-opts": { "max-size": "10m", "max-file": "3" }
      }
    mode: "0644"
  notify: restart docker
```

## Trivy Image Scan

```bash
trivy image --severity HIGH,CRITICAL --exit-code 1 myapp:latest
trivy fs --security-checks vuln,secret --format json . | jq '.Results[].Vulnerabilities[]'
```

## Anti-Patterns

- `cap_add: [SYS_ADMIN]` in production — equivalent to root. Use specific capabilities.
- Mounting the Docker socket into a container — container escape to host root.
