Skip to main content

Admission Policies

Admission policies let you enforce your own rules on the Stages of a Kargo instance. For example, you can require every Stage to carry a team label.

A policy is a Kubernetes ValidatingAdmissionPolicy. Each rule is a CEL expression that must be true for a change to be allowed. The Akuity Platform installs your policies into the instance control plane, where Kargo stores its Stages. The policies then check every create, update or delete of a Stage, no matter where it comes from: the Kargo UI or CLI, declarative management, Terraform, or Kargo itself.

The settings can be located by navigating to Kargo → your instance → Settings → Admission Policies.

What a policy can match​

Policies may only match stages in the kargo.akuity.io API group. The other rules:

  • Operations must be CREATE, UPDATE or DELETE. Wildcards (*) and subresources are not allowed in the matched resources.

  • paramKind and paramRef are not supported.

  • A policy needs at least one binding. A binding activates the policy and sets what happens when a validation fails, with validationActions. Use Deny (see the note at the end of this page).

  • An instance can have up to 50 policies. One policy with its bindings can be up to 64 KiB, and all policies together up to 512 KiB.

The policy is checked when you save it, including compiling every CEL expression, so a typo or an unsupported field is rejected with an error before anything reaches the instance. A saved policy takes effect once the instance finishes reconciling, usually within a minute.

Example​

This policy requires every Stage to have a team label:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: require-stage-team-label
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["kargo.akuity.io"]
apiVersions: ["v1alpha1"]
operations: ["CREATE", "UPDATE"]
resources: ["stages"]
matchConditions:
# Check new Stages, and changes to the spec or the labels. Other metadata
# updates, such as Kargo's own finalizers and annotations, are not checked.
# object is null on DELETE and oldObject on CREATE, so check both first.
- name: spec-or-labels-changed
expression: >-
object == null || oldObject == null || object.spec != oldObject.spec
|| (has(object.metadata.labels) ? object.metadata.labels : {})
!= (has(oldObject.metadata.labels) ? oldObject.metadata.labels : {})
validations:
- expression: "has(object.metadata.labels) && 'team' in object.metadata.labels"
message: all Stages must have a team label
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: require-stage-team-label
spec:
policyName: require-stage-team-label
validationActions: ["Deny"]

When someone creates a Stage without the label, removes the label, or changes the spec of a Stage that has none, the change is refused with the policy's message. The match condition checks label changes too, because the rule is about a label: a condition that only looked at the spec would let anyone remove the label.

Manage policies​

  1. Go to Settings → Admission Policies.

  2. Click Add Admission Policy.

  3. Enter a Name.

  4. Under Admission Policy Definition, leave Type set to Validating, the only type today, and enter the Spec in YAML. The spec is the spec of a ValidatingAdmissionPolicy, as in the example above.

  5. Under Admission Policy Bindings, click + to add a binding, and enter its Name and Spec, for example validationActions: ["Deny"]. A policy needs at least one binding. You do not need to set policyName; it is set to this policy.

  6. Click Add.

The list shows every policy with its bindings and validation actions. Use the edit and delete buttons next to a policy to change or remove it. Deleting a policy also deletes its bindings. The form and the list look the same as for Argo CD.

Things to watch out for​

Policies apply to every change, including the ones Kargo and the Akuity Platform make. A policy that is too broad can stop Kargo from working, so keep these in mind:

  • Kargo updates Stages itself. Stage status is stored separately and is not checked, but Kargo also updates Stage metadata, such as finalizers and annotations, on the Stage object. A policy that matches UPDATE without a match condition also checks those updates. Use a match condition like spec-changed in the example to check only changes to the spec.

  • On DELETE, object is null. Only oldObject is set, so an expression that reads object.spec fails, and with failurePolicy: Fail that refuses every delete. If you add DELETE to a policy, check object == null first in the match condition, as in the example, and use oldObject in the validations.

  • Removing finalizers is an UPDATE. A strict UPDATE rule can leave Stages stuck in deletion.

  • DELETE rules can block deleting Stages, including deleting a Project.

  • Existing objects are not fixed by a new policy. Every later change to a Stage that does not comply is refused, including changes by Kargo.

  • With failurePolicy: Fail, an expression error refuses the change. For example, reading a field that is not set fails the expression. Check optional fields with has() first, as in the example.

  • Your own tools are checked too. Declarative apply, Terraform and edits in the Kargo UI can start failing after you add a policy.

  • An instance on an unsupported Kargo version can't change its policies until it is upgraded.

To roll out a new policy safely, try it on a non-production instance first, and start narrow: match only CREATE, or only objects with a label, using objectSelector in the binding's matchResources. Widen it once you are sure it only refuses what you expect.

note

Warn and Audit are accepted, but only Deny has a visible effect. Warnings go to clients that call the instance control plane directly, and audit annotations go to the control plane's audit log, which are not exposed to you.