Container Security: Why It's Fundamental
Containers have revolutionized how we distribute software, but they have also introduced new attack surfaces. A Docker image contains not only the application code, but also the base operating system, system libraries, and runtime dependencies. Each component can contain vulnerabilities that are inherited by the application.
Container security covers the entire lifecycle: from image building (build-time), to distribution in the registry (distribution), to production execution (runtime). In this article, we will explore tools and practices to protect every phase.
What You'll Learn
- How container image scanning works with Trivy and Grype
- Building secure images with multi-stage builds and distroless
- Image signing and verification with Cosign
- Runtime protection with Falco and network policies
- Security context and Pod Security Standards in Kubernetes
- Best practices for secure Dockerfiles
Trivy: Complete Vulnerability Scanner
Trivy by Aqua Security is the most popular container scanner, capable of analyzing Docker images, filesystems, Git repositories, and IaC configurations. It's fast, accurate, and easy to integrate into CI/CD pipelines.
Docker Image Scanning
# Install Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
# Scan a Docker image
trivy image node:18-alpine
# Scan with severity filter
trivy image --severity CRITICAL,HIGH node:18-alpine
# Output in SARIF format for GitHub
trivy image --format sarif --output trivy-results.sarif myapp:latest
# Scan local filesystem
trivy fs --security-checks vuln,config .
# Scan with exit code for CI (fails if CRITICAL found)
trivy image --exit-code 1 --severity CRITICAL myapp:latest
Trivy Integration in GitHub Actions
# .github/workflows/container-security.yml
name: Container Security Scan
on:
push:
branches: [main]
pull_request:
jobs:
trivy-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
- name: Trivy vulnerability scan
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 SARIF to GitHub Security
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: "trivy-results.sarif"
Grype: Lightweight Alternative for Image Scanning
Grype by Anchore is a lightweight alternative to Trivy, focused exclusively on container image vulnerability scanning. It's particularly fast and integrates well with Syft (from the same team) for SBOM generation.
# Install Grype
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh
# Scan an image
grype myapp:latest
# Scan with filter and JSON output
grype myapp:latest --only-fixed --output json > grype-results.json
# Scan with fail-on severity
grype myapp:latest --fail-on critical
# Generate SBOM with Syft and analyze with Grype
syft myapp:latest -o cyclonedx-json > sbom.json
grype sbom:sbom.json
Building Secure Docker Images
Container security starts with the Dockerfile. A well-built image drastically reduces the attack surface and number of vulnerabilities.
Multi-Stage Build
The multi-stage build pattern separates the compilation environment from the final image, eliminating build tools, sources, and development dependencies from the production image:
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Stage 2: Production (minimal image)
FROM gcr.io/distroless/nodejs20-debian12
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
USER nonroot
CMD ["dist/server.js"]
Distroless Images
Google's distroless images contain only the application and its runtime dependencies, without shell, package manager, or other system tools. This reduces the attack surface to the absolute minimum.
Size and Vulnerability Comparison
| Base Image | Size | Typical CVEs |
|---|---|---|
| node:20 | ~1.1 GB | 200-400 |
| node:20-slim | ~250 MB | 50-100 |
| node:20-alpine | ~180 MB | 10-30 |
| distroless/nodejs20 | ~130 MB | 0-5 |
Dockerfile Security Best Practices
A secure Dockerfile follows specific rules that reduce the attack surface:
# 1. Use specific versions, never :latest
FROM node:20.11.0-alpine3.19
# 2. Create a non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# 3. Set WORKDIR
WORKDIR /app
# 4. Copy dependency files first (cache layer)
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
# 5. Copy application code
COPY --chown=appuser:appgroup . .
# 6. Don't expose privileged ports
EXPOSE 8080
# 7. Use non-root user
USER appuser
# 8. Use HEALTHCHECK
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
# 9. Use exec form for CMD
CMD ["node", "dist/server.js"]
Image Signing with Cosign
Cosign (part of the Sigstore project) allows digitally signing container images, ensuring the image hasn't been modified after the build. This is fundamental for supply chain security.
# Install Cosign
brew install cosign # macOS
# or
go install github.com/sigstore/cosign/v2/cmd/cosign@latest
# Generate a key pair
cosign generate-key-pair
# Sign an image
cosign sign --key cosign.key myregistry.com/myapp:v1.0.0
# Verify the signature
cosign verify --key cosign.pub myregistry.com/myapp:v1.0.0
# Sign with keyless (Sigstore/Fulcio)
cosign sign myregistry.com/myapp:v1.0.0
Runtime Protection with Falco
Falco by Sysdig is the reference open source tool for container runtime security. It monitors kernel syscalls in real-time and generates alerts when it detects anomalous or suspicious behavior.
What Falco Detects
- Shell spawn inside a container
- Access to sensitive files (/etc/shadow, /etc/passwd)
- Unexpected network connections
- System file modifications
- Privilege escalation
- Cryptomining activity
Kubernetes Pod Security
In Kubernetes environments, container security extends to Pod configuration. Pod Security Standards define three security levels:
# Pod with restrictive security context
apiVersion: v1
kind: Pod
metadata:
name: secure-app
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:v1.0.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources:
limits:
memory: "256Mi"
cpu: "500m"
requests:
memory: "128Mi"
cpu: "250m"
Network Policies
Network Policies in Kubernetes limit network traffic between Pods, implementing the principle of least privilege at the network level:
# Deny all ingress by default
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
---
# Allow only specific traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
Container Security Best Practices
- Scan on every build: integrate Trivy/Grype in the CI pipeline for every image
- Minimal images: use Alpine or distroless as base, avoid full images
- Non-root: always run containers as non-root user
- Read-only filesystem: mount root filesystem as read-only
- No latest tag: always use specific tags with SHA256 digest
- Private registry: use a private registry with integrated scanning
- Image signing: sign every image with Cosign before deploying
- Runtime monitoring: implement Falco for production monitoring
Conclusions
Container security is a critical aspect of DevSecOps that requires attention at every stage of the lifecycle. From building minimal and secure images, to automated scanning, to runtime monitoring, each layer adds protection against threats.
In the next article, we'll explore Supply Chain Security, analyzing SBOM (Software Bill of Materials), Sigstore for artifact signing, and the SLSA framework for ensuring software supply chain integrity.
Related deep dive: 04 - SCA - Software Composition Analysis e Dependency Vulnerabilities.
Related deep dive: 06 - Supply Chain Security - SBOM e Sigstore per Artifact Integrity.
Frequently asked questions
What is the primary concern with Docker images in terms of security?
Docker images contain not only application code, but also base operating systems, system libraries, and runtime dependencies, which can inherit vulnerabilities.
How does Trivy contribute to container security?
Trivy is a popular container scanner that analyzes Docker images, filesystems, Git repositories, and IaC configurations, providing fast and accurate vulnerability scanning.
What is the purpose of using a multi-stage build pattern in Dockerfile?
The multi-stage build pattern separates the compilation environment from the final image, eliminating unnecessary components like build tools, sources, and development dependencies from the production image.
How can container images be digitally signed to ensure their integrity?
Container images can be digitally signed using Cosign, which ensures that the image hasn't been modified after the build.
Commenti
Caricamento commenti...
Sign in to comment