| name | security-docker |
| description | Review Docker and container security audit patterns. Use for auditing secrets in layers, port exposure, and non-root users. Use proactively when Dockerfile or docker-compose.yml is present.
Examples:
- user: "Audit this Dockerfile" → check for secrets in ENV/ARG and non-root USER
- user: "Review docker-compose ports" → find accidentally exposed databases
- user: "Check for secrets in image history" → audit layers and build artifacts
- user: "Optimize Docker security" → implement multi-stage builds and minimal base images
- user: "Audit container privileges" → check for privileged: true or docker.sock mounts |
Security audit patterns for Docker and container deployments covering secrets in images, port exposure, user privileges, and compose security.
Secrets in Images (Critical)
Secrets in Build Args/ENV
# ❌ CRITICAL: Secret in ENV (visible in image history)
ENV API_KEY=sk_live_abc123
ENV DATABASE_URL=postgres://user:password@host/db
# ❌ CRITICAL: Secret in ARG (visible in image history)
ARG AWS_SECRET_ACCESS_KEY
RUN aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
# ✓ Use runtime secrets
# Pass via docker run -e or docker-compose environment/env_file
# ✓ Docker secrets (Swarm) or orchestrator-specific secrets
# Use /run/secrets/* instead of ENV/ARG when available
Secrets Baked into Layers
# ❌ CRITICAL: Even if deleted, secret is in layer history
COPY .env /app/.env
RUN source /app/.env && do_something
RUN rm /app/.env # Still in previous layer!
# ❌ CRITICAL: Copying all files includes secrets
COPY . /app/ # Copies .env, .git, etc.
# ✓ Use .dockerignore
# In .dockerignore:
# .env*
# .git
# *.pem
# *.key
# ✓ Or explicit COPY
COPY package*.json /app/
COPY src/ /app/src/
Checking Image History
docker history --no-trunc <image>
docker inspect <image> | jq '.[0].Config.Env'
Port Exposure
docker-compose.yml
services:
db:
image: postgres
ports:
- "5432:5432"
redis:
image: redis
ports:
- "6379:6379"
services:
db:
image: postgres
expose:
- "5432"
db:
ports:
- "127.0.0.1:5432:5432"
Default Credentials
services:
db:
image: postgres
environment:
POSTGRES_PASSWORD: postgres
redis:
image: redis
services:
db:
image: postgres
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
Non-Root User
# ❌ Running as root (default)
FROM node:18
COPY . /app
CMD ["node", "server.js"] # Runs as root
# ✓ Create and use non-root user
FROM node:18
WORKDIR /app
COPY --chown=node:node . .
USER node
CMD ["node", "server.js"]
# ✓ Using numeric UID (more portable)
FROM node:18
RUN useradd -r -u 1001 appuser
WORKDIR /app
COPY --chown=1001:1001 . .
USER 1001
CMD ["node", "server.js"]
Multi-Stage Builds
# ❌ Build tools and secrets in final image
FROM node:18
COPY . .
RUN npm install
RUN npm run build
CMD ["node", "dist/server.js"]
# Final image has: source, node_modules (dev deps), build tools
# ✓ Multi-stage: only production artifacts
FROM node:18 AS builder
WORKDIR /app
COPY package*.json .
RUN npm ci
COPY . .
RUN npm run build
FROM node:18-slim AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER node
CMD ["node", "dist/server.js"]
# Final image: minimal, no source, no build tools
Docker Compose Security
Privileged Mode
services:
app:
privileged: true
services:
app:
cap_add:
- SYS_ADMIN
- NET_ADMIN
Volume Mounts
services:
app:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
services:
app:
volumes:
- /etc:/etc
- /root:/root
Network Mode
services:
app:
network_mode: host
Image Security
Base Image
# ❌ Outdated or unverified
FROM node:14 # EOL version
FROM random-user/node-app # Unverified
# ✓ Official, recent, minimal
FROM node:20-slim
FROM node:20-alpine
Image Scanning
docker scout cves <image>
trivy image <image>
grype <image>
Quick Audit Commands
rg "(ENV|ARG).*(KEY|SECRET|PASSWORD|TOKEN)" Dockerfile*
rg "ports:" docker-compose*.yml -A 3
rg "(privileged|cap_add|network_mode)" docker-compose*.yml
rg "docker.sock" docker-compose*.yml
grep "^USER" Dockerfile
cat .dockerignore | grep -E "(env|key|secret|pem)"
Hardening Checklist