# Policy-as-Code dla zespołów: warstwa kontroli platformy

> Polityka, której nie da się uruchomić, to dokument. Jak zamienić bezpieczeństwo i zgodność w warstwową płaszczyznę kontroli — od PR po admission — egzekwowaną jako guardrail, nie bramkarza.

URL: https://eiac.dev/blog/policy-as-code-dla-zespolow
Filar: SDLC / Policy-as-Code
Data: 2026-02-23
Tagi: opa, rego, security, ci, policy-as-code, platform-engineering, adp, guardrails

---

Znasz to: w firmowej wiki leży „standard", którego nikt nie czyta, a na review i tak przechodzi PR łamiący zasadę — bo recenzent akurat jej nie pamiętał. Sam nieraz byłem tym recenzentem. Polityka, której nie da się uruchomić, to nie polityka, tylko dokument. **Policy-as-code (PaC)** zamienia ją w coś przeciwnego: regułę zapisaną jako kod, którą maszyna sprawdza przy każdej zmianie — deterministycznie, w tym samym miejscu co reszta pipeline'u, z wynikiem, który albo przepuszcza, albo zatrzymuje.

W [wprowadzeniu do serii](/blog/platform-engineering-normalizacja-pracy) postawiliśmy tezę, że platform engineering to normalizacja pracy — kodowanie osądu *raz* i stosowanie go *wszędzie*. Policy-as-code jest tej tezy najczystszym wcieleniem: zamiast debatować każdy przypadek z osobna, zespół platformowy zapisuje regułę i pozwala jej działać w kółko. Ten artykuł rozwija PaC od pojedynczej bramki w CI do **warstwowej płaszczyzny kontroli** całej platformy — i pokazuje, dlaczego to ona staje się fundamentem zarówno zgodności regulacyjnej, jak i bezpiecznej autonomii agentów.

<div class="callout">
<strong>Teza</strong>
<p>Policy-as-code to nie pojedynczy skan w CI, lecz <em>warstwa kontroli</em> rozłożona wzdłuż całego cyklu — od IDE i PR, przez build i plan IaC, po admission w klastrze. Egzekwowana automatycznie i blisko miejsca, gdzie pracuje inżynier, zamienia bezpieczeństwo i zgodność z bramkarza, który blokuje, w guardrail, który prowadzi — i daje gotowy ślad audytowy jako produkt uboczny.</p>
</div>

## Co właściwie znaczy „as code"

Sednem PaC jest **oddzielenie decyzji od egzekwowania**. Zamiast zaszywać logikę „czy wolno?" w każdej aplikacji i każdym skrypcie, opisujesz ją deklaratywnie w jednym miejscu, a silnik polityk odpowiada na pytania w czasie, gdy decyzja jest potrzebna. To daje trzy własności, których dokument nigdy nie miał:

- **Wersjonowanie** — polityka żyje w repo, ma historię zmian, autora i przegląd jak każdy inny kod.
- **Testowalność** — regułę można pokryć testami i uruchomić lokalnie, zanim trafi na produkcję.
- **Audytowalność** — każde uruchomienie zostawia ślad: co sprawdzono, na jakich danych i z jakim wynikiem.

To ta sama zmiana nośnika, którą opisaliśmy przy [tokenach designu](/blog/design-as-code-tokeny-od-zera): gdy reguła staje się danymi, przestaje zależeć od ludzkiej pamięci, a zaczyna od pipeline'u.

## OPA i Rego: uniwersalny silnik

[Open Policy Agent](/katalog/open-policy-agent) (OPA) to ogólnego przeznaczenia silnik polityk i [projekt CNCF na poziomie „graduated"](https://www.cncf.io/projects/) — czyli najwyższym poziomie dojrzałości. Jego siła to uniwersalność: jednym językiem opiszesz reguły dla Kubernetes, planu [Terraform/OpenTofu](/blog/suwerenny-idp-exit-by-design), API mikroserwisu czy pipeline'u CI. OPA nie wie nic o domenie — dostaje dane wejściowe (JSON) i zwraca decyzję.

Reguły pisze się w [Rego](https://www.openpolicyagent.org/docs/policy-language) — deklaratywnym języku wywodzącym się z Datalog, zaprojektowanym do odpytywania złożonych, zagnieżdżonych struktur danych. Prosty przykład bramki w CI, która odrzuca obraz z tagiem `latest`:

```rego
package ci.images

# odrzuć każdy kontener używający :latest
deny contains msg if {
    some c in input.spec.containers
    endswith(c.image, ":latest")
    msg := sprintf("obraz %v używa :latest — przypnij wersję", [c.image])
}
```

```bash
# ta sama reguła jako bramka w pipeline (bez klastra)
$ conftest test deployment.yaml
FAIL - deployment.yaml - ci.images - obraz app:latest używa :latest — przypnij wersję
1 test, 0 passed, 1 failure   # exit 1 → PR nie przechodzi
```

Tu rolę uruchamiacza w CI pełni [Conftest](/katalog/conftest) (Rego poza klastrem), a tę samą politykę w klastrze egzekwuje jako admission webhook [OPA Gatekeeper](/katalog/gatekeeper). Uczciwa uwaga: Rego bywa stromy — opanowanie go to realnie kilkadziesiąt godzin nauki, a w 2026 r. ekosystem OPA przeszedł zmiany właścicielskie (część twórców i inżynierów Styra dołączyła do Apple). To nie podważa dojrzałości projektu, ale warto znać krajobraz alternatyw.

## Nie tylko OPA: krajobraz silników

„Policy-as-code" to kategoria, nie jedno narzędzie. Wybór zależy od tego, *co* egzekwujesz i *kto* ma pisać reguły.

| Silnik | Język / format | Najlepszy do | Cena wyboru |
|---|---|---|---|
| [OPA](/katalog/open-policy-agent) + [Conftest](/katalog/conftest) | Rego | Spójne reguły w całym stacku (K8s, IaC, API, CI) | Stroma krzywa Rego |
| [Kyverno](/katalog/kyverno) | YAML (zasoby K8s) | Governance Kubernetes bez nowego języka | Mocno związany z K8s |
| [Gatekeeper](/katalog/gatekeeper) | Rego (CRD) | Admission control w klastrze na bazie OPA | Tylko klaster |
| Cedar | [Cedar](https://www.cedarpolicy.com/) | Autoryzacja aplikacyjna (czytelna, szybka, analizowalna) | Węższy zakres, ekosystem AWS |
| [Checkov](/katalog/checkov) / [Terrascan](/katalog/terrascan) | wbudowane reguły | Skan IaC out-of-the-box | Mniej elastyczne niż własne reguły |
| [Casbin](/katalog/casbin) | modele (ACL/RBAC/ABAC) | Autoryzacja wewnątrz aplikacji | To biblioteka, nie bramka CI |

Dwie osie pomagają wybrać. Pierwsza: **uniwersalność vs prostota** — OPA daje jeden język na wszystko kosztem nauki Rego; Kyverno zostaje w YAML, ale tylko dla Kubernetes. Druga: **kto czyta regułę** — Cedar jest celowo czytelny i ściśle typowany (łatwiejszy do przejrzenia także dla nie-programisty oraz do automatycznej analizy), Rego bywa gęsty. W EIAC nie stawiamy na konkretne narzędzie, lecz na zasadę: reguła jest kodem, bramką i śladem — niezależnie od silnika.

## Warstwowa płaszczyzna kontroli

Najczęstszy błąd to potraktowanie PaC jak pojedynczego skanu w CI. Dojrzała platforma rozkłada polityki **warstwowo**, tak by każda bramka działała tam, gdzie ma najwięcej kontekstu i najmniej przeszkadza:

- **Źródło i projekt (IDE, PR)** — linty, szablony, walidacja konwencji; najtańsze i najszybsze sprzężenie zwrotne.
- **Build** — polityki nad Dockerfile i obrazami (brak `latest`, brak roota, dozwolone bazowe obrazy), skan zależności ([Trivy](/katalog/trivy)).
- **Plan / provisioning** — reguły nad planem [IaC](/katalog/checkov) (zanim cokolwiek powstanie: brak otwartych portów, wymagane szyfrowanie, tagi właściciela).
- **Deploy / runtime** — admission controller ([Gatekeeper](/katalog/gatekeeper)/[Kyverno](/katalog/kyverno)) odrzuca niezgodne zasoby przy `kubectl apply`/`helm`.

<figure>
<svg viewBox="0 0 780 200" role="img" aria-label="Warstwowa płaszczyzna kontroli policy-as-code wzdłuż cyklu wytwarzania: IDE/PR, Build, Plan IaC, Admission/runtime — w każdym etapie bramka polityki, im później tym wyższy koszt naprawy.">
  <line x1="24" y1="150" x2="756" y2="150" stroke="var(--color-muted)" stroke-width="1.5"/>
  <g font-family="'Space Grotesk', system-ui, sans-serif" fill="currentColor" font-size="13" text-anchor="middle">
    <text x="100" y="170">IDE / PR</text>
    <text x="300" y="170">Build</text>
    <text x="500" y="170">Plan IaC</text>
    <text x="690" y="170">Admission / runtime</text>
  </g>
  <g fill="none" stroke="currentColor" stroke-width="1.5">
    <rect x="44" y="92" width="112" height="40" rx="6"/>
    <rect x="244" y="92" width="112" height="40" rx="6"/>
    <rect x="444" y="92" width="112" height="40" rx="6"/>
    <rect x="634" y="92" width="112" height="40" rx="6"/>
  </g>
  <g font-family="'Space Mono', monospace" fill="var(--color-rust)" font-size="11" text-anchor="middle">
    <text x="100" y="116">lint / spec</text>
    <text x="300" y="116">obraz / SBOM</text>
    <text x="500" y="116">plan deny</text>
    <text x="690" y="116">webhook</text>
  </g>
  <g fill="var(--color-rust)">
    <circle cx="100" cy="150" r="4"/><circle cx="300" cy="150" r="4"/><circle cx="500" cy="150" r="4"/><circle cx="690" cy="150" r="4"/>
  </g>
  <path d="M44 60 H300" fill="none" stroke="var(--color-muted)" stroke-width="1.5"/>
  <path d="M300 60 l-7 -4 v8 z" fill="var(--color-muted)" transform="rotate(180 300 60)"/>
  <text x="60" y="52" font-family="'Space Grotesk', system-ui, sans-serif" fill="var(--color-muted)" font-size="12">taniej, szybciej naprawić ←</text>
  <text x="540" y="52" font-family="'Space Grotesk', system-ui, sans-serif" fill="var(--color-rust)" font-size="12">→ drożej, później</text>
</svg>
<figcaption>Policy-as-code jako płaszczyzna kontroli: ta sama zasada egzekwowana na kolejnych etapach. Im wcześniej bramka złapie naruszenie, tym taniej je naprawić.</figcaption>
</figure>

Kluczowe jest, by **ta sama intencja** była egzekwowana na wielu warstwach: „brak `latest`" sprawdzasz w CI (szybkie sprzężenie dla dewelopera) *i* w admission (twarda gwarancja, że nic nie obejdzie pipeline'u). Warstwy się nie wykluczają — uzupełniają.

## Guardrails, nie bramkarze

Najważniejsza zmiana myślenia: PaC ma być **guardrailem, nie bramkarzem**. Bramkarz blokuje i zmusza do proszenia o zgodę; guardrail wyznacza bezpieczny tor, po którym jedzie się samoobsługowo. Google Cloud opisuje [cztery mechanizmy kontroli platformy](https://cloud.google.com/blog/products/application-modernization/platform-engineering-control-mechanisms): złote ścieżki (domyślne, wspierane drogi), guardrails (automatyczne granice), siatki bezpieczeństwa (wykrywanie po fakcie) i ręczne punkty kontrolne (gdy naprawdę trzeba człowieka). PaC realizuje przede wszystkim guardrails — i to one skalują się najlepiej, bo „kodują osąd raz i stosują go nieustannie".

Z tego wynika praktyczna reguła doboru: **shift-left to nie tylko „wcześniej", ale „wcześniej i automatycznie"** — walidacja tam, gdzie inżynier już pracuje (IDE, PR, CI, deploy), ze sprzężeniem, które jest *szybkie, konkretne i naprawialne*. Polityka, która zwraca „odmowa" bez wskazania, co poprawić, jest bramkarzem. Polityka, która mówi „użyj `var(--space-2)` zamiast `12px`" albo „przypnij wersję obrazu" — jest guardrailem. Różnica decyduje o tym, czy zespoły platformę pokochają, czy zaczną ją obchodzić.

## Policy-as-code w platformie agentowej

Tu PaC przestaje być wygodą, a staje się warunkiem koniecznym. Gdy część pracy w pętli wykonuje agent AI, a nie człowiek, znika naturalny punkt kontroli, jakim był recenzent przy klawiaturze. Pętla narzędziowa agenta sama z siebie **nie ma punktu decyzyjnego** „czy wolno?" — i to policy-as-code go dostarcza: deklaratywną bramkę, przez którą musi przejść każde działanie, niezależnie od tego, czy zainicjował je człowiek, czy model.

To jest dokładnie rdzeń [deterministycznego szkieletu ADP](/blog/deterministyczny-szkielet-adp): nie ufamy modelowi „że zrobi dobrze", tylko zamykamy go w polityce egzekwowanej maszynowo. Im wyżej w [czterech poziomach autonomii](/blog/cztery-poziomy-agentowego-wytwarzania), tym mocniejsze muszą być te bramki — bo tym mniej człowieka zostaje w pętli. PaC łączy się tu wprost z [Security Plane](/blog/security-plane-sekrety-tozsamosc-policy): tożsamość mówi *kto/co* działa (także tożsamość nie-ludzka agenta), a polityka — *co temu komuś wolno*.

```yaml
# Kyverno: w klastrze nic nie wystartuje jako root — dotyczy tak samo
# wdrożeń człowieka, jak i tych zainicjowanych przez agenta
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata: { name: require-non-root }
spec:
  validationFailureAction: Enforce
  rules:
    - name: check-runAsNonRoot
      match: { any: [{ resources: { kinds: ["Pod"] } }] }
      validate:
        message: "kontener musi działać jako non-root"
        pattern:
          spec:
            securityContext: { runAsNonRoot: true }
```

## Zgodność jako produkt uboczny

Ponieważ każde uruchomienie polityki zostawia ślad — co sprawdzono, z jakim wynikiem — PaC daje *za darmo* to, co regulacje wymagają osobno: dowód. Zamiast ręcznych audytów raz na kwartał masz ciągły, maszynowy zapis zgodności. To wprost odpowiada na wymóg [DORA](/blog/dora-w-praktyce-platformy), by zgodność dało się *wykazać*, oraz na oczekiwania [AI Act](/blog/ai-act-a-agentowy-sdlc) co do śladu i nadzoru. „Compliance by design" nie jest hasłem — to konsekwencja tego, że reguła jest kodem, który się wykonuje i loguje.

Granica uczciwości: PaC pokrywa *techniczną* część zgodności. Procesy organizacyjne — rejestr ryzyka, raportowanie incydentów, role i odpowiedzialności — to wciąż praca ludzi. Platforma jest warunkiem koniecznym zgodności, nie wystarczającym.

## Wybierz ścieżkę

Nie ma jednej słusznej drogi wdrożenia — jest sekwencja, którą warto przejść etapami:

1. **Zacznij od jednej bramki w CI** — np. [Conftest](/katalog/conftest) lub [Checkov](/katalog/checkov) na PR. Niski koszt, szybka wartość, uczy zespół myślenia regułami.
2. **Dołóż admission w klastrze** — [Kyverno](/katalog/kyverno) (gdy zespół woli YAML) lub [Gatekeeper](/katalog/gatekeeper) (gdy już używasz Rego). Domyka lukę „co z tym, co omija CI".
3. **Ujednolić język, jeśli rośnie zakres** — gdy reguły mnożą się w wielu miejscach (K8s, IaC, API), rozważ [OPA](/katalog/open-policy-agent)/Rego jako wspólny silnik; do czystej autoryzacji aplikacyjnej — Cedar lub [Casbin](/katalog/casbin).
4. **Traktuj polityki jak produkt** — wersjonuj, testuj, dawaj czytelne komunikaty naprawcze. Guardrail bez dobrego UX zamienia się w bramkarza, którego zespoły obchodzą.

Reguła kciuka: **najpierw jedna warstwa, potem szerokość, na końcu unifikacja języka.** Budowanie od razu uniwersalnej platformy polityk dla problemu, którego jeszcze nie masz, to ten sam over-engineering, przed którym ostrzegaliśmy przy [samej platformie](/blog/platform-engineering-normalizacja-pracy).

## Podsumowanie

Policy-as-code zamienia zasady bezpieczeństwa i zgodności z dokumentów, które nikt nie egzekwuje, w testowalne bramki, które działają same. Jego dojrzała forma to nie skan w CI, lecz warstwowa płaszczyzna kontroli — od IDE po admission — egzekwowana jako guardrail blisko miejsca pracy inżyniera, a nie jako bramkarz na końcu. W platformie agentowej PaC jest tym punktem decyzyjnym, którego pętla agenta sama nie ma, a przy okazji dostarcza ciągły ślad audytowy spełniający wymogi DORA i AI Act. Wybór silnika należy do Ciebie — OPA, Kyverno, Cedar czy inny — bo w EIAC liczy się zasada, nie narzędzie: reguła jest kodem, bramką i śladem. A od strony praktycznej: zacznij od jednej reguły w CI. Zobaczysz, ile spokoju daje świadomość, że standard egzekwuje się sam, a nie zależy od tego, czy ktoś go akurat pamiętał na review.