Salta al contenuto principale

05 - Container Security - Image Scanning e Runtime Protection

Container image vulnerabilities, scanning (Trivy, Clair), image signing, registry security, runtime monitoring (Falco), network policies, secrets in containers.

Cosa ne pensi di questo articolo?

05 - Container Security - Image Scanning e Runtime Protection
DevSecOpsFoto:panumas nikhomkhaiPexels

In Breve

Container image vulnerabilities, scanning (Trivy, Clair), image signing, registry security, runtime monitoring (Falco), network policies, secrets in containers.

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

  1. Scan ad ogni build: integrare Trivy/Grype nella pipeline CI per ogni immagine
  2. Immagini minimali: usare Alpine o distroless come base, evitare immagini full
  3. Non-root: eseguire sempre i container come utente non-root
  4. Read-only filesystem: montare il filesystem root in sola lettura
  5. No latest tag: usare sempre tag specifici con digest SHA256
  6. Registry privato: usare un registry privato con scanning integrato
  7. Firma delle immagini: firmare ogni immagine con Cosign prima del deploy
  8. 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.

Fonti e approfondimenti

Prova Questi Strumenti

Strumenti gratuiti correlati a questo argomento.

Commenti

Caricamento commenti...

Accedi per commentare

Discuti questo articolo nella community

Unisciti al gruppo Telegram per discutere con altri sviluppatori, fare domande e condividere le tue esperienze.

Ti è piaciuto questo articolo?

Esplora altri contenuti sul blog o scopri i miei progetti