ЁЯПл The SchoolтА║ЁЯЫдя╕П Platform EngineeringтА║ЁЯЫбя╕П рдзрдбрд╛ 09 тАФ Policy as code рдЖрдгрд┐ guardrails: рджреБрд░реБрд╕реНрддреА рд╕рд╛рдВрдЧрдгрд╛рд░реЗ рдирд┐рдпрдо
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯЫбя╕П рдзрдбрд╛ 09 тАФ Policy as code рдЖрдгрд┐ guardrails: рджреБрд░реБрд╕реНрддреА рд╕рд╛рдВрдЧрдгрд╛рд░реЗ рдирд┐рдпрдо

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 09 ┬╖ рдорд╛рдЧреАрд▓: lesson-08-secrets-config ┬╖ рдкреБрдвреАрд▓: lesson-10-scorecards


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбреЗ 01тАУ08, рдЖрдгрд┐ policy as code: рд╢рд╛рд│реЗрдЪреЗ рд╕реБрд░рдХреНрд╖рд╛ рдирд┐рдпрдо code рдореНрд╣рдгреВрди рд▓рд┐рд╣рд┐рд▓реЗрд▓реЗ, рдЬреЗ рдкреНрд░рддреНрдпреЗрдХ deployment рддрдкрд╛рд╕рддрд╛рдд. рдкреНрд░рддреНрдпреЗрдХ рдирд┐рдпрдо рдХрд╛рдп рдЪреБрдХрд▓реЗ рдЖрдгрд┐ рддреЗ рдХрд╕реЗ рджреБрд░реБрд╕реНрдд рдХрд░рд╛рдпрдЪреЗ рд╣реЗ рдкрд░рдд рд╕рд╛рдВрдЧрддреЛ. рддреЗрдЪ рдирд┐рдпрдо audit mode (рдкрд░рд╡рд╛рдирдЧреА, рдкрдг рдЗрд╢рд╛рд░рд╛) рдХрд┐рдВрд╡рд╛ enforce mode (рдирдХрд╛рд░) рдордзреНрдпреЗ рдЪрд╛рд▓рддрд╛рдд. RULES рдЖрдгрд┐ evaluate() idp/guard.py рдордзреНрдпреЗ, policy() idp/demo.py рдордзреНрдпреЗ.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рд╢рд╛рд│реЗрдд рд╡рд░реНрдЧрдЦреЛрд▓реНрдпрд╛рдВрд╕рд╛рдареА рд╕реБрд░рдХреНрд╖рд╛ рдирд┐рдпрдо рдЖрд╣реЗрдд: рдкреНрд░рддреНрдпреЗрдХ рдЦреЛрд▓реАрд▓рд╛ рдирд╛рд╡ рдЕрд╕рд▓реЗрд▓рд╛ рдорд╛рд▓рдХ рдЕрд╕рддреЛ, fire exits рдЕрдбрд╡рд▓реЗрд▓реЗ рдирд╕рддрд╛рдд, рдореЛрдареНрдпрд╛ рд╣реЙрд▓рдордзреНрдпреЗ рдХрд┐рдорд╛рди рджреЛрди рдмрд╛рд╣реЗрд░ рдкрдбрд╛рдпрдЪреЗ рджрд░рд╡рд╛рдЬреЗ рдЕрд╕рддрд╛рдд, рд░рд╕рд╛рдпрдирд╛рдВрдЪреЗ рдХрдкрд╛рдЯ рдХреБрд▓реВрдкрдмрдВрдж рдЕрд╕рддреЗ.

рдЬреБрдиреА рдкрджреНрдзрдд: рдПрдХ рдирд┐рд░реАрдХреНрд╖рдХ рдпреЗрддреЛ, "рдирд╛рдкрд╛рд╕" рдореНрд╣рдгрддреЛ рдЖрдгрд┐ рдирд┐рдШреВрди рдЬрд╛рддреЛ. ЁЯЪл рд╢рд┐рдХреНрд╖рд┐рдХреЗрд▓рд╛ рдХрд╛рдп рдмрджрд▓рд╛рдпрдЪреЗ рддреЗ рдХрд│рдд рдирд╛рд╣реА, рдореНрд╣рдгреВрди рдкреБрдврдЪреНрдпрд╛ рднреЗрдЯреАрдЖрдзреА рддреА рдЕрдбрдЪрдг рд▓рдкрд╡рддреЗ.

рд╕реБрд╡рд┐рдзрд╛ рдХрд╛рд░реНрдпрд╛рд▓рдп рд╣реЗ рд╡реЗрдЧрд│реНрдпрд╛ рдкрджреНрдзрддреАрдиреЗ рдХрд░рддреЗ. рдирд┐рд░реАрдХреНрд╖рдХрд╛рдЪреА рддрдкрд╛рд╕рдгреА-рдпрд╛рджреА рд▓рд┐рд╣реВрди рдареЗрд╡рд▓реЗрд▓реА рдЕрд╕рддреЗ рдЖрдгрд┐ рдЦреЛрд▓реАрдд рдкреНрд░рддреНрдпреЗрдХ рдмрджрд▓ рдЭрд╛рд▓рд╛ рдХреА рддреА рдЖрдкреЛрдЖрдк рдЪрд╛рд▓рддреЗ. рдкреНрд░рддреНрдпреЗрдХ рдЕрдбрдЪрдгреАрд╕рд╛рдареА рддреА рдХрд╛рдп рдЖрдгрд┐ рдХрд╕реЗ рд╕рд╛рдВрдЧрддреЗ: "рджрд╛рд░рд╛рд╡рд░ рдорд╛рд▓рдХрд╛рдЪреА рдкрд╛рдЯреА рдирд╛рд╣реА тЖТ рдЗрдереЗ рддреБрдордЪреНрдпрд╛ рд╡рд┐рднрд╛рдЧрд╛рдЪреЗ рдирд╛рд╡ рд▓рд╛рд╡рд╛ тАФ рдпреЛрдЧреНрдп рд╡реНрдпрдХреНрддреАрд▓рд╛ рдмреЛрд▓рд╛рд╡рдгреНрдпрд╛рд╕рд╛рдареА рдХрд╛рд░реНрдпрд╛рд▓рдп рддреЗ рд╡рд╛рдкрд░рддреЗ." ЁЯЫбя╕П

рдкрд╣рд┐рд▓реНрдпрд╛ рдорд╣рд┐рдиреНрдпрд╛рдд рддрдкрд╛рд╕рдгреА-рдпрд╛рджреА рдлрдХреНрдд рдЗрд╢рд╛рд░рд╛ рджреЗрддреЗ (audit mode): рдЦреЛрд▓реНрдпрд╛ рдЙрдШрдбреНрдпрд╛ рд░рд╛рд╣рддрд╛рдд, рд╢рд┐рдХреНрд╖рд┐рдХрд╛рдВрдирд╛ рдпрд╛рджреА рдорд┐рд│рддреЗ. рдмрд╣реБрддреЗрдХ рдЦреЛрд▓реНрдпрд╛ рдкрд╛рд╕ рдЭрд╛рд▓реНрдпрд╛ рдХреА рдХрд╛рд░реНрдпрд╛рд▓рдп enforce рд╡рд░ рдЬрд╛рддреЗ: рдЕрдбрдЪрдг рдЕрд╕рд▓реЗрд▓реА рдЦреЛрд▓реА рддреА рджреБрд░реБрд╕реНрдд рд╣реЛрдИрдкрд░реНрдпрдВрдд рдЙрдШрдбрдд рдирд╛рд╣реА. рдХрддрд░рд┐рдирд╛рдЪреНрдпрд╛ рдкрд░реАрдХреНрд╖рд╛-рдЦреЛрд▓реАрдд 4 рдЕрдбрдЪрдгреА рд╣реЛрддреНрдпрд╛. рддрд┐рдиреЗ рдлрдХреНрдд messages рд╡рд╛рдЪреВрди рдЪрд╛рд░рд╣реА рджреБрд░реБрд╕реНрдд рдХреЗрд▓реНрдпрд╛, рдЖрдгрд┐ рдЦреЛрд▓реА рдЙрдШрдбрд▓реА.

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart LR
    m["ЁЯУД exam-results manifest"] --> e{"ЁЯЫбя╕П 5 rules"}
    e -->|"audit"| w["тЪая╕П allowed with warnings<br/>4 findings"]
    e -->|"enforce"| d["тЫФ denied<br/>4 findings"]
    d --> f["ЁЯТм each finding:<br/>what is wrong тЖТ fix"]
    f --> m2["ЁЯУД fixed manifest"] --> ok["тЬЕ allowed"]

ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/platform-engineering/lesson-diagrams.html#l09

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг self-service (рднрд╛рдЧ 2) рдиреЗ рдирд┐рд░реНрдгрдп рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рдХрдбреВрди teams рдХрдбреЗ рдиреЗрд▓реЗ. рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рдиреЗ рдкреНрд░рддреНрдпреЗрдХ рдмрджрд▓ рд╣рд╛рддрд╛рдиреЗ review рди рдХрд░рддрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреНрдпрд╛ рдЧреЛрд╖реНрдЯреА рдЦрд▒реНрдпрд╛ рд░рд╛рд╣рддреАрд▓ рдпрд╛рдЪреА рдЦрд╛рддреНрд░реА рдирд┐рдпрдо рдХрд░рддрд╛рдд: рдкреНрд░рддреНрдпреЗрдХ service рд▓рд╛ owner рдЖрд╣реЗ, images pinned рдЖрд╣реЗрдд, production рдордзреНрдпреЗ рджреЛрди replicas рдЖрд╣реЗрдд, buckets private рдЖрд╣реЗрдд. рдЖрдгрд┐ рдХрд╛рд░рдг рдлрдХреНрдд "рдирд╛рд╣реА" рдореНрд╣рдгрдгрд╛рд░рд╛ рдирд┐рдпрдо рдХрд╛рд╣реАрдЪ рд╢рд┐рдХрд╡рдд рдирд╛рд╣реА тАФ рдордЧ рд▓реЛрдХ рдорд╛рдЧрдЪреНрдпрд╛ рд╡реЗрд│реА рдЬреЗ рдкрд╛рд╕ рдЭрд╛рд▓реЗ рддреНрдпрд╛рдЪреА copy рдХрд░рддрд╛рдд, рдХрд┐рдВрд╡рд╛ рдорд╛рдЧрдЪрд╛ рджрд░рд╡рд╛рдЬрд╛ рд╢реЛрдзрддрд╛рдд. рджреБрд░реБрд╕реНрддреА рд╕рдордЬрд╛рд╡рдгрд╛рд░рд╛ рдирд┐рдпрдо рд╕реБрд░рдХреНрд╖рд┐рдд рдорд╛рд░реНрдЧрд╛рд▓рд╛рдЪ рд╕реЛрдкрд╛ рдорд╛рд░реНрдЧ рдмрдирд╡рддреЛ, рдЖрдгрд┐ рд╕реЛрдиреЗрд░реА рд╡рд╛рдЯреЗрдЪреА рд╕рдВрдкреВрд░реНрдг рдХрд▓реНрдкрдирд╛ рд╣реАрдЪ рдЖрд╣реЗ.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

idp/guard.py рдордзреАрд▓ RULES рд╣реА (id, test, message, fix) рдЪреА рдпрд╛рджреА рдЖрд╣реЗ; manifest рдареАрдХ рдЕрд╕реЗрд▓ рддрд░ test(manifest) True рдкрд░рдд рджреЗрддреЛ. рдкрд╛рдЪ рдирд┐рдпрдо: owner-label, no-latest-tag, limits-set, two-replicas-in-prod (рдлрдХреНрдд env production рдЕрд╕реЗрд▓ рддреЗрд╡реНрд╣рд╛рдЪ рддрдкрд╛рд╕рд▓рд╛ рдЬрд╛рддреЛ) рдЖрдгрд┐ no-public-bucket. evaluate(manifest, mode, rules) рдХрд╛рд╣реАрдЪ рдирд╛рдкрд╛рд╕ рдирд╕реЗрд▓ рддрд░ allowed рдкрд░рдд рджреЗрддреЛ; рдирд╛рд╣реАрддрд░ audit mode рдордзреНрдпреЗ allowed with warnings рдХрд┐рдВрд╡рд╛ enforce mode рдордзреНрдпреЗ denied, рдЖрдгрд┐ рд╕реЛрдмрдд рдкреНрд░рддреНрдпреЗрдХ finding рддрд┐рдЪреНрдпрд╛ рджреБрд░реБрд╕реНрддреАрд╕рд╣. рддреБрдореНрд╣реА рддреБрдордЪреА рд╕реНрд╡рддрдГрдЪреА рдирд┐рдпрдорд╛рдВрдЪреА рдпрд╛рджреАрд╣реА рджреЗрдК рд╢рдХрддрд╛.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 idp/demo.py policy
python3 - <<'EOF'
import sys; sys.path.insert(0, "idp"); from guard import RULES, evaluate
m = dict(name="timetable", labels={"owner": "maths"}, image="timetable", limits={"cpu": "250m", "memory": "128Mi"},
         env="staging", replicas=1, public_bucket=True)
decision, findings = evaluate(m, "enforce")
print(decision, [rid for rid, _, _ in findings])
m["env"] = "production"; print("in production:", [rid for rid, _, _ in evaluate(m)[1]])
rules = RULES + [("has-probe", lambda m: "probe" in m, "no health probe", "add probe: /healthz (the template ships one)")]
m.update(image="timetable:2.0.1", replicas=2, public_bucket=False)
print("with a sixth rule:", evaluate(m, "enforce", rules)[0], "┬╖", evaluate(m, "enforce", rules)[1][0][2])
m["probe"] = "/healthz"; print("fixed:", evaluate(m, "enforce", rules))
EOF

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

policy рд╣реЗ рдЫрд╛рдкрддреЗ:

тФАтФА audit mode: exam-results тЖТ allowed with warnings (4 findings)
тФАтФА enforce mode: exam-results тЖТ denied (4 findings)
   тЬЧ owner-label           no owner label тЖТ fix: add labels.owner: <your group> (the catalog uses it to page the right people)
   тЬЧ two-replicas-in-prod  1 replica in production тЖТ fix: set replicas: 2 or more so one restart is not an outage
тФАтФА after the four fixes тЖТ allowed

рддреБрдордЪрд╛ snippet рд╣реЗ рдЫрд╛рдкрддреЛ:

denied ['no-latest-tag', 'no-public-bucket']
in production: ['no-latest-tag', 'two-replicas-in-prod', 'no-public-bucket']
with a sixth rule: denied ┬╖ add probe: /healthz (the template ships one)
fixed: ('allowed', [])

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рддреЛрдЪ manifest audit mode рдордзреНрдпреЗ allowed рдЖрдгрд┐ enforce mode рдордзреНрдпреЗ denied рдЭрд╛рд▓рд╛ тАФ рдирд┐рдпрдо рдмрджрд▓рд▓реЗ рдирд╛рд╣реАрдд, рдлрдХреНрдд рдирд╛рдкрд╛рд╕ рдЭрд╛рд▓реНрдпрд╛рд╡рд░ рдХрд╛рдп рд╣реЛрддреЗ рддреЗ рдмрджрд▓рд▓реЗ. рдЕрдЬрд┐рдмрд╛рдд tag рдирд╕рд▓реЗрд▓реА image :latest рд╕рд╛рд░рдЦреНрдпрд╛рдЪ рдирд┐рдпрдорд╛рдд рдирд╛рдкрд╛рд╕ рдЭрд╛рд▓реА. Manifest staging рд╡рд░реВрди production рд╡рд░ рдиреЗрд▓реНрдпрд╛рд╡рд░ рдПрдХ finding рд╡рд╛рдврд▓реА (staging рд╕рд╛рдареА рдПрдХ replica рдЪрд╛рд▓рддреЗ, production рд╕рд╛рдареА рдирд╛рд╣реА): рдирд┐рдпрдо рд╕рдВрджрд░реНрднрд╛рд╡рд░ рдЕрд╡рд▓рдВрдмреВрди рдЕрд╕реВ рд╢рдХрддрд╛рдд. рд╕рд╣рд╛рд╡рд╛ рдирд┐рдпрдо рдЬреЛрдбрдгреЗ рдореНрд╣рдгрдЬреЗ рдПрдХ рдУрд│, рдЖрдгрд┐ рддреНрдпрд╛рдЪреА рджреБрд░реБрд╕реНрддреА рдереЗрдЯ рдзрдбрд╛ 03 рдордзрд▓реНрдпрд╛ рд╕реЛрдиреЗрд░реА рд╡рд╛рдЯреЗрдХрдбреЗ рдмреЛрдЯ рджрд╛рдЦрд╡рдд рд╣реЛрддреА ("the template ships one"). Template рдордзреВрди рдмрдирд▓реЗрд▓реА service рдкрд╣рд┐рд▓реНрдпрд╛рдЪ рджрд┐рд╡рд╢реА рдмрд╣реБрддреЗрдХ рдирд┐рдпрдо рдкрд╛рд╕ рдХрд░рддреЗ тАФ guardrails рдЖрдгрд┐ golden paths рдЬреЛрдбреАрдиреЗ рдХрд╛рдо рдХрд░рддрд╛рдд.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд

Kyverno, рдЦрд▒реНрдпрд╛ cluster рд╡рд░ тАФ рджреБрд░реБрд╕реНрддреА рдХрд╢реА рдХрд░рд╛рдпрдЪреА рд╣реЗ рд╕рд╛рдВрдЧрдгрд╛рд░рд╛ message рдЕрд╕рд▓реЗрд▓рд╛ рдирд┐рдпрдо. Kyverno 1.13+ рдкреНрд░рддреНрдпреЗрдХ рдирд┐рдпрдорд╛рд╕рд╛рдареА action failureAction рдиреЗ рдард░рд╡рддреЛ; рдЬреБрдиреНрдпрд╛ releases рдордзреНрдпреЗ spec.validationFailureAction рд╡рд╛рдкрд░рдд рдЕрд╕рдд:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-owner-label
spec:
  rules:
    - name: owner-label
      match:
        any:
          - resources:
              kinds: [Deployment]
      validate:
        failureAction: Audit
        message: "Add metadata.labels.owner: <your group> тАФ the catalog uses it to page the right people."
        pattern:
          metadata:
            labels:
              owner: "?*"

OPA Gatekeeper рдирд┐рдпрдо (Rego рдордзреАрд▓ ConstraintTemplate) рдЖрдгрд┐ рддреЛ рдХреБрдареЗ рд╡ рдХрд╕рд╛ рд▓рд╛рдЧреВ рд╣реЛрддреЛ (рдПрдХ constraint) рд╣реЗ рд╡реЗрдЧрд│реЗ рдареЗрд╡рддреЛ. dry-run рдкрд╛рд╕реВрди рд╕реБрд░реБрд╡рд╛рдд:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels            # from the Gatekeeper policy library
metadata:
  name: must-have-owner
spec:
  enforcementAction: dryrun        # later: warn, then deny
  match:
    kinds:
      - apiGroups: ["apps"]
        kinds: ["Deployment"]
  parameters:
    labels:
      - key: owner

рддреЗрдЪ Rego рдХрд╛рд╣реАрд╣реА deploy рд╣реЛрдгреНрдпрд╛рдЖрдзреА CI рдордзреНрдпреЗ files рддрдкрд╛рд╕реВ рд╢рдХрддреЗ:

conftest test deploy/values.yaml --policy policy/

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: enforce рдХрд░рдгреНрдпрд╛рдЖрдзреА рдХрд╛рд╣реА рдЖрдард╡рдбреЗ audit рдЪреЗ рдирд┐рдХрд╛рд▓ рдкрд╛рд╣рд╛. рдкреНрд░рддреНрдпреЗрдХ рдирд┐рдпрдорд╛рдЪреНрдпрд╛ findings рдЪреА рд╕рдВрдЦреНрдпрд╛ рд╕рд╛рдВрдЧрддреЗ рдХреА рдХреЛрдгрддреНрдпрд╛ рдирд┐рдпрдорд╛рд▓рд╛ рдЪрд╛рдВрдЧрд▓рд╛ template default рд╣рд╡рд╛, рдЖрдгрд┐ рдХреЛрдгрддреНрдпрд╛рд▓рд╛ рдЪрд╛рдВрдЧрд▓рд╛ message.

тПня╕П рдкреБрдвреЗ

Policies рд╕рд╛рдВрдЧрддрд╛рдд рдХреА рдХрд╛рдп рдЦрд░реЗ рдЕрд╕рд▓реЗрдЪ рдкрд╛рд╣рд┐рдЬреЗ. рдкреБрдвреЗ: рдПрдХ scorecard рдЬреЛ рджрд╛рдЦрд╡рддреЛ рдХреА рдкреНрд░рддреНрдпреЗрдХ service рдХрд╛рдп рдЦрд░реЗ рдЕрд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ рддреНрдпрд╛рдкрд╛рд╕реВрди рдХрд┐рддреА рджреВрд░ рдЖрд╣реЗ тАФ рдЖрдгрд┐ рдкреБрдвреЗ рдХрд╛рдп рдХрд░рд╛рдпрдЪреЗ.

git checkout lesson-10-scorecards

ЁЯЫбя╕П Lesson 09 тАФ Policy as code & guardrails: rules that say how to fix

ЁЯУН You are here: Lesson 09 of 12 ┬╖ Previous: lesson-08-secrets-config ┬╖ Next: lesson-10-scorecards


ЁЯУж What's in this branch

Lessons 01тАУ08, plus policy as code: the school's safety rules written as code that checks every deployment. Each rule returns what is wrong and how to fix it. The same rules run in audit mode (allow, but warn) or enforce mode (deny). RULES and evaluate() in idp/guard.py, policy() in idp/demo.py.

ЁЯзТ Explain like I'm 5

The school has safety rules for classrooms: every room has a named owner, fire exits are not blocked, there are at least two exits in the big hall, the chemical cupboard is locked.

The old way: an inspector walks in, says "FAILED", and leaves. ЁЯЪл The teacher does not know what to change, so she hides the problem before the next visit.

The facilities office does it differently. The inspector's checklist is written down and runs automatically every time a room changes. For every problem it says what and how: "No owner sign on the door тЖТ put your department's name here тАФ the office uses it to call the right person." ЁЯЫбя╕П

For the first month, the checklist only warns (audit mode): rooms stay open, teachers get the list. When most rooms pass, the office switches to enforce: a room with a problem does not open until it is fixed. Katrina's exam room had 4 problems. She fixed all 4 from the messages alone, and it opened.

ЁЯЧ║я╕П Diagram

flowchart LR
    m["ЁЯУД exam-results manifest"] --> e{"ЁЯЫбя╕П 5 rules"}
    e -->|"audit"| w["тЪая╕П allowed with warnings<br/>4 findings"]
    e -->|"enforce"| d["тЫФ denied<br/>4 findings"]
    d --> f["ЁЯТм each finding:<br/>what is wrong тЖТ fix"]
    f --> m2["ЁЯУД fixed manifest"] --> ok["тЬЕ allowed"]

ЁЯЧ║я╕П Drawn version + a lab: https://school-edh.pages.dev/platform-engineering/lesson-diagrams.html#l09

тЭУ What

ЁЯдФ Why

Because self-service (Part 2) moved decisions from the office to the teams. Rules make sure the important things stay true without the office reviewing every change by hand: every service has an owner, images are pinned, production has two replicas, buckets are private. And because a rule that only says "no" teaches nothing тАФ people then copy whatever passed last time, or find the side door. A rule that explains the fix makes the safe way the easy way, which is the whole idea of a golden path.

ЁЯФз How (in this repo)

RULES in idp/guard.py is a list of (id, test, message, fix); test(manifest) returns True when the manifest is fine. The five rules: owner-label, no-latest-tag, limits-set, two-replicas-in-prod (only checked when env is production) and no-public-bucket. evaluate(manifest, mode, rules) returns allowed when nothing fails; otherwise allowed with warnings in audit mode or denied in enforce mode, plus every finding with its fix. You can pass your own list of rules.

ЁЯзк Try it

python3 idp/demo.py policy
python3 - <<'EOF'
import sys; sys.path.insert(0, "idp"); from guard import RULES, evaluate
m = dict(name="timetable", labels={"owner": "maths"}, image="timetable", limits={"cpu": "250m", "memory": "128Mi"},
         env="staging", replicas=1, public_bucket=True)
decision, findings = evaluate(m, "enforce")
print(decision, [rid for rid, _, _ in findings])
m["env"] = "production"; print("in production:", [rid for rid, _, _ in evaluate(m)[1]])
rules = RULES + [("has-probe", lambda m: "probe" in m, "no health probe", "add probe: /healthz (the template ships one)")]
m.update(image="timetable:2.0.1", replicas=2, public_bucket=False)
print("with a sixth rule:", evaluate(m, "enforce", rules)[0], "┬╖", evaluate(m, "enforce", rules)[1][0][2])
m["probe"] = "/healthz"; print("fixed:", evaluate(m, "enforce", rules))
EOF

тЬЕ Verify тАФ what you should see

policy prints:

тФАтФА audit mode: exam-results тЖТ allowed with warnings (4 findings)
тФАтФА enforce mode: exam-results тЖТ denied (4 findings)
   тЬЧ owner-label           no owner label тЖТ fix: add labels.owner: <your group> (the catalog uses it to page the right people)
   тЬЧ two-replicas-in-prod  1 replica in production тЖТ fix: set replicas: 2 or more so one restart is not an outage
тФАтФА after the four fixes тЖТ allowed

Your snippet prints:

denied ['no-latest-tag', 'no-public-bucket']
in production: ['no-latest-tag', 'two-replicas-in-prod', 'no-public-bucket']
with a sixth rule: denied ┬╖ add probe: /healthz (the template ships one)
fixed: ('allowed', [])

ЁЯПБ What you just proved

The same manifest was allowed in audit mode and denied in enforce mode тАФ the rules did not change, only what happens on failure. An image with no tag at all failed the same rule as :latest. Moving the manifest from staging to production added one finding (one replica is fine for staging, not for production): rules can depend on context. Adding a sixth rule was one line, and its fix pointed straight back at the golden path from lesson 03 ("the template ships one"). A service made from the template passes most rules on day one тАФ guardrails and golden paths work as a pair.

тЪая╕П Common mistakes

ЁЯПн In production

Kyverno, on a real cluster тАФ a rule with a message that says how to fix it. Kyverno 1.13+ sets the action per rule with failureAction; older releases used spec.validationFailureAction:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-owner-label
spec:
  rules:
    - name: owner-label
      match:
        any:
          - resources:
              kinds: [Deployment]
      validate:
        failureAction: Audit
        message: "Add metadata.labels.owner: <your group> тАФ the catalog uses it to page the right people."
        pattern:
          metadata:
            labels:
              owner: "?*"

OPA Gatekeeper separates the rule (a ConstraintTemplate in Rego) from where and how it applies (a constraint). Starting in dry-run:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels            # from the Gatekeeper policy library
metadata:
  name: must-have-owner
spec:
  enforcementAction: dryrun        # later: warn, then deny
  match:
    kinds:
      - apiGroups: ["apps"]
        kinds: ["Deployment"]
  parameters:
    labels:
      - key: owner

The same Rego can check files in CI before anything is deployed:

conftest test deploy/values.yaml --policy policy/

ЁЯПн Why this matters in production: watch the audit results for a few weeks before enforcing. The number of findings per rule tells you which rule needs a better template default, and which needs a better message.

тПня╕П Next

Policies say what must be true. Next: a scorecard that shows how far each service is from what should be true тАФ and what to do next.

git checkout lesson-10-scorecards
тЖР Previoussecrets configNext тЖТscorecards

This page is the lesson's README from the lesson-09-policy-guardrails branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.