{"id":483,"date":"2026-02-05T23:01:46","date_gmt":"2026-02-05T22:01:46","guid":{"rendered":"https:\/\/secure-pipelines.com\/?p=483"},"modified":"2026-03-24T12:55:08","modified_gmt":"2026-03-24T11:55:08","slug":"policy-as-code-ci-cd-opa-rego-security-gates-2","status":"publish","type":"post","link":"https:\/\/secure-pipelines.com\/fr\/ci-cd-security\/policy-as-code-ci-cd-opa-rego-security-gates-2\/","title":{"rendered":"Policy as Code pour le CI\/CD : Appliquer des Gates de S\u00e9curit\u00e9 avec OPA et Rego"},"content":{"rendered":"<h2>Introduction : Pourquoi les Revues de S\u00e9curit\u00e9 Manuelles ne Passent pas \u00e0 l&rsquo;\u00c9chelle<\/h2>\n<p>Chaque \u00e9quipe d&rsquo;ing\u00e9nierie finit par se heurter au m\u00eame mur : les revues de s\u00e9curit\u00e9 qui d\u00e9pendent de l&rsquo;\u0153il humain ne peuvent pas suivre le rythme des pipelines CI\/CD modernes. Lorsque les \u00e9quipes d\u00e9ploient des dizaines ou des centaines de fois par jour, demander \u00e0 un ing\u00e9nieur s\u00e9curit\u00e9 de v\u00e9rifier manuellement chaque plan Terraform, chaque manifeste Kubernetes ou chaque Dockerfile devient un goulot d&rsquo;\u00e9tranglement qui soit ralentit la livraison \u00e0 un rythme d&rsquo;escargot, soit est tout simplement contourn\u00e9.<\/p>\n<p>Les cons\u00e9quences sont pr\u00e9visibles. Des erreurs de configuration passent entre les mailles du filet. Des conteneurs s&rsquo;ex\u00e9cutent en tant que root. Les images de base d\u00e9rivent vers des versions non corrig\u00e9es. Terraform provisionne des buckets S3 accessibles publiquement. Il ne s&rsquo;agit pas de vuln\u00e9rabilit\u00e9s zero-day exotiques \u2014 ce sont des sch\u00e9mas connus comme probl\u00e9matiques qui pourraient \u00eatre d\u00e9tect\u00e9s automatiquement si nous disposions d&rsquo;un moyen syst\u00e9matique d&rsquo;exprimer et d&rsquo;appliquer des r\u00e8gles de s\u00e9curit\u00e9.<\/p>\n<p>C&rsquo;est l\u00e0 que le <strong>Policy as Code<\/strong> entre en jeu. Au lieu d&rsquo;int\u00e9grer des v\u00e9rifications de s\u00e9curit\u00e9 sous forme de scripts shell fragiles dispers\u00e9s dans les d\u00e9finitions de pipelines, le Policy as Code traite les r\u00e8gles de s\u00e9curit\u00e9 comme des artefacts de premi\u00e8re classe : d\u00e9claratifs, versionn\u00e9s, testables et applicables \u00e0 chaque \u00e9tape du cycle de vie CI\/CD.<\/p>\n<p>Dans ce guide, nous allons explorer comment utiliser l&rsquo;<strong>Open Policy Agent (OPA)<\/strong> et son langage de politiques <strong>Rego<\/strong> pour construire des gates de s\u00e9curit\u00e9 automatis\u00e9s et auditables dans vos pipelines CI\/CD \u2014 des gates qui \u00e9voluent avec votre cadence de d\u00e9ploiement plut\u00f4t que contre elle.<\/p>\n<h2>Qu&rsquo;est-ce que le Policy as Code ?<\/h2>\n<p>Le Policy as Code est une m\u00e9thodologie pour d\u00e9finir, g\u00e9rer et appliquer des r\u00e8gles en utilisant du code plut\u00f4t que des processus manuels ou des scripts ad-hoc. En son c\u0153ur, il s&rsquo;agit d&rsquo;\u00e9crire des <strong>r\u00e8gles d\u00e9claratives<\/strong> qui sont \u00e9valu\u00e9es par rapport \u00e0 des <strong>donn\u00e9es structur\u00e9es<\/strong> pour produire des <strong>d\u00e9cisions<\/strong> \u2014 autoriser, refuser ou avertir.<\/p>\n<h3>Concepts Fondamentaux<\/h3>\n<ul>\n<li><strong>R\u00e8gles d\u00e9claratives \u00e9valu\u00e9es par rapport \u00e0 des donn\u00e9es structur\u00e9es :<\/strong> Les politiques d\u00e9crivent <em>ce qui<\/em> doit \u00eatre vrai, et non <em>comment<\/em> le v\u00e9rifier. Un moteur de politiques re\u00e7oit une entr\u00e9e structur\u00e9e (JSON, YAML) et \u00e9value les r\u00e8gles par rapport \u00e0 celle-ci pour produire une d\u00e9cision.<\/li>\n<li><strong>S\u00e9paration de la politique et de la logique du pipeline :<\/strong> Les politiques vivent dans leurs propres d\u00e9p\u00f4ts, maintenues par les \u00e9quipes s\u00e9curit\u00e9 ou plateforme. Les d\u00e9finitions de pipelines r\u00e9f\u00e9rencent les politiques mais ne les contiennent pas. Cette s\u00e9paration des pr\u00e9occupations signifie que les changements de politique ne n\u00e9cessitent pas de changements de pipeline, et vice versa.<\/li>\n<li><strong>Versionn\u00e9, testable, r\u00e9visable :<\/strong> Parce que les politiques sont du code, elles suivent le m\u00eame cycle de vie que le code applicatif \u2014 pull requests, revues de code, tests automatis\u00e9s et releases versionn\u00e9es.<\/li>\n<li><strong>Auditable par conception :<\/strong> Chaque \u00e9valuation de politique produit une d\u00e9cision avec une trace claire de ce qui a \u00e9t\u00e9 \u00e9valu\u00e9, quelles r\u00e8gles ont correspondu et pourquoi. C&rsquo;est essentiel pour la conformit\u00e9 et la r\u00e9ponse aux incidents.<\/li>\n<\/ul>\n<h3>En Quoi Cela Diff\u00e8re des V\u00e9rifications par Script Shell<\/h3>\n<p>Beaucoup d&rsquo;\u00e9quipes commencent avec des scripts shell dans leurs pipelines \u2014 un <code>grep<\/code> pour \u00ab latest \u00bb dans un Dockerfile, une requ\u00eate <code>jq<\/code> sur un plan Terraform. Cela fonctionne au d\u00e9but mais s&rsquo;effondre rapidement :<\/p>\n<ul>\n<li>Les scripts shell sont imp\u00e9ratifs et fragiles \u2014 un changement mineur de format les casse.<\/li>\n<li>Ils manquent de composabilit\u00e9 \u2014 combiner plusieurs v\u00e9rifications n\u00e9cessite une logique d&rsquo;orchestration.<\/li>\n<li>Ils produisent une sortie incoh\u00e9rente \u2014 pas de format standard pour les violations ou les avertissements.<\/li>\n<li>Ils sont difficiles \u00e0 tester de mani\u00e8re isol\u00e9e.<\/li>\n<li>Ils ne peuvent pas \u00eatre facilement partag\u00e9s entre \u00e9quipes ou pipelines.<\/li>\n<\/ul>\n<p>Le Policy as Code r\u00e9sout tous ces probl\u00e8mes en fournissant un cadre structur\u00e9 et d\u00e9claratif avec un moteur d&rsquo;\u00e9valuation d\u00e9di\u00e9.<\/p>\n<h2>Fondamentaux d&rsquo;OPA et Rego<\/h2>\n<p>L&rsquo;<strong>Open Policy Agent (OPA)<\/strong> est un moteur de politiques open-source et g\u00e9n\u00e9raliste maintenu par la Cloud Native Computing Foundation (CNCF). Il d\u00e9couple la politique des services qui doivent l&rsquo;appliquer, fournissant un cadre unique pour la politique \u00e0 travers toute la stack \u2014 du contr\u00f4le d&rsquo;admission Kubernetes aux gates CI\/CD en passant par l&rsquo;autorisation des API.<\/p>\n<h3>Comment Fonctionne OPA<\/h3>\n<p>OPA suit un mod\u00e8le simple : <strong>entr\u00e9e \u2192 politique \u2192 d\u00e9cision<\/strong>.<\/p>\n<ul>\n<li><strong>Entr\u00e9e :<\/strong> Des donn\u00e9es structur\u00e9es (JSON) repr\u00e9sentant l&rsquo;\u00e9l\u00e9ment \u00e9valu\u00e9 \u2014 un manifeste Kubernetes, un plan Terraform, un arbre d&rsquo;analyse de Dockerfile ou une configuration de pipeline.<\/li>\n<li><strong>Politique :<\/strong> Un ou plusieurs fichiers Rego d\u00e9finissant des r\u00e8gles qui \u00e9valuent l&rsquo;entr\u00e9e.<\/li>\n<li><strong>D\u00e9cision :<\/strong> Un r\u00e9sultat JSON indiquant si l&rsquo;entr\u00e9e est conforme, et sinon, pourquoi.<\/li>\n<\/ul>\n<h3>Syntaxe de Base de Rego<\/h3>\n<p>Rego est le langage de politique con\u00e7u sp\u00e9cifiquement pour OPA. Il est d\u00e9claratif, ce qui signifie que vous d\u00e9crivez des conditions plut\u00f4t que d&rsquo;\u00e9crire une logique pas \u00e0 pas. Les \u00e9l\u00e9ments cl\u00e9s incluent :<\/p>\n<ul>\n<li><strong>Packages :<\/strong> Organisent les politiques de mani\u00e8re logique dans des espaces de noms (ex. <code>package cicd.docker<\/code>).<\/li>\n<li><strong>R\u00e8gles :<\/strong> Expressions nomm\u00e9es qui s&rsquo;\u00e9valuent \u00e0 vrai ou produisent des valeurs.<\/li>\n<li><strong>Imports :<\/strong> R\u00e9f\u00e9rencent des donn\u00e9es depuis l&rsquo;entr\u00e9e ou des sources de donn\u00e9es externes.<\/li>\n<\/ul>\n<h3>Un Exemple Simple : Refuser le Tag \u00ab latest \u00bb<\/h3>\n<p>Commen\u00e7ons par une r\u00e8gle de s\u00e9curit\u00e9 courante : refuser tout d\u00e9ploiement Kubernetes qui utilise le tag d&rsquo;image <code>latest<\/code>, car cela rend les builds non reproductibles et masque la version r\u00e9elle ex\u00e9cut\u00e9e en production.<\/p>\n<pre><code># policy\/k8s\/deny_latest_tag.rego\npackage k8s.images\n\ndeny[msg] {\n    container := input.spec.template.spec.containers[_]\n    endswith(container.image, \":latest\")\n    msg := sprintf(\"Container '%s' uses the 'latest' tag \u2014 pin to a specific version\", [container.name])\n}\n\ndeny[msg] {\n    container := input.spec.template.spec.containers[_]\n    not contains(container.image, \":\")\n    msg := sprintf(\"Container '%s' has no tag specified (defaults to 'latest') \u2014 pin to a specific version\", [container.name])\n}<\/code><\/pre>\n<p>Cette politique it\u00e8re sur tous les conteneurs dans une spec de d\u00e9ploiement Kubernetes et g\u00e9n\u00e8re un message de refus si l&rsquo;image utilise <code>:latest<\/code> ou n&rsquo;a aucun tag sp\u00e9cifi\u00e9.<\/p>\n<h3>Ex\u00e9cuter OPA Localement<\/h3>\n<p>Vous pouvez \u00e9valuer cette politique localement avec le CLI OPA :<\/p>\n<pre><code># Sauvegarder un exemple d'entr\u00e9e\ncat > input.json <<'EOF'\n{\n  \"spec\": {\n    \"template\": {\n      \"spec\": {\n        \"containers\": [\n          {\"name\": \"app\", \"image\": \"myregistry\/app:latest\"},\n          {\"name\": \"sidecar\", \"image\": \"envoyproxy\/envoy:v1.28.0\"}\n        ]\n      }\n    }\n  }\n}\nEOF\n\n# \u00c9valuer la politique\nopa eval --input input.json --data policy\/ \"data.k8s.images.deny\"<\/code><\/pre>\n<p>La sortie inclura le message de refus pour le conteneur <code>app<\/code> utilisant <code>:latest<\/code>, tandis que le conteneur <code>sidecar<\/code> avec une version \u00e9pingl\u00e9e passe sans probl\u00e8me.<\/p>\n<h2>Cas d'Usage CI\/CD pour OPA<\/h2>\n<p>OPA ne se limite pas \u00e0 Kubernetes. Sa conception agnostique par rapport aux entr\u00e9es le rend utile partout o\u00f9 vous devez valider des donn\u00e9es structur\u00e9es par rapport \u00e0 des r\u00e8gles. Voici les cas d'usage CI\/CD les plus impactants.<\/p>\n<h3>Valider les Manifestes Kubernetes Avant le D\u00e9ploiement<\/h3>\n<p>D\u00e9tectez les erreurs de configuration avant qu'elles n'atteignent le cluster : limites de ressources manquantes, conteneurs privil\u00e9gi\u00e9s, acc\u00e8s au r\u00e9seau h\u00f4te, contextes de s\u00e9curit\u00e9 manquants ou labels non conformes.<\/p>\n<pre><code># policy\/k8s\/deny_privileged.rego\npackage k8s.security\n\ndeny[msg] {\n    container := input.spec.template.spec.containers[_]\n    container.securityContext.privileged == true\n    msg := sprintf(\"Container '%s' must not run in privileged mode\", [container.name])\n}\n\ndeny[msg] {\n    not input.spec.template.spec.containers[_].resources.limits\n    msg := \"All containers must define resource limits\"\n}<\/code><\/pre>\n<h3>Appliquer les Bonnes Pratiques Dockerfile<\/h3>\n<p>En utilisant des outils comme <code>conftest<\/code> avec un parseur de Dockerfile (comme la sortie JSON de <code>hadolint<\/code> ou <code>dockerfile-json<\/code>), vous pouvez appliquer des r\u00e8gles comme l'interdiction d'ex\u00e9cution en tant que root et l'\u00e9pinglage des images de base :<\/p>\n<pre><code># policy\/docker\/best_practices.rego\npackage docker\n\ndeny[msg] {\n    input.stages[_].commands[i].cmd == \"user\"\n    input.stages[_].commands[i].value == \"root\"\n    msg := \"Dockerfile must not explicitly set USER to root\"\n}\n\ndeny[msg] {\n    stage := input.stages[_]\n    stage.base_image\n    not contains(stage.base_image, \"@sha256:\")\n    not regex.match(`:.+$`, stage.base_image)\n    msg := sprintf(\"Base image '%s' must be pinned to a tag or digest\", [stage.base_image])\n}<\/code><\/pre>\n<h3>V\u00e9rifier les Plans Terraform pour les Violations de S\u00e9curit\u00e9<\/h3>\n<p>Convertissez un plan Terraform en JSON avec <code>terraform show -json tfplan<\/code>, puis validez-le par rapport aux politiques de s\u00e9curit\u00e9 :<\/p>\n<pre><code># policy\/terraform\/aws_security.rego\npackage terraform.aws\n\ndeny[msg] {\n    resource := input.resource_changes[_]\n    resource.type == \"aws_s3_bucket\"\n    resource.change.after.acl == \"public-read\"\n    msg := sprintf(\"S3 bucket '%s' must not be publicly readable\", [resource.address])\n}\n\ndeny[msg] {\n    resource := input.resource_changes[_]\n    resource.type == \"aws_security_group_rule\"\n    resource.change.after.cidr_blocks[_] == \"0.0.0.0\/0\"\n    resource.change.after.type == \"ingress\"\n    msg := sprintf(\"Security group rule '%s' must not allow ingress from 0.0.0.0\/0\", [resource.address])\n}<\/code><\/pre>\n<h3>Valider les Configurations de Pipeline<\/h3>\n<p>Vous pouvez m\u00eame appliquer des r\u00e8gles sur les d\u00e9finitions de pipeline elles-m\u00eames \u2014 en vous assurant que chaque pipeline inclut les \u00e9tapes requises comme le scan SAST, la d\u00e9tection de secrets ou la signature d'images :<\/p>\n<pre><code># policy\/pipeline\/required_steps.rego\npackage pipeline\n\nrequired_jobs := {\"sast-scan\", \"secret-detection\", \"image-sign\"}\n\nmissing_jobs[job] {\n    job := required_jobs[_]\n    not job_exists(job)\n}\n\njob_exists(name) {\n    input.jobs[name]\n}\n\ndeny[msg] {\n    count(missing_jobs) > 0\n    msg := sprintf(\"Pipeline is missing required security jobs: %v\", [missing_jobs])\n}<\/code><\/pre>\n<h3>Appliquer la Protection des Branches et les Politiques d'Approbation<\/h3>\n<p>Validez que les modifications vers les branches de production sont accompagn\u00e9es des approbations requises et passent les v\u00e9rifications obligatoires avant le merge. OPA peut \u00e9valuer les payloads de webhooks GitHub ou GitLab ou les r\u00e9ponses d'API pour appliquer ces politiques de mani\u00e8re programmatique.<\/p>\n<h2>Int\u00e9grer OPA dans les Pipelines CI\/CD<\/h2>\n<p>La mani\u00e8re la plus ergonomique d'int\u00e9grer OPA dans le CI\/CD est via <strong>Conftest<\/strong>, un outil de test construit au-dessus d'OPA et sp\u00e9cifiquement con\u00e7u pour valider des fichiers de configuration structur\u00e9s. Il comprend nativement YAML, JSON, HCL, Dockerfile et de nombreux autres formats.<\/p>\n<h3>GitHub Actions : OPA avec Conftest<\/h3>\n<pre><code># .github\/workflows\/policy-check.yml\nname: Policy Checks\n\non:\n  pull_request:\n    branches: [main]\n\njobs:\n  validate-kubernetes:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions\/checkout@v4\n\n      - name: Install Conftest\n        run: |\n          LATEST=$(wget -qO- \"https:\/\/api.github.com\/repos\/open-policy-agent\/conftest\/releases\/latest\" | jq -r '.tag_name' | sed 's\/v\/\/')\n          wget -q \"https:\/\/github.com\/open-policy-agent\/conftest\/releases\/download\/v${LATEST}\/conftest_${LATEST}_Linux_x86_64.tar.gz\"\n          tar xzf conftest_${LATEST}_Linux_x86_64.tar.gz\n          sudo mv conftest \/usr\/local\/bin\/\n\n      - name: Validate Kubernetes manifests\n        run: |\n          conftest test k8s\/*.yaml \\\n            --policy policy\/k8s\/ \\\n            --output json \\\n            --all-namespaces\n\n  validate-terraform:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions\/checkout@v4\n\n      - name: Setup Terraform\n        uses: hashicorp\/setup-terraform@v3\n\n      - name: Install Conftest\n        run: |\n          LATEST=$(wget -qO- \"https:\/\/api.github.com\/repos\/open-policy-agent\/conftest\/releases\/latest\" | jq -r '.tag_name' | sed 's\/v\/\/')\n          wget -q \"https:\/\/github.com\/open-policy-agent\/conftest\/releases\/download\/v${LATEST}\/conftest_${LATEST}_Linux_x86_64.tar.gz\"\n          tar xzf conftest_${LATEST}_Linux_x86_64.tar.gz\n          sudo mv conftest \/usr\/local\/bin\/\n\n      - name: Generate Terraform plan JSON\n        run: |\n          cd terraform\/\n          terraform init\n          terraform plan -out=tfplan\n          terraform show -json tfplan > tfplan.json\n\n      - name: Validate Terraform plan\n        run: |\n          conftest test terraform\/tfplan.json \\\n            --policy policy\/terraform\/ \\\n            --output json<\/code><\/pre>\n<h3>GitLab CI : Conftest dans un Job CI<\/h3>\n<pre><code># .gitlab-ci.yml\nstages:\n  - validate\n  - build\n  - deploy\n\npolicy-check-k8s:\n  stage: validate\n  image: openpolicyagent\/conftest:latest\n  script:\n    - conftest test k8s\/*.yaml\n        --policy policy\/k8s\/\n        --output json\n        --all-namespaces\n  rules:\n    - changes:\n        - k8s\/**\/*\n        - policy\/k8s\/**\/*\n\npolicy-check-terraform:\n  stage: validate\n  image:\n    name: hashicorp\/terraform:latest\n    entrypoint: [\"\"]\n  before_script:\n    - apk add --no-cache wget\n    - wget -q https:\/\/github.com\/open-policy-agent\/conftest\/releases\/download\/v0.50.0\/conftest_0.50.0_Linux_x86_64.tar.gz\n    - tar xzf conftest_0.50.0_Linux_x86_64.tar.gz\n    - mv conftest \/usr\/local\/bin\/\n  script:\n    - cd terraform\/\n    - terraform init\n    - terraform plan -out=tfplan\n    - terraform show -json tfplan > tfplan.json\n    - conftest test tfplan.json --policy ..\/policy\/terraform\/\n  rules:\n    - changes:\n        - terraform\/**\/*\n        - policy\/terraform\/**\/*<\/code><\/pre>\n<h3>Conftest vs CLI OPA Brut : Quand Utiliser Lequel<\/h3>\n<ul>\n<li><strong>Utilisez Conftest quand :<\/strong> Vous validez des fichiers de configuration (YAML, JSON, HCL, Dockerfiles) dans le CI\/CD. Conftest g\u00e8re le parsing des fichiers, fournit des formats de sortie structur\u00e9s, supporte plusieurs types de fichiers et suit les conventions \u00e9tablies (r\u00e8gles <code>deny<\/code>, <code>warn<\/code>, <code>violation<\/code>).<\/li>\n<li><strong>Utilisez le CLI OPA brut quand :<\/strong> Vous devez \u00e9valuer des politiques par rapport \u00e0 une entr\u00e9e JSON personnalis\u00e9e, int\u00e9grer OPA en tant que sidecar ou daemon pour des d\u00e9cisions en temps r\u00e9el, travailler avec des bundles OPA ou avez besoin de l'API OPA compl\u00e8te (\u00e9valuation partielle, profilage, etc.).<\/li>\n<\/ul>\n<p>Pour la plupart des cas d'usage de gates de s\u00e9curit\u00e9 CI\/CD, Conftest est le bon choix. Il r\u00e9duit le boilerplate et s'int\u00e8gre proprement dans les \u00e9tapes de pipeline.<\/p>\n<h2>\u00c9crire des Politiques Rego Efficaces<\/h2>\n<p>\u00c9crire des politiques Rego maintenables, d\u00e9boguables et utiles en pratique n\u00e9cessite de suivre des patterns et conventions \u00e9tablis.<\/p>\n<h3>Deny-by-Default vs Allow-by-Default<\/h3>\n<p>Il existe deux approches fondamentales :<\/p>\n<ul>\n<li><strong>Deny-by-default :<\/strong> Tout est autoris\u00e9 sauf si une r\u00e8gle <code>deny<\/code> correspond. C'est la convention standard de Conftest et cela fonctionne bien pour les gates CI\/CD o\u00f9 vous voulez d\u00e9tecter des sch\u00e9mas sp\u00e9cifiques connus comme probl\u00e9matiques.<\/li>\n<li><strong>Allow-by-default avec refus explicites :<\/strong> Identique au pr\u00e9c\u00e9dent \u2014 c'est le pattern le plus courant pour les cas d'usage CI\/CD.<\/li>\n<\/ul>\n<p>Pour une s\u00e9curit\u00e9 maximale, certaines organisations utilisent un mod\u00e8le <strong>strict deny-by-default<\/strong> o\u00f9 l'entr\u00e9e doit correspondre explicitement \u00e0 une r\u00e8gle <code>allow<\/code> sinon elle est rejet\u00e9e. Ceci est plus appropri\u00e9 pour le contr\u00f4le d'admission que pour les gates CI\/CD.<\/p>\n<pre><code># Deny-by-default (courant pour le CI\/CD \u2014 d\u00e9tecte les violations sp\u00e9cifiques)\npackage k8s.images\n\ndeny[msg] {\n    # Refuser explicitement les sch\u00e9mas connus comme probl\u00e9matiques\n    container := input.spec.template.spec.containers[_]\n    endswith(container.image, \":latest\")\n    msg := sprintf(\"Container '%s' uses ':latest' tag\", [container.name])\n}\n\n# Strict allow-only (tout ce qui n'est pas explicitement autoris\u00e9 est refus\u00e9)\npackage k8s.registries\n\nallowed_registries := {\n    \"gcr.io\/my-project\",\n    \"us-docker.pkg.dev\/my-project\",\n}\n\ndeny[msg] {\n    container := input.spec.template.spec.containers[_]\n    image := container.image\n    not image_from_allowed_registry(image)\n    msg := sprintf(\"Container '%s' uses image '%s' from a non-approved registry\", [container.name, image])\n}\n\nimage_from_allowed_registry(image) {\n    some registry in allowed_registries\n    startswith(image, registry)\n}<\/code><\/pre>\n<h3>G\u00e9n\u00e9rer des Messages de Violation Significatifs<\/h3>\n<p>Une politique qui dit \u00ab violation d\u00e9tect\u00e9e \u00bb est quasiment inutile. De bons messages de violation doivent indiquer \u00e0 l'ing\u00e9nieur <em>ce qui<\/em> ne va pas, <em>o\u00f9<\/em> dans la configuration cela se produit, et id\u00e9alement <em>comment<\/em> y rem\u00e9dier :<\/p>\n<pre><code>deny[msg] {\n    container := input.spec.template.spec.containers[_]\n    not container.securityContext.runAsNonRoot\n    msg := sprintf(\n        \"Container '%s' must set securityContext.runAsNonRoot to true. \"\n        \"See: https:\/\/wiki.internal\/policies\/container-security#non-root\",\n        [container.name]\n    )\n}<\/code><\/pre>\n<h3>Test des Politiques avec <code>opa test<\/code><\/h3>\n<p>Les politiques Rego doivent \u00eatre test\u00e9es comme le code applicatif. OPA inclut un framework de test int\u00e9gr\u00e9 :<\/p>\n<pre><code># policy\/k8s\/deny_latest_tag_test.rego\npackage k8s.images\n\ntest_deny_latest_tag {\n    result := deny with input as {\n        \"spec\": {\"template\": {\"spec\": {\"containers\": [\n            {\"name\": \"app\", \"image\": \"nginx:latest\"}\n        ]}}}\n    }\n    count(result) == 1\n    contains(result[_], \"latest\")\n}\n\ntest_allow_pinned_tag {\n    result := deny with input as {\n        \"spec\": {\"template\": {\"spec\": {\"containers\": [\n            {\"name\": \"app\", \"image\": \"nginx:1.25.3\"}\n        ]}}}\n    }\n    count(result) == 0\n}\n\ntest_deny_no_tag {\n    result := deny with input as {\n        \"spec\": {\"template\": {\"spec\": {\"containers\": [\n            {\"name\": \"app\", \"image\": \"nginx\"}\n        ]}}}\n    }\n    count(result) == 1\n}<\/code><\/pre>\n<p>Ex\u00e9cutez les tests avec :<\/p>\n<pre><code>opa test policy\/ -v<\/code><\/pre>\n<h3>Organiser les Politiques par Domaine<\/h3>\n<p>Une structure de d\u00e9p\u00f4t de politiques propre rend les politiques d\u00e9couvrables et maintenables :<\/p>\n<pre><code>policy\/\n\u251c\u2500\u2500 k8s\/\n\u2502   \u251c\u2500\u2500 deny_latest_tag.rego\n\u2502   \u251c\u2500\u2500 deny_latest_tag_test.rego\n\u2502   \u251c\u2500\u2500 deny_privileged.rego\n\u2502   \u251c\u2500\u2500 deny_privileged_test.rego\n\u2502   \u251c\u2500\u2500 require_labels.rego\n\u2502   \u2514\u2500\u2500 require_labels_test.rego\n\u251c\u2500\u2500 terraform\/\n\u2502   \u251c\u2500\u2500 aws_security.rego\n\u2502   \u251c\u2500\u2500 aws_security_test.rego\n\u2502   \u251c\u2500\u2500 gcp_security.rego\n\u2502   \u2514\u2500\u2500 gcp_security_test.rego\n\u251c\u2500\u2500 docker\/\n\u2502   \u251c\u2500\u2500 best_practices.rego\n\u2502   \u2514\u2500\u2500 best_practices_test.rego\n\u2514\u2500\u2500 pipeline\/\n    \u251c\u2500\u2500 required_steps.rego\n    \u2514\u2500\u2500 required_steps_test.rego<\/code><\/pre>\n<h3>G\u00e9rer les Bundles de Politiques<\/h3>\n<p>Pour les organisations comptant de nombreuses \u00e9quipes, la distribution des politiques sous forme de <strong>bundles OPA<\/strong> est l'approche recommand\u00e9e. Les bundles sont des archives tar versionn\u00e9es de fichiers Rego et de donn\u00e9es pouvant \u00eatre h\u00e9berg\u00e9es sur n'importe quel serveur HTTP, registre OCI ou stockage cloud :<\/p>\n<pre><code># Construire un bundle\nopa build -b policy\/ -o bundle.tar.gz\n\n# Pousser vers un registre OCI\nconftest push myregistry.io\/policies\/security:v1.2.0\n\n# R\u00e9cup\u00e9rer et utiliser dans un pipeline\nconftest pull myregistry.io\/policies\/security:v1.2.0\nconftest test k8s\/*.yaml --policy policy\/<\/code><\/pre>\n<p>Cette approche permet aux \u00e9quipes s\u00e9curit\u00e9 de publier des politiques de mani\u00e8re centralis\u00e9e tandis que les \u00e9quipes applicatives consomment des versions sp\u00e9cifiques, et permet un d\u00e9ploiement contr\u00f4l\u00e9 des nouvelles versions de politiques.<\/p>\n<h2>Faire \u00c9chouer les Pipelines de Mani\u00e8re S\u00fbre et Explicite<\/h2>\n<p>Appliquer des politiques dans le CI\/CD est autant un d\u00e9fi d'ing\u00e9nierie que de s\u00e9curit\u00e9. D\u00e9ployer des \u00e9checs stricts d\u00e8s le premier jour cr\u00e9era le chaos. Une approche mesur\u00e9e est essentielle.<\/p>\n<h3>Gates Strictes vs Gates Souples<\/h3>\n<p>Conftest supporte deux types de r\u00e8gles qui correspondent parfaitement \u00e0 cette distinction :<\/p>\n<ul>\n<li><strong>R\u00e8gles <code>deny<\/code> :<\/strong> Gates strictes. Le pipeline \u00e9choue si une r\u00e8gle <code>deny<\/code> correspond.<\/li>\n<li><strong>R\u00e8gles <code>warn<\/code> :<\/strong> Gates souples. Le pipeline enregistre l'avertissement mais continue. C'est inestimable pour le d\u00e9ploiement de nouvelles politiques.<\/li>\n<\/ul>\n<pre><code># Commencer avec warn, promouvoir en deny une fois que les \u00e9quipes se sont adapt\u00e9es\nwarn[msg] {\n    container := input.spec.template.spec.containers[_]\n    not container.resources.requests\n    msg := sprintf(\"[WARN] Container '%s' should define resource requests\", [container.name])\n}<\/code><\/pre>\n<h3>Exceptions et D\u00e9rogations de Politique<\/h3>\n<p>Aucune politique ne peut couvrir chaque cas l\u00e9gitime particulier. Vous avez besoin d'un m\u00e9canisme d'exceptions qui soit auditable et ne contourne pas enti\u00e8rement le syst\u00e8me :<\/p>\n<pre><code># policy\/k8s\/exceptions.rego\npackage k8s.images\n\nimport data.exceptions\n\ndeny[msg] {\n    container := input.spec.template.spec.containers[_]\n    endswith(container.image, \":latest\")\n    not exception_exists(input.metadata.name, container.name)\n    msg := sprintf(\"Container '%s' uses the 'latest' tag\", [container.name])\n}\n\nexception_exists(deployment, container) {\n    exception := exceptions.approved[_]\n    exception.deployment == deployment\n    exception.container == container\n    exception.policy == \"deny-latest-tag\"\n    time.now_ns() < exception.expires_ns\n}<\/code><\/pre>\n<p>Le fichier de donn\u00e9es d'exceptions est \u00e9galement versionn\u00e9 et n\u00e9cessite une approbation :<\/p>\n<pre><code># data\/exceptions.json\n{\n  \"approved\": [\n    {\n      \"deployment\": \"legacy-app\",\n      \"container\": \"app\",\n      \"policy\": \"deny-latest-tag\",\n      \"reason\": \"Legacy build system cannot produce tagged images \u2014 migration tracked in JIRA-1234\",\n      \"approved_by\": \"security-team\",\n      \"expires_ns\": 1735689600000000000\n    }\n  ]\n}<\/code><\/pre>\n<h3>Rapporter les R\u00e9sultats de Politique aux Tableaux de Bord<\/h3>\n<p>Conftest et OPA supportent tous deux la sortie JSON, ce qui facilite l'envoi des r\u00e9sultats vers des plateformes d'observabilit\u00e9. Dans votre pipeline, capturez la sortie et envoyez-la \u00e0 votre SIEM, plateforme de logging ou tableau de bord personnalis\u00e9 :<\/p>\n<pre><code># Capturer les r\u00e9sultats en JSON\nconftest test k8s\/*.yaml --policy policy\/k8s\/ --output json > policy-results.json\n\n# Envoyer \u00e0 votre plateforme de logging\ncurl -X POST https:\/\/logging.internal\/api\/v1\/policy-results \\\n  -H \"Content-Type: application\/json\" \\\n  -d @policy-results.json<\/code><\/pre>\n<p>Cela cr\u00e9e une piste d'audit ind\u00e9pendante des logs CI\/CD \u2014 essentielle pour la conformit\u00e9 et l'analyse des tendances.<\/p>\n<h3>D\u00e9ploiement Progressif : Mode Audit Avant Mode Application<\/h3>\n<p>La strat\u00e9gie de d\u00e9ploiement recommand\u00e9e pour toute nouvelle politique suit cette progression :<\/p>\n<ol>\n<li><strong>Mode audit :<\/strong> Ex\u00e9cutez la politique sous forme de r\u00e8gles <code>warn<\/code>. Collectez des donn\u00e9es sur le nombre de pipelines qui \u00e9choueraient. Partagez les rapports avec les \u00e9quipes.<\/li>\n<li><strong>Application souple :<\/strong> Gardez les r\u00e8gles <code>warn<\/code> mais ajoutez des notifications \u2014 alertes Slack, tickets Jira \u2014 pour que les \u00e9quipes soient inform\u00e9es et puissent rem\u00e9dier.<\/li>\n<li><strong>Application stricte :<\/strong> Promouvez les r\u00e8gles de <code>warn<\/code> \u00e0 <code>deny<\/code> apr\u00e8s une date limite communiqu\u00e9e. Assurez-vous que le processus d'exception est en place.<\/li>\n<li><strong>Ajustement continu :<\/strong> Surveillez les faux positifs, ajustez les politiques, ajoutez de nouvelles r\u00e8gles bas\u00e9es sur les incidents et les renseignements sur les menaces.<\/li>\n<\/ol>\n<p>Cette approche respecte les workflows d'ing\u00e9nierie tout en \u00e9levant progressivement le niveau de s\u00e9curit\u00e9.<\/p>\n<h2>Limitations et Compromis<\/h2>\n<p>Le Policy as Code avec OPA est puissant, mais comporte des compromis. \u00catre honn\u00eate sur les limitations vous aide \u00e0 prendre des d\u00e9cisions \u00e9clair\u00e9es.<\/p>\n<h3>La Courbe d'Apprentissage de Rego<\/h3>\n<p>Rego est un langage con\u00e7u dans un but pr\u00e9cis avec un mod\u00e8le d'\u00e9valuation unique. Il n'est pas imp\u00e9ratif \u2014 il n'y a pas de boucles ni de variables mutables au sens traditionnel. Les ing\u00e9nieurs habitu\u00e9s \u00e0 Python, Go ou Bash auront besoin de temps pour internaliser l'approche d\u00e9clarative et bas\u00e9e sur les ensembles de Rego. Investissez dans la formation d'\u00e9quipe, la programmation en bin\u00f4me sur les premi\u00e8res politiques et une biblioth\u00e8que d'exemples de politiques bien comment\u00e9s.<\/p>\n<h3>Performance avec des Entr\u00e9es Volumineuses<\/h3>\n<p>OPA \u00e9value les politiques en m\u00e9moire. Pour la plupart des cas d'usage CI\/CD \u2014 manifestes Kubernetes, plans Terraform, Dockerfiles \u2014 les tailles d'entr\u00e9e sont petites et l'\u00e9valuation est quasi instantan\u00e9e. Cependant, des plans Terraform tr\u00e8s volumineux (des milliers de ressources) ou des politiques complexes avec une r\u00e9cursion profonde peuvent causer une latence notable. Profilez les politiques avec <code>opa eval --profile<\/code> si la performance devient une pr\u00e9occupation.<\/p>\n<h3>OPA vs Autres Outils de Politique<\/h3>\n<p>OPA n'est pas la seule option. Consid\u00e9rez les alternatives en fonction de votre stack :<\/p>\n<ul>\n<li><strong>Kyverno :<\/strong> Moteur de politique natif Kubernetes. Si vos politiques concernent exclusivement les ressources Kubernetes et que vous pr\u00e9f\u00e9rez des politiques en YAML plut\u00f4t qu'en Rego, Kyverno est une excellente alternative.<\/li>\n<li><strong>HashiCorp Sentinel :<\/strong> \u00c9troitement int\u00e9gr\u00e9 avec Terraform Cloud\/Enterprise. Si votre organisation est standardis\u00e9e sur les outils HashiCorp et que vous avez besoin de politiques principalement pour Terraform, Sentinel peut \u00eatre plus naturel.<\/li>\n<li><strong>AWS Cedar :<\/strong> Con\u00e7u pour l'autorisation au niveau applicatif. Ce n'est pas un concurrent direct pour les cas d'usage de politique CI\/CD, mais c'est pertinent si vous construisez une autorisation fine pour votre plateforme.<\/li>\n<\/ul>\n<p>La force d'OPA est sa g\u00e9n\u00e9ralit\u00e9. Il fonctionne avec Kubernetes, Terraform, Docker, les configurations de pipeline et toute autre donn\u00e9e structur\u00e9e. Si vous avez besoin de politiques sur plusieurs domaines, OPA \u00e9vite la prolif\u00e9ration d'outils.<\/p>\n<h3>D\u00e9rive des Politiques et Maintenance<\/h3>\n<p>Les politiques sont des artefacts vivants. Elles n\u00e9cessitent une maintenance continue :<\/p>\n<ul>\n<li>Les nouveaux types de ressources et versions d'API n\u00e9cessitent de nouvelles r\u00e8gles.<\/li>\n<li>Les faux positifs \u00e9rodent la confiance et doivent \u00eatre trait\u00e9s rapidement.<\/li>\n<li>Les exceptions s'accumulent et n\u00e9cessitent une revue p\u00e9riodique.<\/li>\n<li>Le turnover des \u00e9quipes signifie que la connaissance de l'intention des politiques peut \u00eatre perdue.<\/li>\n<\/ul>\n<p>Traitez votre d\u00e9p\u00f4t de politiques avec la m\u00eame rigueur que votre code applicatif : assignez des propri\u00e9taires, planifiez des revues, suivez la couverture et d\u00e9pr\u00e9ciez les r\u00e8gles obsol\u00e8tes.<\/p>\n<h2>Conclusion : Le Policy as Code Est de l'Infrastructure<\/h2>\n<p>Le Policy as Code n'est pas un luxe ni une case \u00e0 cocher pour la conformit\u00e9. C'est de l'infrastructure \u2014 au m\u00eame titre que votre pipeline CI\/CD, votre orchestrateur de conteneurs et les API de votre fournisseur cloud sont de l'infrastructure. Cela m\u00e9rite la m\u00eame discipline d'ing\u00e9nierie.<\/p>\n<p>La voie \u00e0 suivre est claire :<\/p>\n<ol>\n<li><strong>Commencez petit.<\/strong> Choisissez une politique \u00e0 fort impact \u2014 refuser les tags <code>latest<\/code>, exiger des limites de ressources, bloquer les buckets S3 publics \u2014 et impl\u00e9mentez-la avec Conftest dans un seul pipeline.<\/li>\n<li><strong>D\u00e9veloppez le r\u00e9flexe.<\/strong> \u00c9crivez des tests pour vos politiques. Mettez en place un d\u00e9p\u00f4t de politiques avec du CI. Familiarisez l'\u00e9quipe avec Rego.<\/li>\n<li><strong>\u00c9tendez syst\u00e9matiquement.<\/strong> Ajoutez des politiques par domaine (Kubernetes, Terraform, Docker, configuration de pipeline). D\u00e9ployez d'abord en mode audit.<\/li>\n<li><strong>Op\u00e9rationnalisez.<\/strong> Construisez des tableaux de bord. D\u00e9finissez le processus d'exception. Int\u00e9grez avec votre workflow de r\u00e9ponse aux incidents.<\/li>\n<\/ol>\n<p>Traitez vos politiques comme du code : testez-les, r\u00e9visez-les, versionnez-les, d\u00e9ployez-les. Le r\u00e9sultat est une posture de s\u00e9curit\u00e9 qui \u00e9volue avec votre cadence de livraison \u2014 applicable, auditable et automatis\u00e9e du premier commit \u00e0 la production.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction : Pourquoi les Revues de S\u00e9curit\u00e9 Manuelles ne Passent pas \u00e0 l&rsquo;\u00c9chelle Chaque \u00e9quipe d&rsquo;ing\u00e9nierie finit par se heurter au m\u00eame mur : les revues de s\u00e9curit\u00e9 qui d\u00e9pendent de l&rsquo;\u0153il humain ne peuvent pas suivre le rythme des pipelines CI\/CD modernes. Lorsque les \u00e9quipes d\u00e9ploient des dizaines ou des centaines de fois par &#8230; <a title=\"Policy as Code pour le CI\/CD : Appliquer des Gates de S\u00e9curit\u00e9 avec OPA et Rego\" class=\"read-more\" href=\"https:\/\/secure-pipelines.com\/fr\/ci-cd-security\/policy-as-code-ci-cd-opa-rego-security-gates-2\/\" aria-label=\"En savoir plus sur Policy as Code pour le CI\/CD : Appliquer des Gates de S\u00e9curit\u00e9 avec OPA et Rego\">Lire la suite<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[49,51],"tags":[],"post_folder":[],"class_list":["post-483","post","type-post","status-publish","format-standard","hentry","category-ci-cd-security","category-pipeline-hardening"],"_links":{"self":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts\/483","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/comments?post=483"}],"version-history":[{"count":2,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts\/483\/revisions"}],"predecessor-version":[{"id":488,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts\/483\/revisions\/488"}],"wp:attachment":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/media?parent=483"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/categories?post=483"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/tags?post=483"},{"taxonomy":"post_folder","embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/post_folder?post=483"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}