Comprehensive container security guidance including vulnerability scanning with Trivy, image hardening, secrets management, and CIS benchmark compliance. Activates when working with "container security", "image scanning", "CVE", "vulnerability", "docker security", "hardening", or "CIS benchmark".
/plugin marketplace add Lobbi-Docs/claude/plugin install container-workflow@claude-orchestrationThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Implement defense-in-depth security practices for containerized applications. Master vulnerability scanning, image hardening, secrets management, runtime security, and compliance with CIS Docker Benchmark to build secure, production-ready containers.
Scan Images for Vulnerabilities:
Install and run Trivy to detect CVEs in container images:
# Install Trivy
brew install aquasecurity/trivy/trivy
# Scan image for vulnerabilities
trivy image myapp:latest
# Filter by severity
trivy image --severity HIGH,CRITICAL myapp:latest
# Output JSON for automation
trivy image --format json --output results.json myapp:latest
# Scan with exit code on findings
trivy image --exit-code 1 --severity CRITICAL myapp:latest
Scan Dockerfiles for Misconfigurations:
Detect security issues in Dockerfiles:
# Scan Dockerfile
trivy config Dockerfile
# Scan with specific policies
trivy config --policy ./policies Dockerfile
# Output in table format
trivy config --format table Dockerfile
Integrate Scanning into CI/CD:
Add Trivy scanning to GitHub Actions:
name: Container Security Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '1'
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v2
if: always()
with:
sarif_file: 'trivy-results.sarif'
Schedule Regular Scans:
Set up automated scanning for deployed images:
# Scan all images in registry
trivy image --severity HIGH,CRITICAL \
$(docker images --format "{{.Repository}}:{{.Tag}}")
# Scan specific registry
trivy image ghcr.io/org/app:latest
# Generate SBOM (Software Bill of Materials)
trivy image --format cyclonedx myapp:latest > sbom.json
Configure Scanning Policies:
Create custom policies with .trivyignore:
# .trivyignore
# Ignore specific CVEs (with justification)
CVE-2023-12345 # Fixed in runtime, not exploitable in our context
CVE-2023-67890 # Mitigation applied via network policies
# Ignore low severity in specific packages
CVE-2023-11111 package=curl
Run Containers as Unprivileged Users:
Never run containers as root:
FROM node:20-alpine
# Create non-root user
RUN addgroup -g 1001 -S appuser && \
adduser -S appuser -u 1001 -G appuser
# Set up application directory
WORKDIR /app
COPY --chown=appuser:appuser . .
# Install dependencies
RUN npm ci --only=production
# Switch to non-root user
USER appuser
EXPOSE 3000
CMD ["node", "server.js"]
Verify User in Runtime:
Check effective user in running container:
docker run --rm myapp:latest id
# Expected output: uid=1001(appuser) gid=1001(appuser)
Make Filesystem Immutable:
Run containers with read-only root:
FROM python:3.11-slim
RUN useradd -m -u 1001 appuser
WORKDIR /app
COPY --chown=appuser:appuser . .
RUN pip install --no-cache-dir -r requirements.txt
USER appuser
# Create writable temp directory
RUN mkdir -p /tmp/app && chown appuser:appuser /tmp/app
ENV TMPDIR=/tmp/app
CMD ["python", "app.py"]
Run with read-only filesystem:
docker run --read-only --tmpfs /tmp myapp:latest
Kubernetes configuration:
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-app
spec:
template:
spec:
containers:
- name: app
image: myapp:latest
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1001
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
Use Minimal Base Images:
Choose distroless or scratch images:
# Option 1: Distroless (no shell, no package manager)
FROM gcr.io/distroless/python3-debian12
COPY --chown=nonroot:nonroot app/ /app/
WORKDIR /app
USER nonroot
CMD ["main.py"]
# Option 2: Scratch (for static binaries)
FROM golang:1.21 AS builder
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o app
FROM scratch
COPY --from=builder /src/app /app
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
USER 65534:65534
ENTRYPOINT ["/app"]
# Option 3: Alpine (minimal with package manager)
FROM alpine:3.19
RUN apk add --no-cache ca-certificates && \
adduser -D -u 1001 appuser
COPY --chown=appuser:appuser app /app
USER appuser
CMD ["/app"]
Remove Unnecessary Packages:
Clean up build dependencies:
FROM ubuntu:22.04
# Install build dependencies, build, then remove in same layer
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
python3-dev && \
# Build application
pip3 install -r requirements.txt && \
# Remove build tools
apt-get purge -y --auto-remove build-essential python3-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
Use Environment Variables:
Pass secrets at runtime:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
USER node
# Don't set secret values in Dockerfile
ENV NODE_ENV=production
# ENV API_KEY=secret123 # NEVER DO THIS
CMD ["node", "server.js"]
Run with secrets:
# Bad: Visible in process list and history
docker run -e API_KEY=secret123 myapp:latest
# Better: Read from file
docker run --env-file .env.production myapp:latest
# Best: Use secrets management
docker run --secret id=api_key,src=./secrets/api_key myapp:latest
Implement Docker Secrets:
Use BuildKit secrets for build-time secrets:
# syntax=docker/dockerfile:1.4
FROM python:3.11-slim
WORKDIR /app
# Use secret during build without persisting it
RUN --mount=type=secret,id=pip_token \
PIP_TOKEN=$(cat /run/secrets/pip_token) && \
pip install --extra-index-url https://token:${PIP_TOKEN}@private-repo.com/simple/ \
-r requirements.txt
COPY . .
CMD ["python", "app.py"]
Build with secrets:
docker buildx build \
--secret id=pip_token,src=./secrets/pip_token \
-t myapp:latest .
Use Kubernetes Secrets:
Reference secrets in pod specs:
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
stringData:
database-url: postgresql://user:pass@db:5432/mydb
api-key: super-secret-key
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
template:
spec:
containers:
- name: app
image: myapp:latest
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
- name: API_KEY
valueFrom:
secretKeyRef:
name: app-secrets
key: api-key
Integrate with HashiCorp Vault:
Use Vault agent injector:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-inject-secret-config: "secret/data/myapp/config"
vault.hashicorp.com/role: "myapp"
spec:
serviceAccountName: myapp
containers:
- name: app
image: myapp:latest
Configure Pod Security Standards:
Implement restrictive security contexts:
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:latest
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1001
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
Drop All Capabilities by Default:
Only grant necessary capabilities:
# Dockerfile with minimal capabilities
FROM alpine:3.19
RUN adduser -D -u 1001 appuser
COPY app /app
USER appuser
CMD ["/app"]
Docker run with limited capabilities:
docker run \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--security-opt=no-new-privileges \
myapp:latest
Kubernetes configuration:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
template:
spec:
containers:
- name: app
image: myapp:latest
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
allowPrivilegeEscalation: false
Restrict Network Access:
Define network policies to limit traffic:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: app-network-policy
spec:
podSelector:
matchLabels:
app: myapp
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
- to:
- namespaceSelector: {}
ports:
- protocol: UDP
port: 53 # DNS
Implement Key Controls:
Apply critical CIS recommendations:
# Use official images from verified publishers
FROM node:20-alpine
# Verify image signatures
# docker trust inspect node:20-alpine
FROM debian:12-slim
# Install only required packages
RUN apt-get update && \
apt-get install -y --no-install-recommends \
python3 \
python3-pip && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Regular scanning
trivy image --severity HIGH,CRITICAL myapp:latest
# Good
COPY app.py /app/
# Avoid unless needed
ADD https://example.com/file.tar.gz /tmp/
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD curl -f http://localhost:8080/health || exit 1
docker run --read-only --tmpfs /tmp myapp:latest
docker run \
--memory="512m" \
--memory-swap="512m" \
--cpus="0.5" \
myapp:latest
Run Automated CIS Checks:
Use Docker Bench Security:
# Clone Docker Bench Security
git clone https://github.com/docker/docker-bench-security.git
cd docker-bench-security
# Run audit
sudo sh docker-bench-security.sh
# Run specific checks
sudo sh docker-bench-security.sh -c container_images
# Output to file
sudo sh docker-bench-security.sh -l /tmp/docker-bench.log
Address Common Findings:
Fix typical CIS violations:
# Before (non-compliant)
FROM node:latest
COPY . /app
WORKDIR /app
RUN npm install
CMD npm start
# After (CIS compliant)
FROM node:20.11.1-alpine3.19
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
WORKDIR /app
# Copy dependency manifests
COPY --chown=nodejs:nodejs package*.json ./
# Install dependencies
RUN npm ci --only=production && \
npm cache clean --force
# Copy application
COPY --chown=nodejs:nodejs . .
# Switch to non-root user
USER nodejs
# Health check
HEALTHCHECK --interval=30s --timeout=3s \
CMD node healthcheck.js
EXPOSE 3000
CMD ["node", "server.js"]
Triage Vulnerability Findings:
Address vulnerabilities systematically:
Update Base Images:
Keep base images current:
# Check for updates regularly
FROM node:20-alpine # Update from 20.10.0 to 20.11.1
# Pin specific version for reproducibility
FROM node:20.11.1-alpine3.19
# Rebuild images monthly to get security patches
Patch Application Dependencies:
Update vulnerable packages:
# Check for outdated packages
npm audit
# Fix vulnerabilities
npm audit fix
# Force fix (may introduce breaking changes)
npm audit fix --force
# Update specific package
npm update package-name
Layer Security Controls:
Apply multiple security measures:
Build Time:
Registry:
Runtime:
# Complete secure deployment example
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-app
spec:
replicas: 3
template:
metadata:
labels:
app: secure-app
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: ghcr.io/org/app:v1.2.3@sha256:abc123...
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources:
limits:
memory: "512Mi"
cpu: "500m"
requests:
memory: "256Mi"
cpu: "250m"
livenessProbe:
httpGet:
path: /health
port: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080
volumeMounts:
- name: tmp
mountPath: /tmp
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
volumes:
- name: tmp
emptyDir: {}
serviceAccountName: app-sa
automountServiceAccountToken: false
Create Software Bill of Materials:
Track dependencies for compliance:
# Generate SBOM with Trivy
trivy image --format cyclonedx --output sbom.json myapp:latest
# Generate SBOM with Syft
syft myapp:latest -o cyclonedx-json > sbom.json
# Attest SBOM to image
cosign attest --predicate sbom.json --type cyclonedx myapp:latest
Implement Image Signing:
Use Cosign for signing:
# Generate key pair
cosign generate-key-pair
# Sign image
cosign sign --key cosign.key myapp:latest
# Verify signature
cosign verify --key cosign.pub myapp:latest
# Sign with keyless (OIDC)
cosign sign myapp:latest
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.