Skip to content

PolicyAsACodeRegoExample

Voici un exemple concret en Rego, le langage utilisé par Open Policy Agent (OPA), pour empêcher le déploiement d’un bucket Amazon S3 accessible publiquement via Terraform. OPA évalue une entrée structurée — ici le plan Terraform — et renvoie une ou plusieurs raisons de refus. Rego est conçu pour exprimer des politiques déclaratives sur des données hiérarchiques. openpolicyagent

Règle de sécurité

Politique métier : « Aucun bucket de stockage ne doit être accessible publiquement. »

Fichier policies/s3.rego :

package terraform.security

# Aucun refus par défaut
default deny := []

# Refuse un bucket dont l'ACL est publiquement lisible
deny contains message if {
  resource := input.resource_changes[_]

  resource.type == "aws_s3_bucket"
  resource.change.after.acl == "public-read"

  message := sprintf(
    "Refus : le bucket %q est public (ACL public-read). Configurez-le en privé.",
    [resource.address],
  )
}

# Refuse une ressource qui désactive le blocage des ACL publiques
deny contains message if {
  resource := input.resource_changes[_]

  resource.type == "aws_s3_bucket_public_access_block"
  resource.change.after.block_public_acls == false

  message := sprintf(
    "Refus : %q doit bloquer les ACL publiques (block_public_acls = true).",
    [resource.address],
  )
}

Cette politique parcourt les ressources prévues dans input.resource_changes. Elle produit un message de refus lorsqu’un bucket utilise l’ACL public-read, ou lorsqu’une configuration désactive le blocage des ACL publiques. Un schéma de contrôle comparable — refuser une ressource qui rend un bucket public — est employé dans les exemples de politiques OPA appliquées à des plans Terraform. openpolicyagent

Entrée contrôlée

Le pipeline CI convertit ou transmet le plan Terraform à OPA sous une forme JSON, par exemple :

{
  "resource_changes": [
    {
      "address": "aws_s3_bucket.documents_clients",
      "type": "aws_s3_bucket",
      "change": {
        "after": {
          "bucket": "documents-clients-production",
          "acl": "public-read"
        }
      }
    },
    {
      "address": "aws_s3_bucket_public_access_block.documents_clients",
      "type": "aws_s3_bucket_public_access_block",
      "change": {
        "after": {
          "block_public_acls": false
        }
      }
    }
  ]
}

Résultat

Lors de la CI, l’évaluation peut retourner :

{
  "deny": [
    "Refus : le bucket \"aws_s3_bucket.documents_clients\" est public (ACL public-read). Configurez-le en privé.",
    "Refus : \"aws_s3_bucket_public_access_block.documents_clients\" doit bloquer les ACL publiques (block_public_acls = true)."
  ]
}

Le pipeline interprète une liste deny non vide comme un échec et bloque le terraform apply. L’équipe corrige l’infrastructure avant qu’une ressource publique ne soit créée.

Version Terraform conforme

Voici une configuration qui satisferait cette règle :

resource "aws_s3_bucket" "documents_clients" {
  bucket = "documents-clients-production"

  tags = {
    owner       = "equipe-plateforme"
    environment = "production"
    data_class  = "confidentiel"
  }
}

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

  block_public_acls       = true
  ignore_public_acls      = true
  block_public_policy     = true
  restrict_public_buckets = true
}

Dans un contexte d’entreprise, on complète souvent cette première règle par des contrôles de chiffrement, de tags obligatoires, de région autorisée, de journalisation, de durée de rétention et d’accès IAM minimal. L’important est que chaque contrôle soit automatisable, explicite, revu dans Git et exécutable dès la pull request plutôt qu’après un incident.