# Container Security

> When to activate: container security, Docker hardening, image scanning, Trivy, Grype, Falco, runtime security, seccomp, non-root container

- Skill: `mattakushi432/container-security` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mattakushi432/container-security`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mattakushi432/container-security/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: Mattakushi432 (https://skillmd.com/u/mattakushi432)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mattakushi432/container-security

---

# Container Security Patterns

## Dockerfile Hardening

```dockerfile
# Use minimal base image
FROM python:3.12-slim AS base

# Run as non-root user
RUN groupadd -r appgroup && useradd -r -g appgroup appuser

WORKDIR /app

# Copy and install dependencies first (cache layer)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY --chown=appuser:appgroup . .

# Drop to non-root
USER appuser

# Read-only filesystem — mount writable volumes explicitly
# docker run --read-only --tmpfs /tmp myimage

# No new privileges
# docker run --security-opt=no-new-privileges myimage

EXPOSE 8080
ENTRYPOINT ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
```

```dockerfile
# Multi-stage build — no build tools in final image
FROM golang:1.22 AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o server ./cmd/server

FROM scratch  # Minimal — just the binary
COPY --from=builder /build/server /server
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
USER 65534:65534  # nobody
ENTRYPOINT ["/server"]
```

## Image Scanning

```bash
# Trivy — comprehensive scanner
brew install trivy

# Scan image
trivy image python:3.12-slim
trivy image --severity HIGH,CRITICAL myapp:latest

# Scan and fail CI on HIGH+
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest

# Scan filesystem/repo
trivy fs --scanners vuln,secret,misconfig .

# Generate SBOM
trivy image --format cyclonedx --output sbom.json myapp:latest

# Grype — alternative scanner
brew install grype
grype myapp:latest
grype myapp:latest --fail-on high
```

```yaml
# GitHub Actions image scan
- name: Build image
  run: docker build -t myapp:${{ github.sha }} .

- name: Scan with Trivy
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: myapp:${{ github.sha }}
    format: sarif
    output: trivy-results.sarif
    severity: HIGH,CRITICAL
    exit-code: 1

- name: Upload SARIF
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: trivy-results.sarif
```

## Runtime Security with Falco

```yaml
# falco-rules.yaml — custom rules
- rule: Shell spawned in container
  desc: A shell was spawned in a container (possible intrusion)
  condition: >
    spawned_process and container and
    shell_procs and not user_known_shell_spawn_activities
  output: >
    Shell spawned (user=%user.name container=%container.name
    image=%container.image.repository cmd=%proc.cmdline)
  priority: WARNING

- rule: Sensitive file read
  desc: Attempt to read /etc/shadow or /etc/passwd
  condition: >
    open_read and container and
    fd.name in (/etc/shadow, /etc/passwd, /etc/sudoers)
  output: "Sensitive file read (file=%fd.name container=%container.name)"
  priority: ERROR

- rule: Outbound connection to unexpected host
  desc: Container making outbound connection not in allowlist
  condition: >
    outbound and container and
    not fd.sip in (allowed_outbound_ips)
  output: "Unexpected outbound (dest=%fd.rip container=%container.name)"
  priority: WARNING
```

## Kubernetes Security Context

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
spec:
  template:
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        runAsGroup: 10001
        fsGroup: 10001
        seccompProfile:
          type: RuntimeDefault  # applies default seccomp

      containers:
      - name: app
        image: myapp:latest
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop: [ALL]
            add: []  # add only what's needed e.g. NET_BIND_SERVICE

        volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: cache
          mountPath: /app/cache

      volumes:
      - name: tmp
        emptyDir: {}
      - name: cache
        emptyDir: {}
```

## Security Checklist

```
Image Build:
  ✓ Minimal base image (slim/alpine/distroless/scratch)
  ✓ Multi-stage build — no compilers in final image
  ✓ No secrets in image layers (use secrets mount)
  ✓ Non-root USER defined
  ✓ .dockerignore excludes .git, .env, credentials

Runtime:
  ✓ --read-only filesystem (writable paths mounted explicitly)
  ✓ --no-new-privileges flag
  ✓ Drop ALL capabilities, add only needed
  ✓ Seccomp profile applied (RuntimeDefault minimum)
  ✓ AppArmor/SELinux profile in production

Registry:
  ✓ Private registry — no public unless intentional
  ✓ Image signing (Cosign/Notary)
  ✓ Admission controller validates signatures (Kyverno/OPA)
  ✓ Vulnerability scan on push to registry

Monitoring:
  ✓ Falco or equivalent for runtime anomaly detection
  ✓ Alert on shell spawn, sensitive file read, unexpected network
  ✓ Container logs shipped to SIEM
```

