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,UPDATEorDELETE. Wildcards (*) and subresources are not allowed in the matched resources. -
paramKindandparamRefare not supported. -
A policy needs at least one binding. A binding activates the policy and sets what happens when a validation fails, with
validationActions. UseDeny(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
- Dashboard
- Akuity CLI
- Terraform
-
Go to Settings → Admission Policies.
-
Click Add Admission Policy.
-
Enter a Name.
-
Under Admission Policy Definition, leave Type set to Validating, the only type today, and enter the Spec in YAML. The spec is the
specof a ValidatingAdmissionPolicy, as in the example above. -
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 setpolicyName; it is set to this policy. -
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.
Admission policies are part of declarative management. Put the ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding manifests, like the example above, next to your other instance manifests and apply them:
akuity kargo apply -f kargo/demo/
-
Policies are merged by name with the policies already on the instance. A policy that is applied again is replaced together with its bindings, so always apply a policy and its bindings together, for example from the same file.
-
To make your files the complete set of policies and remove any policy they don't contain, add
--prune admission-policies.--prune alldoes the same. -
akuity kargo exportincludes the policies and bindings as plain manifests.
Add the ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding manifests to kargo_resources of the akp_kargo_instance resource, the same way as Stages. See the provider example.
Once kargo_resources contains any policy or binding, Terraform manages all admission policies of the instance. Policies that are not in your configuration, including policies added in the dashboard, are removed on the next apply that changes the instance.
If the instance already has policies, add them to your configuration before you declare the first one in Terraform, or they are deleted. akuity kargo export prints them as manifests you can copy. An apply that does not change the instance leaves the stored policies alone.
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
UPDATEwithout a match condition also checks those updates. Use a match condition likespec-changedin the example to check only changes to the spec. -
On
DELETE,objectis null. OnlyoldObjectis set, so an expression that readsobject.specfails, and withfailurePolicy: Failthat refuses every delete. If you addDELETEto a policy, checkobject == nullfirst in the match condition, as in the example, and useoldObjectin the validations. -
Removing finalizers is an
UPDATE. A strictUPDATErule can leave Stages stuck in deletion. -
DELETErules 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 withhas()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.
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.