Admission vs audit¶
Portal evaluates rules in three loops: admission, audit, and network. The mode: field on a rule ([]api.Mode, see internal/api/rule.go) opts the rule into one or more.
When admission fires¶
- Driven by
ValidatingWebhookConfiguration— synchronous, pre-persistence. - Has the inbound object in
request.objectand the prior state (UPDATE) inrequest.oldObject. - Exposes
request.{operation,dryRun,userInfo}in the expr-lang env (see ../reference/expression-language.md). enforcementActionis honoured:denyrejects the request,warnadmits with akubectlwarning,dryrunadmits silently but still records to sinks.- Code path:
internal/admission/handler.go.
When audit fires¶
- Driven by
client-goinformers; events arrive on already-persisted objects (OnAdd/OnUpdate/OnDelete). - Asynchronous.
request.*is not bound — onlyobject,metadata, and pod-sugar keys are present. enforcementActionis ignored. Audit always feeds the violation to sinks and the action dispatcher.- Code path:
internal/audit/controller.go.
Picking modes¶
mode: [admission] # block at the door; nothing to audit
mode: [audit] # observe-only; pair with deny+warn rule later
mode: [admission, audit] # block new, surface existing
Audit is the safety net: a typo'd field or a missing sugar key in an admission rule still gives audit a chance to surface the problem on the next informer event. For high-confidence policies, run both — admission blocks the bleed-in, audit catches what landed before the rule was installed.
Ordering of events¶
- A single
kubectl applycan produce: an admission decision then, if admitted, an auditOnAdd. The two are independent invocations of the rule engine with different contexts. - For an
UPDATE, admission sees bothrequest.objectandrequest.oldObject; audit sees only the new state. - Audit does not re-fire on a watch reconnect alone — only on object changes (and the configurable resync interval, default 10 min, used as a safety net).