Container Security: perchè e Fondamentale
I container hanno rivoluzionato il modo in cui distribuiamo il software, ma hanno anche introdotto nuove superfici di attacco. Un'immagine Docker contiene non solo il codice dell'applicazione, ma anche il sistema operativo base, le librerie di sistema e le dipendenze runtime. Ogni componente può contenere vulnerabilità che vengono ereditate dall'applicazione.
La container security copre l'intero ciclo di vita: dalla costruzione dell'immagine (build-time), alla distribuzione nel registry (distribution), fino all'esecuzione in produzione (runtime). In questo articolo esploreremo strumenti e pratiche per proteggere ogni fase.
Cosa Imparerai
- Come funziona il container image scanning con Trivy e Grype
- Costruire immagini sicure con multi-stage build e distroless
- Signing e verifica delle immagini con Cosign
- Runtime protection con Falco e network policies
- Security context e Pod Security Standards in Kubernetes
- Best practice per Dockerfile sicuri
Trivy: Scanner di Vulnerabilità Completo
Trivy di Aqua Security e lo scanner di container più popolare, capace di analizzare immagini Docker, filesystem, repository Git e configurazioni IaC. E veloce, preciso e facile da integrare nella pipeline CI/CD.
Scan di Immagini Docker
# Installare Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
# Scan di un'immagine Docker
trivy image node:18-alpine
# Scan con filtro severita
trivy image --severity CRITICAL,HIGH node:18-alpine
# Output in formato SARIF per GitHub
trivy image --format sarif --output trivy-results.sarif myapp:latest
# Scan del filesystem locale
trivy fs --security-checks vuln,config .
# Scan con exit code per CI (fallisce se trova CRITICAL)
trivy image --exit-code 1 --severity CRITICAL myapp:latest
Integrazione Trivy 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: Alternativa Leggera per Image Scanning
Grype di Anchore e un'alternativa leggera a Trivy, focalizzata esclusivamente sul vulnerability scanning delle immagini container. E particolarmente veloce e si integra bene con Syft (dello stesso team) per la generazione di SBOM.
# Installare Grype
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh
# Scan di un'immagine
grype myapp:latest
# Scan con filtro e output JSON
grype myapp:latest --only-fixed --output json > grype-results.json
# Scan con fail-on severity
grype myapp:latest --fail-on critical
# Generare SBOM con Syft e analizzare con Grype
syft myapp:latest -o cyclonedx-json > sbom.json
grype sbom:sbom.json
Costruire Immagini Docker Sicure
La sicurezza di un container inizia dal Dockerfile. Un'immagine ben costruita riduce drasticamente la superficie di attacco e il numero di vulnerabilità.
Multi-Stage Build
Il pattern multi-stage build separa l'ambiente di compilazione dall'immagine finale, eliminando tool di build, sorgenti e dipendenze di sviluppo dall'immagine di produzione:
# 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 (immagine minimale)
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"]
Immagini Distroless
Le immagini distroless di Google contengono solo l'applicazione e le sue dipendenze runtime, senza shell, package manager o altri tool di sistema. Questo riduce la superficie di attacco al minimo assoluto.
Confronto Dimensioni e Vulnerabilità
| Immagine Base | Dimensione | CVE Tipiche |
|---|---|---|
| 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 Practice
Un Dockerfile sicuro segue regole specifiche che riducono la superficie di attacco:
# 1. Usare versioni specifiche, mai :latest
FROM node:20.11.0-alpine3.19
# 2. Creare un utente non-root
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# 3. Impostare WORKDIR
WORKDIR /app
# 4. Copiare prima i file di dipendenze (cache layer)
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
# 5. Copiare il codice applicativo
COPY --chown=appuser:appgroup . .
# 6. Non esporre porte privilegiate
EXPOSE 8080
# 7. Usare l'utente non-root
USER appuser
# 8. Usare HEALTHCHECK
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
# 9. Usare exec form per CMD
CMD ["node", "dist/server.js"]
Image Signing con Cosign
Cosign (parte del progetto Sigstore) permette di firmare digitalmente le immagini container, garantendo che l'immagine non sia stata modificata dopo la build. Questo e fondamentale per la supply chain security.
# Installare Cosign
brew install cosign # macOS
# oppure
go install github.com/sigstore/cosign/v2/cmd/cosign@latest
# Generare una coppia di chiavi
cosign generate-key-pair
# Firmare un'immagine
cosign sign --key cosign.key myregistry.com/myapp:v1.0.0
# Verificare la firma
cosign verify --key cosign.pub myregistry.com/myapp:v1.0.0
# Firmare con keyless (Sigstore/Fulcio)
cosign sign myregistry.com/myapp:v1.0.0
Runtime Protection con Falco
Falco di Sysdig e lo strumento open source di riferimento per il runtime security dei container. Monitora le syscall del kernel in tempo reale e genera alert quando rileva comportamenti anomali o sospetti.
Cosa Rileva Falco
- Shell spawn all'interno di un container
- Accesso a file sensibili (/etc/shadow, /etc/passwd)
- Connessioni di rete inattese
- Modifica di file di sistema
- Privilege escalation
- Cryptomining activity
Kubernetes Pod Security
In ambienti Kubernetes, la sicurezza dei container si estende alla configurazione dei Pod. I Pod Security Standards definiscono tre livelli di sicurezza:
# Pod con security context restrittivo
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
Le Network Policies in Kubernetes limitano il traffico di rete tra i Pod, implementando il principio del least privilege a livello di rete:
# 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
Best Practice per Container Security
- Scan ad ogni build: integrare Trivy/Grype nella pipeline CI per ogni immagine
- Immagini minimali: usare Alpine o distroless come base, evitare immagini full
- Non-root: eseguire sempre i container come utente non-root
- Read-only filesystem: montare il filesystem root in sola lettura
- No latest tag: usare sempre tag specifici con digest SHA256
- Registry privato: usare un registry privato con scanning integrato
- Firma delle immagini: firmare ogni immagine con Cosign prima del deploy
- Runtime monitoring: implementare Falco per il monitoraggio in produzione
Conclusioni
La container security e un aspetto critico di DevSecOps che richiede attenzione in ogni fase del ciclo di vita. Dalla costruzione di immagini minimali e sicure, allo scanning automatizzato, fino al runtime monitoring, ogni livello aggiunge protezione contro le minacce.
Nel prossimo articolo esploreremo Supply Chain Security, analizzando SBOM (Software Bill of Materials), Sigstore per la firma degli artifact e il framework SLSA per garantire l'integrita della supply chain del software.
Domande frequenti
Quali sono le nuove superfici di attacco introdotte dai container?
I container hanno introdotto nuove superfici di attacco, poiché un'immagine Docker contiene non solo il codice dell'applicazione, ma anche il sistema operativo base, le librerie di sistema e le dipendenze runtime.
Qual è l'importanza della container security nel ciclo di vita dei container?
La container security copre l'intero ciclo di vita, dalla costruzione dell'immagine (build-time), alla distribuzione nel registry (distribution), fino all'esecuzione in produzione (runtime).
Qual è la differenza tra Trivy e Grype?
Trivy è uno scanner di container più popolare che può analizzare immagini Docker, filesystem, repository Git e configurazioni IaC, mentre Grype è un'alternativa leggera focalizzata esclusivamente sul vulnerability scanning delle immagini container.
Come si riduce la superficie di attacco con le immagini distroless?
Le immagini distroless contengono solo l'applicazione e le sue dipendenze runtime, senza shell, package manager o altri tool di sistema, riducendo così la superficie di attacco al minimo assoluto.

Commenti
Caricamento commenti...
Accedi per commentare