Attestkeep docs

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:

  1. A policy whose spec.targetNamespaces lists the namespace wins.
  2. Otherwise the policy with no targetNamespaces applies — 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

FieldValuesWhat it decides
enforcementEnforce | AuditRefuse, or only record what would have been refused.
admissionTimingAllowAndScan | DenyUntilScannedWhat happens to an image with no scan record yet.
blockLatestTagboolRefuse the one tag that changes underneath you.
digestEnforcementAllowTags | PreferDigest | RequiredWhether images must be pinned by digest.
allowedRegistrieslist of hostsEmpty allows any registry; non-empty refuses all others.
targetNamespaceslistEmpty makes this the global policy; non-empty scopes it to those namespaces, overriding the global one there.
thresholdsblockCritical/blockHigh refuse on any such finding; maxMedium/maxLow set ceilings (0 = unlimited).
slaWindowsdurationsHow long a finding of each severity may stay open before reports call it overdue.
requireSignatureboolRefuse images without a verified cosign signature.
requireSBOMboolRefuse images without an SBOM attestation.
requireVulnAttestationboolRefuse images without a vulnerability-scan attestation.
attestationMaxAgedurationAn attestation older than this no longer counts.
workloadHardeningmode, minSeverity, exemptWhether privileged containers, hostPath mounts and similar refuse (enforce), warn, or pass; exempt names workloads allowed anyway.
breakGlassA named, approved, expiring exception; every use is recorded.
triageDefaultsmaxDuration, requireJustificationHow long an accepted risk may stand, and whether it must say why.
storageUnreachablePolicyAllow | DenyThe admission answer while the database is unreachable.