Salta al contenuto principale

06 - Sicurezza IaC: Checkov, Trivy e OPA Policy-as-Code

Terraform IaC 14 min Sicurezza IaC: Checkov, Trivy e OPA Policy-as-Code Integra il security scanning nel ciclo Terraform: Checkov per controlli statici su...

Cosa ne pensi di questo articolo?

06 - Sicurezza IaC: Checkov, Trivy e OPA Policy-as-Code
Terraform IaCFoto:Daniil KomovPexels

In Breve

Terraform IaC 14 min Sicurezza IaC: Checkov, Trivy e OPA Policy-as-Code Integra il security scanning nel ciclo Terraform: Checkov per controlli statici su...

Terraform IaC 14 min

Sicurezza IaC: Checkov, Trivy e OPA Policy-as-Code

Integra il security scanning nel ciclo Terraform: Checkov per controlli statici su 1000+ policy, Trivy come successore di tfsec, e OPA/conftest per policy custom organizzative nel gate pre-apply.

Checkov Trivy OPA Policy as Code IaC Security Terraform Security

Perché la Sicurezza IaC È Critica

Nel 2023, Palo Alto Networks ha documentato che il 65% delle violazioni cloud ha origine da misconfigurazioni IaC: bucket S3 pubblici, security group troppo permissivi, logging disabilitato, encryption non configurata. La caratteristica devastante di questi errori è che si scalano: un modulo Terraform insicuro usato in 50 ambienti crea 50 vulnerabilità simultaneamente.

La soluzione è lo Shift Left Security: spostare i controlli di sicurezza il più presto possibile nel workflow, prima che le misconfigurazioni raggiungano il cloud. In questo articolo integriamo tre strumenti complementari: Checkov per la copertura ampia, Trivy per la ricerca di vulnerabilità, e OPA per policy organizzative custom.

I Tre Strumenti e i Loro Ruoli

  • Checkov: 1000+ policy built-in per AWS/Azure/GCP/K8s. Specializzato in IaC. Mantiene da Bridgecrew (acquisita da Palo Alto).
  • Trivy: Successore di tfsec (Aqua Security). Scanner unificato: IaC, container, dipendenze, segreti. Migrazione tfsec → Trivy completata nel 2024.
  • OPA + conftest: Policy Engine generico. Scrivi policy in Rego per regole organizzative custom (naming convention, tag obbligatori, regioni permesse).

Checkov: Security Scanning con 1000+ Policy

Checkov analizza i file Terraform (.tf) staticamente, senza eseguire init o plan. Copre vulnerabilità comuni: storage non cifrato, accesso pubblico non intenzionale, logging disabilitato, IAM troppo permissivo.

# Installazione
pip install checkov

# Oppure con Docker (raccomandato per CI)
docker pull bridgecrew/checkov:latest

# Scansione base di una directory Terraform
checkov -d ./environments/production --framework terraform

# Solo errori HIGH e CRITICAL (ignora LOW e MEDIUM)
checkov -d . --check HIGH,CRITICAL

# Output SARIF per integrare con GitHub Code Scanning
checkov -d . --output sarif --output-file checkov.sarif

# Skip di check specifici (con motivazione documentata)
checkov -d . --skip-check CKV_AWS_20,CKV_AWS_57
# CKV_AWS_20: bucket S3 public read - skippiamo solo se il bucket è intenzionalmente pubblico
# Esempio: codice Terraform con vulnerabilità e relativo fix

# VULNERABILE - Checkov alert: CKV_AWS_18, CKV_AWS_52, CKV_AWS_144
resource "aws_s3_bucket" "logs" {
  bucket = "my-app-logs"
  # MANCA: server_side_encryption_configuration
  # MANCA: versioning
  # MANCA: replication_configuration per disaster recovery
}

# SICURO - Tutti i check Checkov superati
resource "aws_s3_bucket" "logs" {
  bucket = "my-app-logs"
}

resource "aws_s3_bucket_server_side_encryption_configuration" "logs" {
  bucket = aws_s3_bucket.logs.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "aws:kms"  # CKV_AWS_19: usa KMS, non AES256
    }
    bucket_key_enabled = true
  }
}

resource "aws_s3_bucket_versioning" "logs" {
  bucket = aws_s3_bucket.logs.id
  versioning_configuration {
    status = "Enabled"  # CKV_AWS_21
  }
}

resource "aws_s3_bucket_public_access_block" "logs" {
  bucket = aws_s3_bucket.logs.id

  block_public_acls       = true  # CKV_AWS_53
  block_public_policy     = true  # CKV_AWS_54
  ignore_public_acls      = true  # CKV_AWS_55
  restrict_public_buckets = true  # CKV_AWS_56
}

resource "aws_s3_bucket_logging" "logs" {
  bucket = aws_s3_bucket.logs.id

  target_bucket = aws_s3_bucket.access_logs.id  # CKV_AWS_18
  target_prefix = "s3-access-logs/"
}
# checkov.yaml - Configurazione progetto (in root del repo)
# Skippa check non applicabili con motivazione documentata
skip-check:
  - CKV_AWS_144  # S3 cross-region replication: non richiesto per log bucket interni

# Controlla solo risorse dei moduli custom (escludi moduli registry)
directory:
  - modules/

# Severity minima da riportare
soft-fail-on: LOW,MEDIUM  # Non fallisce la pipeline per LOW/MEDIUM

# Abilita output colorato
compact: false

Trivy: Scanner IaC Unificato

Trivy è il successore di tfsec (la migrazione è completata nel 2024). Il vantaggio rispetto a Checkov è che Trivy scansiona anche container images, dipendenze applicative e segreti nella stessa pipeline, riducendo il numero di strumenti.

# Installazione
brew install trivy          # macOS
# oppure:
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh

# Scansione Terraform
trivy config ./environments/production

# Con severity filter
trivy config --severity HIGH,CRITICAL ./

# Output JSON per parsing
trivy config --format json --output trivy-results.json ./

# Template Terraform specifico con maggiore dettaglio
trivy config --tf-vars ./environments/production/terraform.tfvars ./environments/production

# Scansione tutto il repo: IaC + dipendenze + segreti
trivy fs ./
# Esempio output Trivy (semplificato)
# trivy config ./environments/production

2024-01-15T10:30:00Z INFO Misconfiguration Types: terraform
environments/production/main.tf (terraform)

HIGH: Resource 'aws_security_group.app' has a wide open egress rule
------------------------------------------
Via: environments/production/main.tf:45

    egress {
      from_port   = 0
      to_port     = 0
      protocol    = "-1"
      cidr_blocks = ["0.0.0.0/0"]  # VULN: egress completamente aperto
    }

Fix: Limita l'egress solo alle porte e destinazioni necessarie.

MEDIUM: Resource 'aws_db_instance.main' has no deletion protection
Via: environments/production/main.tf:78
    deletion_protection = false  # VULN: in production deve essere true
# .trivyignore - Ignora check specifici con motivazione
# Formato: CHECK_ID # Commento obbligatorio con motivazione

AVD-AWS-0025 # S3: CORS permissivo necessario per CDN pubblica
AVD-AWS-0168 # CloudFront: TLS 1.0 permesso per client legacy pre-2022

OPA e conftest: Policy Custom con Rego

Open Policy Agent (OPA) è un policy engine generico usato da Kubernetes, Istio e Terraform. conftest è il CLI che applica policy OPA a file di configurazione (inclusi Terraform plan JSON). Con OPA puoi implementare policy organizzative che non esistono in Checkov o Trivy: naming convention, tag obbligatori, regioni permesse, limiti di costo.

# Installazione conftest
brew install conftest

# oppure:
curl -L https://github.com/open-policy-agent/conftest/releases/download/v0.49.0/conftest_0.49.0_linux_amd64.tar.gz | tar xz
# policy/terraform/naming.rego
# Policy: tutte le risorse AWS devono seguire la naming convention aziendale

package main

import future.keywords.in

# Regex per naming convention: {team}-{environment}-{name}
naming_pattern := `^[a-z]+-(?:dev|staging|production)-[a-z0-9-]+$`

# Risorse che richiedono naming convention
resources_requiring_naming := {
  "aws_s3_bucket",
  "aws_rds_instance",
  "aws_elasticache_cluster",
  "aws_eks_cluster",
}

# Deny se il nome non segue la convention
deny[msg] {
  resource := input.resource[resource_type][resource_name]
  resource_type in resources_requiring_naming

  name := resource.config.bucket[0].constant_value  # per S3
  not regex.match(naming_pattern, name)

  msg := sprintf(
    "POLICY VIOLATION: %s '%s' ha nome '%s' che non rispetta la naming convention '%s'",
    [resource_type, resource_name, name, naming_pattern]
  )
}
# policy/terraform/tags.rego
# Policy: tag obbligatori su tutte le risorse AWS

package main

# Tag obbligatori per compliance e cost allocation
required_tags := {
  "Team",
  "Environment",
  "CostCenter",
  "ManagedBy",
}

# Risorse che devono avere tag
taggable_resources := {
  "aws_instance",
  "aws_s3_bucket",
  "aws_rds_instance",
  "aws_eks_cluster",
  "aws_lambda_function",
}

deny[msg] {
  resource := input.resource[resource_type][resource_name]
  resource_type in taggable_resources

  # Ottieni i tag dalla configurazione Terraform (formato piano JSON)
  tags := resource.config.tags[0].constant_value

  # Verifica che ogni tag obbligatorio sia presente
  required_tag := required_tags[_]
  not tags[required_tag]

  msg := sprintf(
    "POLICY VIOLATION: %s '%s' manca il tag obbligatorio '%s'",
    [resource_type, resource_name, required_tag]
  )
}

# Policy: il tag 'Environment' deve avere un valore valido
deny[msg] {
  resource := input.resource[resource_type][resource_name]
  resource_type in taggable_resources

  tags := resource.config.tags[0].constant_value
  env := tags.Environment
  not env in {"dev", "staging", "production"}

  msg := sprintf(
    "POLICY VIOLATION: %s '%s' tag Environment='%s' non valido. Deve essere dev|staging|production",
    [resource_type, resource_name, env]
  )
}
# policy/terraform/regions.rego
# Policy: deploy permesso solo nelle regioni EU (GDPR compliance)

package main

allowed_regions := {"eu-west-1", "eu-west-2", "eu-central-1", "eu-south-1"}

deny[msg] {
  resource := input.resource[resource_type][resource_name]
  region := resource.config.region[0].constant_value

  not region in allowed_regions

  msg := sprintf(
    "POLICY VIOLATION: %s '%s' usa la regione '%s' non permessa per compliance GDPR. Regioni ammesse: %v",
    [resource_type, resource_name, region, allowed_regions]
  )
}
# Uso di conftest con piano Terraform in JSON

# 1. Genera il piano Terraform in formato JSON
terraform init
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json

# 2. Esegui conftest con le policy
conftest test tfplan.json --policy policy/terraform/

# Output esempio:
# FAIL - tfplan.json - main - POLICY VIOLATION: aws_s3_bucket 'logs' manca il tag obbligatorio 'CostCenter'
# FAIL - tfplan.json - main - POLICY VIOLATION: aws_s3_bucket 'data' usa la regione 'us-east-1' non permessa per compliance GDPR
# 2 tests, 2 failures

# Test con output strutturato JSON
conftest test tfplan.json --policy policy/ --output json

# Test con namespace specifico
conftest test tfplan.json --policy policy/ --namespace main

Pipeline CI Integrata con Tutti i Tool

# .github/workflows/security-scan.yml
name: IaC Security Scan

on:
  pull_request:
    paths:
      - '**.tf'
      - '**.tfvars'

jobs:
  checkov:
    name: Checkov Scan
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Checkov
        uses: bridgecrewio/checkov-action@v12
        with:
          directory: .
          framework: terraform
          output_format: sarif
          output_file_path: reports/checkov.sarif
          soft_fail: true  # Non blocca la pipeline su LOW/MEDIUM

      - name: Upload SARIF to GitHub Code Scanning
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: reports/checkov.sarif

  trivy:
    name: Trivy Scan
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Trivy
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'config'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy.sarif'
          severity: 'HIGH,CRITICAL'
          exit-code: '1'   # Blocca la pipeline su HIGH/CRITICAL

      - name: Upload Trivy SARIF
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: trivy.sarif

  opa-policy:
    name: OPA Policy Check
    runs-on: ubuntu-latest
    needs: [checkov, trivy]

    steps:
      - uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: '1.7.5'

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.TERRAFORM_PLAN_ROLE }}
          aws-region: eu-west-1

      - name: Generate Terraform Plan JSON
        run: |
          cd environments/production
          terraform init -no-color
          terraform plan -out=tfplan -no-color
          terraform show -json tfplan > ../../tfplan.json

      - name: Install conftest
        run: |
          curl -L https://github.com/open-policy-agent/conftest/releases/download/v0.49.0/conftest_0.49.0_linux_amd64.tar.gz | tar xz
          sudo mv conftest /usr/local/bin/

      - name: Run OPA Policies
        run: |
          conftest test tfplan.json --policy policy/terraform/ --output json | tee opa-results.json
          # Fallisce se ci sono violation
          jq -e '.[] | select(.failures | length > 0)' opa-results.json && exit 1 || exit 0

Matrice di Copertura: Quali Tool per Quali Rischi

Rischio Checkov Trivy OPA
S3 public access Si (CKV_AWS_20) Si Custom
Encryption at rest mancante Si (molti check) Si No
Security group too permissive Si Si No
Tag obbligatori mancanti Parziale No Si (custom Rego)
Naming convention No No Si (custom Rego)
Regioni non permesse (GDPR) No No Si (custom Rego)
Segreti nel codice Parziale Si (secret scanning) No
Vulnerabilità container No Si No
Dipendenze vulnerabili No Si No

Conclusioni e Prossimi Passi

La combinazione di Checkov (copertura ampia built-in), Trivy (scanner unificato) e OPA (policy organizzative custom) forma una difesa a strati che copre la maggior parte dei rischi di sicurezza nell'IaC. Il principio chiave è che questi strumenti devono bloccare il merge del PR, non essere avvisi ignorabili.

Prossimi Articoli della Serie

  • Articolo 7: Terraform Multi-Cloud - AWS + Azure + GCP con Moduli Condivisi: costruisci un abstraction layer per gestire provider multipli con un'interfaccia unificata.
  • Articolo 8: GitOps per Terraform - Flux TF Controller e Drift Detection: porta Terraform nel paradigma GitOps con riconciliazione continua.

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