Policies
One cluster-scoped resource decides what may run. The console edits it live; kubectl apply edits it from a repository. They are the same object, so GitOps and the UI never fight over a second source of truth.
Where policy lives
Policy is an ImageSecurityPolicy, a cluster-scoped custom resource in the
attestkeep.com API group. The chart installs one named default. Look at it
the way you would look at anything else in the cluster:
kubectl get imagesecuritypolicies
kubectl describe imagesecuritypolicy default
kubectl get imagesecuritypolicy default -o yaml
A policy edit takes effect on the next admission decision. There is no redeploy, no restart, and nothing to reload.
How a namespace gets its policy
Two rules, checked in order:
- A policy whose
spec.targetNamespaceslists the namespace wins. - Otherwise the policy with no
targetNamespacesapplies — that is the global default.
So the usual arrangement is one global policy, plus a stricter one naming the namespaces that have earned it. A namespace listed nowhere, in a cluster whose only policies are all targeted, is not covered — keep one global policy unless you mean that.
Example: the shipped default, annotated
This is the policy the chart installs, with the reasoning attached. It admits workloads whose images are not yet scanned, queues the scan, and refuses once findings cross the thresholds — the adoption-friendly posture.
apiVersion: attestkeep.com/v1alpha1
kind: ImageSecurityPolicy
metadata:
name: default
spec:
# Enforce refuses; Audit only records what it would have refused.
enforcement: Enforce
# AllowAndScan admits an unknown digest and queues the scan.
# DenyUntilScanned refuses anything without a scan record.
admissionTiming: AllowAndScan
# A mutable tag can change what runs without anyone deploying.
blockLatestTag: true
# AllowTags | PreferDigest | Required — whether images must be pinned
# by digest. Required is the strongest claim and the strictest rollout.
digestEnforcement: AllowTags
thresholds:
blockCritical: true # any critical finding refuses the image
blockHigh: true # any high finding refuses the image
maxMedium: 0 # 0 means unlimited here; set a ceiling to refuse past it
maxLow: 0
# How long a finding may stay open before the report calls it overdue.
slaWindows:
critical: 24h
high: 168h
medium: 720h
low: 2160h
# Supply-chain requirements, all off by default: turn them on once your
# build pipeline actually produces signatures and attestations.
requireSignature: false
requireSBOM: false
requireVulnAttestation: false
attestationMaxAge: 168h
# Accepted-risk decisions: how long a triage may stand, and whether it
# must say why.
triageDefaults:
maxDuration: 2160h
requireJustification: true
kubectl apply -f policy-default.yaml
Example: audit-only adoption
The first week on an existing cluster. Nothing is refused; every decision that would have refused is recorded, so you learn what enforcement will cost before paying it.
apiVersion: attestkeep.com/v1alpha1
kind: ImageSecurityPolicy
metadata:
name: default
spec:
enforcement: Audit
admissionTiming: AllowAndScan
blockLatestTag: true
digestEnforcement: AllowTags
thresholds:
blockCritical: true
blockHigh: true
Example: a production namespace that has earned strictness
The global default stays adoption-friendly; prod gets the hard gate. Unknown
images are refused until scanned, images must be pinned by digest, must come from your
registry, must be signed, and workload hardening findings refuse rather than warn.
apiVersion: attestkeep.com/v1alpha1
kind: ImageSecurityPolicy
metadata:
name: prod-hard-gate
spec:
targetNamespaces: ["prod", "payments"]
enforcement: Enforce
admissionTiming: DenyUntilScanned
blockLatestTag: true
digestEnforcement: Required
# Anything not from these hosts is refused outright.
allowedRegistries:
- registry.example.com
- ghcr.io/your-org
thresholds:
blockCritical: true
blockHigh: true
requireSignature: true
requireSBOM: true
requireVulnAttestation: true
attestationMaxAge: 168h
# off | warn | enforce. enforce refuses privileged containers, hostPath
# mounts and their relatives; exempt lists workloads allowed anyway.
workloadHardening:
mode: enforce
minSeverity: high
exempt: []
Pair DenyUntilScanned with webhook.failurePolicy: Fail in the chart
values if the gate must hold even while the operator is down — see
Admission for what that trade costs.
Example: break-glass
Production is down, the fix is an image the policy refuses, and the incident channel has agreed to let it through. Break-glass admits the named images, records every use of the exception, and expires on its own — an exception that cannot outlive the incident.
apiVersion: attestkeep.com/v1alpha1
kind: ImageSecurityPolicy
metadata:
name: default
spec:
enforcement: Enforce
admissionTiming: AllowAndScan
blockLatestTag: true
thresholds:
blockCritical: true
blockHigh: true
breakGlass:
enabled: true
reason: "INC-2417: rollback image carries a known high finding"
approvedBy: "on-call, incident commander"
auditRef: "INC-2417"
expiresAt: "2026-09-01T06:00:00Z"
affectedImages:
- registry.example.com/payments/api:1.41.2
Every admission that passed only because of break-glass is marked as such in the audit trail and in compliance reports — the exception is evidence too, which is the point.
Field reference
| Field | Values | What it decides |
|---|---|---|
enforcement | Enforce | Audit | Refuse, or only record what would have been refused. |
admissionTiming | AllowAndScan | DenyUntilScanned | What happens to an image with no scan record yet. |
blockLatestTag | bool | Refuse the one tag that changes underneath you. |
digestEnforcement | AllowTags | PreferDigest | Required | Whether images must be pinned by digest. |
allowedRegistries | list of hosts | Empty allows any registry; non-empty refuses all others. |
targetNamespaces | list | Empty makes this the global policy; non-empty scopes it to those namespaces, overriding the global one there. |
thresholds | — | blockCritical/blockHigh refuse on any such finding; maxMedium/maxLow set ceilings (0 = unlimited). |
slaWindows | durations | How long a finding of each severity may stay open before reports call it overdue. |
requireSignature | bool | Refuse images without a verified cosign signature. |
requireSBOM | bool | Refuse images without an SBOM attestation. |
requireVulnAttestation | bool | Refuse images without a vulnerability-scan attestation. |
attestationMaxAge | duration | An attestation older than this no longer counts. |
workloadHardening | mode, minSeverity, exempt | Whether privileged containers, hostPath mounts and similar refuse (enforce), warn, or pass; exempt names workloads allowed anyway. |
breakGlass | — | A named, approved, expiring exception; every use is recorded. |
triageDefaults | maxDuration, requireJustification | How long an accepted risk may stand, and whether it must say why. |
storageUnreachablePolicy | Allow | Deny | The admission answer while the database is unreachable. |