gatekeeper Documentation

Policy Controller for Kubernetes

RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy gatekeeper on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name gatekeeper --restart always open-policy-agent/gatekeeper:latest

Technical Documentation: open-policy-agent/gatekeeper

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (`README.md`)

Gatekeeper

How is Gatekeeper different from OPA?

Compared to using OPA with its sidecar kube-mgmt (aka Gatekeeper v1.0), Gatekeeper introduces the following functionality:

  • An extensible, parameterized policy library
  • Native Kubernetes CRDs for instantiating the policy library (aka "constraints")
  • Native Kubernetes CRDs for extending the policy library (aka "constraint templates")
  • Native Kubernetes CRDs for mutation support
  • Audit functionality
  • External data support

Getting started

Check out the installation instructions to deploy Gatekeeper components to your Kubernetes cluster.

Documentation

Please see the Gatekeeper website for more in-depth information.

Policy Library

See the Gatekeeper policy library for a collection of constraint templates and sample constraints that you can use with Gatekeeper.

Community & Contributing

Please refer to Gatekeeper's contribution guide to find out how you can help.

Code of conduct

This project is governed by the CNCF Code of conduct.

Security

For details on how to report vulnerabilities and security release process, please refer to Gatekeeper Security for more information.


Overview (`deprecated/README.md`)

DEPRECATED

This directory contains Rego source for old-style (Kubernetes Policy Controller)
rules. This source is being preserved as a potential reference for writing
constraints for a future constraint library and should be deleted after
conversion.

It should be noted that for the Gatekeeper admission controller, this Rego
source is not functional as-written.


Overview (`library/README.md`)

Gatekeeper policy library

The Gatekeeper policy library has moved to a separate repository.


Overview (`website/README.md`)

Website

This website is built using Docusaurus 2, a modern static website generator.

Installation

console
yarn install

Local Development

console
yarn start

This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server.

Build

console
yarn build

This command generates static content into the build directory and can be served using any static contents hosting service.

Deployment

console
GIT_USER=<Your GitHub username> USE_SSH=true yarn deploy

If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the gh-pages branch.

Gatekeeper docs website uses Algolia DocSearch service. Please see here for more information.

If the search index has any issues:

  1. Go to Algolia search dashboard
  2. Click manage index and export configuration
  3. Delete the index
  4. Import saved configuration
  5. Go to Algolia crawler and restart crawling manually (takes about a few minutes to crawl). This is scheduled to run every week automatically.

Overview (`charts/gatekeeper/README.md`)

Gatekeeper Helm Chart

Get Repo Info

console
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo update

See helm repo for command documentation.

Install Chart

console
# Helm install with gatekeeper-system namespace already created
$ helm install -n gatekeeper-system [RELEASE_NAME] gatekeeper/gatekeeper

# Helm install and create namespace
$ helm install -n gatekeeper-system [RELEASE_NAME] gatekeeper/gatekeeper --create-namespace

See parameters below.

See helm install for command documentation.

Upgrade Chart

Upgrading from < v3.4.0
Chart 3.4.0 deprecates support for Helm 2 and also removes the creation of the gatekeeper-system Namespace from within
the chart. This follows Helm 3 Best Practices.

Option 1:
A simple way to upgrade is to uninstall first and re-install with 3.4.0 or greater.

console
$ helm uninstall gatekeeper
$ helm install -n gatekeeper-system [RELEASE_NAME] gatekeeper/gatekeeper --create-namespace

Option 2:
Run the helm_migrate.sh script before installing the 3.4.0 or greater chart. This will remove the Helm secret for the
original release, while keeping all of the resources. It then updates the annotations of the resources so that the new
chart can import and manage them.

console
$ helm_migrate.sh
$ helm install -n gatekeeper-system gatekeeper gatekeeper/gatekeeper

Upgrading from >= v3.4.0

console
$ helm upgrade -n gatekeeper-system [RELEASE_NAME] gatekeeper/gatekeeper

See helm 2 to 3 for Helm 2 migration documentation.

Exempting Namespace

The Helm chart automatically sets the Gatekeeper flag --exempt-namespace={{ .Release.Namespace }} in order to exempt
the namespace where the chart is installed, and adds the admission.gatekeeper.sh/ignore label to the namespace during
a post-install hook.

See Exempting Namespaces for more
information.

Parameters

Parameter Description Default
postInstall.labelNamespace.enabled Add labels to the namespace during post install hooks true
postInstall.labelNamespace.extraNamespaces The extra namespaces that need to have the label during post install hooks []
postInstall.labelNamespace.extraAnnotations Extra annotations added to the post install Job {}
postInstall.labelNamespace.image.repository Image with kubectl to label the namespace openpolicyagent/gatekeeper-crds
postInstall.labelNamespace.image.tag Image tag Current release version: v3.24.0-beta.0
postInstall.labelNamespace.image.pullPolicy Image pullPolicy IfNotPresent
postInstall.labelNamespace.image.pullSecrets Image pullSecrets []
postInstall.labelNamespace.extraRules Extra rules for the gatekeeper-update-namespace-label Role []
postInstall.labelNamespace.priorityClassName Priority class name for gatekeeper-update-namespace-label Job ``
postInstall.probeWebhook.enabled Probe webhook API post install. When enabled along with postInstall.labelNamespace.enabled, this probe will run as part of postInstall.labelNamespace Job as an initContainer true
postInstall.probeWebhook.image.repository Image with curl to probe the webhook API curlimages/curl
postInstall.probeWebhook.image.tag Image tag 8.20.0
postInstall.probeWebhook.image.pullPolicy Image pullPolicy IfNotPresent
postInstall.probeWebhook.image.pullSecrets Image pullSecrets []
postInstall.probeWebhook.waitTimeout Total time to wait for the webhook API to become available 60
postInstall.probeWebhook.httpTimeout HTTP client timeout 2
postInstall.probeWebhook.insecureHTTPS Ignore server SSL certificate false
postInstall.probeWebhook.priorityClassName Priority class name for gatekeeper-probe-webhook-post-install Job ``
postInstall.affinity The affinity to use for pod scheduling in postInstall hook jobs {}
postInstall.tolerations The tolerations to use for pod scheduling in postInstall hook jobs []
postInstall.nodeSelector The node selector to use for pod scheduling in postInstall hook jobs kubernetes.io/os: linux
postInstall.resources The resource request/limits for the container image in postInstall hook jobs {}
postInstall.securityContext Security context applied on the container { "allowPrivilegeEscalation": false, "capabilities": "drop": [all], "readOnlyRootFilesystem": true, "runAsGroup": 999, "runAsNonRoot": true, "runAsUser": 1000 }
postUpgrade.labelNamespace.enabled Add labels to the namespace during post upgrade hooks false
postUpgrade.labelNamespace.extraNamespaces The extra namespaces that need to have the label during post upgrade hooks []
postUpgrade.labelNamespace.extraAnnotations Extra annotations added to the post upgrade Job {}
postUpgrade.labelNamespace.image.repository Image with kubectl to label the namespace openpolicyagent/gatekeeper-crds
postUpgrade.labelNamespace.image.tag Image tag Current release version: v3.24.0-beta.0
postUpgrade.labelNamespace.image.pullPolicy Image pullPolicy IfNotPresent
postUpgrade.labelNamespace.image.pullSecrets Image pullSecrets []
postUpgrade.labelNamespace.priorityClassName Priority class name for gatekeeper-update-namespace-label-post-upgrade Job ``
postUpgrade.affinity The affinity to use for pod scheduling in postUpgrade hook jobs {}
postUpgrade.tolerations The tolerations to use for pod scheduling in postUpgrade hook jobs []
postUpgrade.nodeSelector The node selector to use for pod scheduling in postUpgrade hook jobs kubernetes.io/os: linux
postUpgrade.resources The resource request/limits for the container image in postUpgrade hook jobs {}
postUpgrade.securityContext Security context applied on the container { "allowPrivilegeEscalation": false, "capabilities": "drop": [all], "readOnlyRootFilesystem": true, "runAsGroup": 999, "runAsNonRoot": true, "runAsUser": 1000 }
preInstall.crdRepository.image.repository Image with kubectl to update the CRDs. If not set, the image.crdRepository is used instead. null
preInstall.crdRepository.image.tag Image tag Current release version: v3.24.0-beta.0
preUninstall.deleteWebhookConfigurations.enabled Delete webhooks before gatekeeper itself is uninstalled false
preUninstall.deleteWebhookConfigurations.image.repository Image with kubectl to delete the webhooks openpolicyagent/gatekeeper-crds
preUninstall.deleteWebhookConfigurations.image.tag Image tag Current release version: v3.24.0-beta.0
preUninstall.deleteWebhookConfigurations.image.pullPolicy Image pullPolicy IfNotPresent
preUninstall.deleteWebhookConfigurations.image.pullSecrets Image pullSecrets []
preUninstall.deleteWebhookConfigurations.extraRules Extra rules for the gatekeeper-delete-webhook-configs Role []
preUninstall.deleteWebhookConfigurations.priorityClassName Priority class name for gatekeeper-delete-webhook-configs Job ``
preUninstall.affinity The affinity to use for pod scheduling in preUninstall hook jobs {}
preUninstall.tolerations The tolerations to use for pod scheduling in preUninstall hook jobs []
preUninstall.nodeSelector The node selector to use for pod scheduling in preUninstall hook jobs kubernetes.io/os: linux
preUninstall.resources The resource request/limits for the container image in preUninstall hook jobs {}
preUninstall.securityContext Security context applied on the container { "allowPrivilegeEscalation": false, "capabilities": "drop": [all], "readOnlyRootFilesystem": true, "runAsGroup": 999, "runAsNonRoot": true, "runAsUser": 1000 }
upgradeCRDs.enabled Upgrade CRDs using pre-install/pre-upgrade hooks true
upgradeCRDs.extraRules Extra rules for the gatekeeper-admin-upgrade-crds ClusterRole []
upgradeCRDs.priorityClassName Priority class name for gatekeeper-update-crds-hook Job ``
crds.affinity The affinity to use for pod scheduling in crds hook jobs {}
crds.tolerations The tolerations to use for pod scheduling in crds hook jobs []
crds.nodeSelector The node selector to use for pod scheduling in crds hook jobs kubernetes.io/os: linux
crds.resources The resource request/limits for the container image in crds hook jobs {}
crds.securityContext Security context applied to the container { "allowPrivilegeEscalation": false, "capabilities": "drop": [all], "readOnlyRootFilesystem": true, "runAsGroup": 65532, "runAsNonRoot": true, "runAsUser": 65532 }
auditInterval The frequency with which audit is run 60
constraintViolationsLimit The maximum # of audit violations reported on a constraint 20
auditFromCache Take the roster of resources to audit from the audit cache false
auditChunkSize (alpha) Chunk size for listing cluster resources for audit 500
auditMatchKindOnly Only check resources of the kinds specified in all constraints defined in the cluster. false
disableAudit Disable audit controller false
disableMutation Disable mutation false
disableValidatingWebhook Disable the validating webhook false
validatingWebhookName The name of the ValidatingWebhookConfiguration gatekeeper-validating-webhook-configuration
validatingWebhookTimeoutSeconds The timeout for the validating webhook in seconds 3
validatingWebhookFailurePolicy The failurePolicy for the validating webhook Ignore
validatingWebhookAnnotations The annotations to add to the ValidatingWebhookConfiguration {}
validatingWebhookObjectSelector The label selector to further refine which namespaced resources will be selected by the webhook. Please note that an exemption label means users can circumvent Gatekeeper's validation webhook unless measures are taken to control how exemption labels can be set. {}
validatingWebhookMatchConditions The match conditions written in CEL to further refine which resources will be selected by the webhook. All match conditions must evaluate to true for the webhook to be called []
validatingWebhookCheckIgnoreFailurePolicy The failurePolicy for the check-ignore-label validating webhook Fail
validatingWebhookExemptNamespacesLabels Additional namespace labels that will be exempt from the validating webhook. Please note that anyone in the cluster capable to manage namespaces will be able to skip all Gatekeeper validation by setting one of these labels for their namespace. {}
validatingWebhookCustomRules Custom rules for selecting which API resources trigger the webhook. Mutually exclusive with enableDeleteOperations. NOTE: If you change this, ensure all your constraints are still being enforced. {}
validatingWebhookSubResources Rule for selecting which API subresources trigger the webhook. ['pods/ephemeralcontainers', 'pods/exec', 'pods/log', 'pods/eviction', 'pods/portforward', 'pods/proxy', 'pods/attach', 'pods/binding', 'pods/resize', 'deployments/scale', 'replicasets/scale', 'statefulsets/scale', 'replicationcontrollers/scale', 'services/proxy', 'nodes/proxy', 'services/status']
validatingWebhookURL Custom URL for Kubernetes API server to use to reach the validating webhook pod. If not set, the default of connecting via the kubernetes service endpoint is used. null
validatingWebhookScope The scope for the validating webhook. Does not work with validatingWebhookCustomRules *
additionalValidatingWebhookConfigsToRotateCerts The name of additional ValidatingWebhookConfigurations that the gatekeeper cert rotator should manage certs for []
enableDeleteOperations Enable validating webhook for delete operations. Does not work with validatingWebhookCustomRules false
enableConnectOperations Enable validating webhook for connect operations. false
enableExternalData Enable external data true
enableGeneratorResourceExpansion Enable generator resource expansion (beta feature) true
enableTLSHealthcheck Enable probing webhook API with certificate stored in certDir false
maxServingThreads Limit the number of concurrent calls the validation backend made by the validation webhook. -1 limits this value to GOMAXPROCS. Configuring this value may lower max RAM usage and limit CPU throttling, Tuning it can optimize serving capacity. -1
metricsBackends Metrics exporters to use. Valid exporters are: prometheus, stackdriver, and opentelemetry starting from Gatekeeper 3.15 ["prometheus"]
otlpEndpoint The OTLP endpoint for the OpenTelemetry metric exporter ""
otlpMetricInterval The interval to read metrics for the OpenTelemetry exporter "10s"
stackdriverOnlyWhenAvailable Only attempt to start the stackdriver exporter if credentials are available false
stackdriverMetricInterval Interval to read metrics for stackdriver exporter "10s"
mutatingWebhookName The name of the MutatingWebhookConfiguration gatekeeper-mutating-webhook-configuration
mutatingWebhookFailurePolicy The failurePolicy for the mutating webhook Ignore
mutatingWebhookReinvocationPolicy The reinvocationPolicy for the mutating webhook Never
mutatingWebhookAnnotations The annotations to add to the MutatingWebhookConfiguration {}
mutatingWebhookExemptNamespacesLabels Additional namespace labels that will be exempt from the mutating webhook. Please note that anyone in the cluster capable to manage namespaces will be able to skip all Gatekeeper validation by setting one of these labels for their namespace. {}
mutatingWebhookObjectSelector The label selector to further refine which namespaced resources will be selected by the webhook. Please note that an exemption label means users can circumvent Gatekeeper's mutation webhook unless measures are taken to control how exemption labels can be set. {}
mutatingWebhookMatchConditions The match conditions written in CEL to further refine which resources will be selected by the webhook. All match conditions must evaluate to true for the webhook to be called []
mutatingWebhookTimeoutSeconds The timeout for the mutating webhook in seconds 3
mutatingWebhookCustomRules Custom rules for selecting which API resources trigger the webhook. NOTE: If you change this, ensure all your constraints are still being enforced. {}
mutatingWebhookSubResources Rule for selecting which API subresources trigger the webhook. ['pods/ephemeralcontainers', 'pods/exec', 'pods/log', 'pods/eviction', 'pods/portforward', 'pods/proxy', 'pods/attach', 'pods/binding', 'deployments/scale', 'replicasets/scale', 'statefulsets/scale', 'replicationcontrollers/scale', 'services/proxy', 'nodes/proxy', 'services/status']
mutatingWebhookURL Custom URL for Kubernetes API server to use to reach the mutating webhook pod. If not set, the default of connecting via the kubernetes service endpoint is used. null
mutatingWebhookScope The scope for the mutating webhook. Does not work with mutatingWebhookCustomRules *
additionalMutatingWebhookConfigsToRotateCerts The name of additional MutatingWebhookConfigurations that the gatekeeper cert rotator should manage certs for []
emitAdmissionEvents (alpha) Emit K8s events in configurable namespace for admission violations false
emitAuditEvents (alpha) Emit K8s events in configurable namespace for audit violations false
logStatsAdmission (alpha) Log stats for admission webhook false
logStatsAudit (alpha) Log stats metrics for the audit run false
enableK8sNativeValidation Enable the K8s Native Validating driver to allow constraint templates to use rules written in VAP-style CEL true
defaultCreateVAPForTemplates (beta) Create VAP resource for template containing VAP-style CEL source. Allowed values are false: do not create Validating Admission Policy unless generateVAP: true is set on constraint template explicitly, true: create Validating Admission Policy unless generateVAP: false is set on constraint template explicitly. true
defaultCreateVAPBindingForConstraints (beta) Create VAPBinding resource for constraint of the template containing VAP-style CEL source. Allowed values are false: do not create Validating Admission Policy Binding, true: create Validating Admission Policy Binding. true
defaultWaitForVAPBGeneration (beta) Wait time in seconds before generating a ValidatingAdmissionPolicyBinding after a constraint CRD is created. 30
auditEventsInvolvedNamespace Emit audit events for each violation in the involved objects namespace, the default (false) generates events in the namespace Gatekeeper is installed in. Audit events from cluster-scoped resources will continue to generate events in the namespace that Gatekeeper is installed in false
admissionEventsInvolvedNamespace Emit admission events for each violation in the involved objects namespace, the default (false) generates events in the namespace Gatekeeper is installed in. Admission events from cluster-scoped resources will continue to generate events in the namespace that Gatekeeper is installed in false
logDenies Log detailed info on each deny false
logLevel Minimum log level INFO
image.pullPolicy The image pull policy IfNotPresent
image.repository Image repository openpolicyagent/gatekeeper
image.release The image release tag to use Current release version: v3.24.0-beta.0
image.pullSecrets Specify an array of imagePullSecrets []
resources The resource request/limits for the container image limits: 1 CPU, 512Mi, requests: 100mCPU, 256Mi
nodeSelector The node selector to use for pod scheduling kubernetes.io/os: linux
controllerManager.containerName Container name for controller manager manager
controllerManager.podLabels The labels to add to the controller manager pod {}
controllerManager.affinity The node affinity to use for controller manager pod scheduling {}
controllerManager.topologySpreadConstraints The topology spread constraints to use for controller manager pod scheduling []
controllerManager.tolerations The tolerations to use for controller manager pod scheduling []
controllerManager.healthPort Health port for controller manager 9090
controllerManager.port Webhook-server port for controller manager 8443
controllerManager.metricsPort Metrics port for controller manager 8888
controllerManager.readinessTimeout Timeout in seconds for the controller manager's readiness probe 1
controllerManager.livenessTimeout Timeout in seconds for the controller manager's liveness probe 1
controllerManager.logLevel The minimum log level for the controller manager, takes precedence over logLevel when specified null
controllerManager.priorityClassName Priority class name for controller manager system-cluster-critical
controllerManager.podSecurityContext Security context on pod level for controller manager {fsGroup: 999, suplementalGroups: [999]}
controllerManager.exemptNamespaces The exact namespaces to exempt by the admission webhook []
controllerManager.exemptNamespacePrefixes The namespace prefixes to exempt by the admission webhook []
controllerManager.hostNetwork Enables controllerManager to be deployed on hostNetwork false
controllerManager.dnsPolicy Set the dnsPolicy for controllerManager pods ClusterFirst
controllerManager.securityContext Security context applied on the container { "allowPrivilegeEscalation": false, "capabilities": "drop": [all], "readOnlyRootFilesystem": true, "runAsGroup": 999, "runAsNonRoot": true, "runAsUser": 1000 }
controllerManager.tlsMinVersion Set the minimum supported TLS version for validating and mutating webhook servers 1.3
controllerManager.extraRules Extra rules for the gatekeeper-manager-role Role []
controllerManager.networkPolicy.enabled Should a network policy for the controller manager be created false
controllerManager.networkPolicy.ingress Additional ingress rules to be added to the controller manager network policy {}
controllerManager.strategyType The strategy type to use for Controller Manager deployment RollingUpdate
controllerManager.strategyRollingUpdate RollingUpdate configuration for Controller Manager deployment {}
controllerManager.disableWebhookOperation Disable webhook operation for controller manager false
controllerManager.disableGenerateOperation Disable generate operation for controller manager true
audit.containerName Container name for audit manager
audit.podLabels The labels to add to the audit pod {}
audit.affinity The node affinity to use for audit pod scheduling {}
audit.topologySpreadConstraints The topology spread constraints to use for audit pod scheduling []
audit.tolerations The tolerations to use for audit pod scheduling []
audit.priorityClassName Priority class name for audit controller system-cluster-critical
audit.podSecurityContext Security context for audit on pod level {fsGroup: 999, suplementalGroups: [999]}
audit.hostNetwork Enables audit to be deployed on hostNetwork false
audit.dnsPolicy Set the dnsPolicy for audit pods ClusterFirst
audit.securityContext Security context applied on the container { "allowPrivilegeEscalation": false, "capabilities": "drop": [all], "readOnlyRootFilesystem": true, "runAsGroup": 999, "runAsNonRoot": true, "runAsUser": 1000 }
audit.healthPort Health port for audit 9090
audit.metricsPort Metrics port for audit 8888
audit.readinessTimeout Timeout in seconds for audit's readiness probe 1
audit.livenessTimeout Timeout in seconds for the audit's liveness probe 1
audit.logLevel The minimum log level for audit, takes precedence over logLevel when specified null
audit.disableGenerateOperation Disable generate operation for audit false
audit.disableAuditOperation Disable audit operation for audit false
audit.disableAuditSidecar Disable audit sidecar false
audit.disableStatusOperation Disable status operation for audit false
enableViolationExport (alpha) Enable exporting violations to external systems false
audit.connection (alpha) Connection name for exporting audit violation messages audit-connection
audit.channel (alpha) Channel name for exporting audit violation messages audit-channel
audit.exportVolume (alpha) Volume for audit pod to export violations. {"name":"tmp-violations","emptyDir":{}}
audit.exportVolumeMount.path (alpha) VolumeMount for audit pod manager container to export violations and sidecar container to read from. /tmp/violations
audit.exportSidecar (alpha) Sidecar container to read violations from disk. {"name":"reader","image":"ghcr.io/open-policy-agent/fake-reader:latest","imagePullPolicy":"Always","securityContext":{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsGroup":999,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"volumeMounts":[{"mountPath":"/tmp/violations","name":"tmp-violations"}]}
audit.exportConnection.path (alpha) Path for audit pod manager container to export violations and sidecar container to read from. Must be a child of volume mount path so the parent is writable. /tmp/violations/topics
audit.exportConnection.maxAuditResults (alpha) Maximum number of audit results that can be stored in the export path. 3
exportBackend (alpha) Backend to use for exporting violations, i.e. dapr, disk. ""
replicas The number of Gatekeeper replicas to deploy for the webhook 3
podAnnotations The annotations to add to the Gatekeeper pods container.seccomp.security.alpha.kubernetes.io/manager: runtime/default
podLabels The labels to add to the Gatekeeper pods {}
podCountLimit The maximum number of Gatekeeper pods to run 100
secretAnnotations The annotations to add to the Gatekeeper secrets {}
pdb.controllerManager.minAvailable The number of controller manager pods that must still be available after an eviction 1
service.type Service type ClusterIP
service.loadBalancerIP The IP address of LoadBalancer service ``
service.ipFamilyPolicy The IP family policy used for the service, for more information see Configure dual-stack. ``
service.ipFamilies List of IP families for the service. Can be "IPv4" and/or "IPv6" ``
service.healthzPort Service port to gatekeeper Webhook health port 9090
rbac.create Enable the creation of RBAC resources true
externalCertInjection.enabled Enable the injection of an external certificate. This disables automatic certificate generation and rotation false
externalCertInjection.secretName Name of secret for injected certificate gatekeeper-webhook-server-cert
externaldataProviderResponseCacheTTL TTL for the external data provider response cache. Specify the duration in 'h', 'm', or 's' for hours, minutes, or seconds respectively. 3m
commonAnnotations Additional annotations added to manager and audit Deployments only {}
extraVolumeMounts Extra volumeMounts injected into controller-manager and audit containers []
extraVolumes Extra volumes injected into controller-manager and audit pods []
controllerManager.serviceAccount.automountServiceAccountToken Whether to automount the service account token for controller-manager pods true
audit.serviceAccount.automountServiceAccountToken Whether to automount the service account token for audit pods true
syncVAPEnforcementScope (beta) Synchronize ValidatingAdmissionPolicy enforcement scope with Gatekeeper's admission validation scope. When enabled, VAP resources inherit match criteria, conditions, and namespace exclusions from Gatekeeper's webhook configuration, Config resource and exempt namespace flags. This ensures consistent policy enforcement between Gatekeeper and VAP but triggers constraint template reconciliation on scope changes in Config resource or webhook configuration. This flag is set to true by default. It is deprecated and will be removed in Gatekeeper v3.24. true
logLevelKey JSON key for the log level field. Only set when specified. Not set (default: level)
logLevelEncoder Encoder for the value of the log level field. Valid values: lower, capital, color, capitalcolor. Only set when specified. Not set (default: lower)
metricsAddr The address the metric endpoint binds to. Only set when specified. Not set (default: 0)
readinessRetries The number of resource ingestion attempts allowed before the resource is disregarded. A value of -1 will retry indefinitely. Only set when specified. Not set (default: 0)
enablePprof Enable pprof profiling. Only set when specified. Not set (default: false)
pprofPort Port for pprof profiling. Only set when specified. Not set (default: 6060)
disableEnforcementActionValidation Disable validation of the enforcementAction and scopedEnforcementActions field of a constraint. Only set when specified. Not set (default: false)
enableReferentialRules Enable referential rules. Set to false to disallow referential constraints. Referential constraints read objects other than the object-under-test and may be subject to race conditions. Only set when specified. Not set (default: true)
controllerManager.host The host address the webhook server listens on. Only set when specified. Not set (default: all addresses)
controllerManager.certDir The directory where certs are stored. Not set (default: /certs)
controllerManager.certServiceName The service name used to generate the TLS cert's hostname. Not set (default: gatekeeper-webhook-service)
controllerManager.clientCAName Name of the certificate authority bundle to authenticate the Kubernetes API server requests against. Only set when specified. Not set
controllerManager.clientCNName Expected CN name on the client certificate attached by apiserver in requests to the webhook. Only set when specified. Not set (default: kube-apiserver)
audit.apiCacheDir The directory where audit from api server cache are stored. Only set when specified. Not set (default: /tmp/audit)
shutdownDelay Time in seconds the controller runtime shutdown gets delayed after receiving a pod termination event. Prevents failing webhooks on pod shutdown. Only set when specified. Not set (default: 10)
enableRemoteCluster (alpha) Enable remote cluster mode where Gatekeeper operates against a target cluster specified via kubeconfig while running in the local cluster. Requires kubeconfig to also be set, and the kubeconfig file to be mounted via extraVolumes/extraVolumeMounts. Only set when specified. Not set (default: false)
kubeconfig Path to kubeconfig file inside the container for remote cluster mode. The file must be mounted into the container using extraVolumes and extraVolumeMounts. Only set when specified. Not set

Contributing Changes

Please refer
to Contributing to Helm Chart
for modifying the Helm chart.


Overview (`demo/agilebank/README.md`)

[!NOTE]
Be sure to look at the prerequisite and Gatekeeper installation steps before attempting to run the script.

[!NOTE]
Use the latest version of Gatekeeper as some of the features are only available in the latest Gatekeeper release and some of the features are only compatible with Kubernetes v1.19+.

[!TIP]
If you are new to k8s, you can stand up a simple cluster using kind: kind create cluster.

This is a demo involving a fictitious bank named Agile Bank.

To run, execute demo.sh and press enter.


Overview (`demo/basic/README.md`)

[!NOTE]
Be sure to look at the prerequisite and Gatekeeper installation steps before attempting to run the script.

[!TIP]
If you are new to k8s, you can stand up a simple cluster using kind: kind create cluster.

Demo of basic Gatekeeper functionality.

To run, execute demo.sh and press enter.


Overview (`demo/k8s-validating-admission-policy/README.md`)

[!WARNING]
This is a demo of an alpha and a beta feature and is subject to change.

This demo shows two new capabilities:

  1. [beta] Together with Gatekeeper and gator CLI, you can get admission, audit, and shift left validations for both Validating Admission Policy style CEL-based and OPA Rego policies, even for clusters that do not support Kubernetes Validating Admission Policy feature yet.
  2. [alpha] Gatekeeper can be enabled to generate Kubernetes Validating Admission Policy and Binding resources such that admission validation can be handled by Kubernetes's in-process Validating Admission Policy Controller instead of the Gatekeeper admission webhook. In the event the Validating Admission Policy Controller fails open, then Gatekeeper admission webhook can act as a fallback. This requires clusters with the Kubernetes Validating Admission Policy feature enabled.

Please refer to https://open-policy-agent.github.io/gatekeeper/website/docs/next/validating-admission-policy for pre-requisites and configuration steps.

Demo


Overview (`demo/scoped-enforcement-actions/README.md`)

[!WARNING]
This is a demo of an alpha feature and is subject to change.

This demo shows:

  1. Configuring different validation actions for different enforcement points.
  2. Integration with VAP as enforcement point such that admission validation can be handled by Kubernetes's in-process Validating Admission Policy Controller instead of the Gatekeeper admission webhook. In the event the Validating Admission Policy Controller fails open, then Gatekeeper admission webhook can act as a fallback. This requires clusters with the Kubernetes Validating Admission Policy feature enabled.

Please refer to https://open-policy-agent.github.io/gatekeeper/website/docs/next/validating-admission-policy for pre-requisites and configuration steps.

Demo


Overview (`docs/design/README.md`)

Design Docs

Generally, design docs are on Google docs:

In Development

Proposed

Implemented

Roadmap (in development)

Roadmap (complete)

Discussions

These docs may not be design docs in themselves, but seek to influence design
decisions

  • Mutation Convergence Proof
    • This looks at the elements of mutation that allow us to assert that any combination of mutators should converge
      and should serve as a guide when considering changes in behavior to mutation.
  • Mutation Dynamics
    • A dissection of the mutation problem space, attempting to break down the
      salient behavioral elements and figure out what's needed for a
      user-friendly set of mutation primitives.
  • Mutation Transience
    • Looking at how a mutation webhook may behave during configuration changes and discussing what that might mean in terms
      of necessary status fields and best practices for writing mutators.
  • Finalizers in OPA Gatekeeper
    • This doc discusses the impact the existence of finalizers has on running
      OPA Gatekeeper. Its points have either been addressed or are on track
      to being addressed.

Overview (`third_party/demo-magic/README.md`)

Demo Magic

demo-magic.sh is a handy shell script that enables you to script repeatable demos in a bash environment so you don't have to type as you present. Rather than trying to type commands when presenting you simply script them and let demo-magic.sh run them for you.

Features

  • Simulates typing. It looks like you are actually typing out commands
  • Allows you to actually run commands or pretend to do so.
  • Can hide commands from presentation. Useful for behind the scenes stuff that doesn't need to be shown.

Functions

pe

Print and Execute. This function will simulate typing whatever you give it. It will then pause until you press ENTER. After your keypress it will run the command.

bash
#!/bin/bash

pe "ls -l"

p

Print only. This function will simulate typing whatever you give it. It will not run the command. After typing it will pause until you press ENTER. After your keypress it will move on to the next instruction in your script.

bash
#!/bin/bash

p "ls -l"

wait

Waits for the user to press ENTER.

If PROMPT_TIMEOUT is defined and > 0 the demo will automatically proceed after the amount of seconds has passed.

bash
#!/bin/bash

# Will wait until user presses enter
PROMPT_TIMEOUT=0
wait

# Will wait max 5 seconds until user presses
PROMPT_TIMEOUT=5
wait

cmd

Enters script into interactive mode and allows newly typed commands to be executed within the script

text
#!/bin/bash

cmd

Getting Started

Create a shell script and include demo-magic.sh

bash
#!/bin/bash

########################
# include the magic
########################
. demo-magic.sh

# hide the evidence
clear

# Put your stuff here

Then use the handy functions to run through your demo.

Command line usage

demo-magic.sh exposes 3 options out of the box to your script.

  • -d - disable simulated typing. Useful for debugging
  • -h - prints the usage text
  • -n - set no default waiting after p and pe functions
  • -w - set no wait timeout after p and pe functions
bash
$ ./my-demo.sh -h

Usage: ./my-demo.sh [options]

  Where options is one or more of:
  -h  Prints Help text
  -d  Debug mode. Disables simulated typing
  -n  No wait
  -w  Waits max the given amount of seconds before proceeding with demo (e.g. `-w5`)

Useful Tricks

Faking network connections

Network connections during demos are often unreliable. Try and fake whatever commands would rely on a network connection. For example: Instead of trying to install node modules in a node.js application you can fake it. You can install the node_modules at home on your decent network. Then rename the directory and pretend to install it later by symlinking. If you want to be thorough you can capture the output of npm install into a log file then cat it out later to simulate the install.

bash
#!/bin/bash

########################
# include the magic
########################
. demo-magic.sh

# hide the evidence
clear

# this command is typed and executed
pe "cd my-app"

# this command is merely typed. Not executed
p "npm install"

# this command runs behind the scenes
ln -s cached_node_modules node_modules

# cat out a log file that captures a previous successful node modules install
cat node-modules-install.log

# now type and run the command to start your app
pe "node index.js"

No waiting

The -n no wait option can be useful if you want to print and execute multiple commands.

bash
# include demo-magic
. demo-magic.sh -n

# add multiple commands
pe 'git status'
pe 'git log --oneline --decorate -n 20'

However this will oblige you to define your waiting points manually e.g.

bash
...
# define waiting points
pe 'git status'
pe 'git log --oneline --decorate -n 20'
wait
pe 'git pull'
pe 'git log --oneline --decorate -n 20'
wait

Overview (`third_party/k8s.io/README.md`)

k8s.io/kubernetes

Forked from k8s.io/kubernetes@139c6daf302442aa19fed6cba8732a1b5c0eb1e0

This is a light fork to use scripts from kubernetes/kubernetes for gatekeeper's use cases.

The original code can be found at https://github.com/kubernetes/kubernetes/tree/139c6daf302442aa19fed6cba8732a1b5c0eb1e0.


Overview (`manifest_staging/charts/gatekeeper/README.md`)

Gatekeeper Helm Chart

Get Repo Info

console
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo update

See helm repo for command documentation.

Install Chart

console
# Helm install with gatekeeper-system namespace already created
$ helm install -n gatekeeper-system [RELEASE_NAME] gatekeeper/gatekeeper

# Helm install and create namespace
$ helm install -n gatekeeper-system [RELEASE_NAME] gatekeeper/gatekeeper --create-namespace

See parameters below.

See helm install for command documentation.

Upgrade Chart

Upgrading from < v3.4.0
Chart 3.4.0 deprecates support for Helm 2 and also removes the creation of the gatekeeper-system Namespace from within
the chart. This follows Helm 3 Best Practices.

Option 1:
A simple way to upgrade is to uninstall first and re-install with 3.4.0 or greater.

console
$ helm uninstall gatekeeper
$ helm install -n gatekeeper-system [RELEASE_NAME] gatekeeper/gatekeeper --create-namespace

Option 2:
Run the helm_migrate.sh script before installing the 3.4.0 or greater chart. This will remove the Helm secret for the
original release, while keeping all of the resources. It then updates the annotations of the resources so that the new
chart can import and manage them.

console
$ helm_migrate.sh
$ helm install -n gatekeeper-system gatekeeper gatekeeper/gatekeeper

Upgrading from >= v3.4.0

console
$ helm upgrade -n gatekeeper-system [RELEASE_NAME] gatekeeper/gatekeeper

See helm 2 to 3 for Helm 2 migration documentation.

Exempting Namespace

The Helm chart automatically sets the Gatekeeper flag --exempt-namespace={{ .Release.Namespace }} in order to exempt
the namespace where the chart is installed, and adds the admission.gatekeeper.sh/ignore label to the namespace during
a post-install hook.

See Exempting Namespaces for more
information.

Parameters

Parameter Description Default
postInstall.labelNamespace.enabled Add labels to the namespace during post install hooks true
postInstall.labelNamespace.extraNamespaces The extra namespaces that need to have the label during post install hooks []
postInstall.labelNamespace.extraAnnotations Extra annotations added to the post install Job {}
postInstall.labelNamespace.image.repository Image with kubectl to label the namespace openpolicyagent/gatekeeper-crds
postInstall.labelNamespace.image.tag Image tag Current release version: v3.24.0-beta.0
postInstall.labelNamespace.image.pullPolicy Image pullPolicy IfNotPresent
postInstall.labelNamespace.image.pullSecrets Image pullSecrets []
postInstall.labelNamespace.extraRules Extra rules for the gatekeeper-update-namespace-label Role []
postInstall.labelNamespace.priorityClassName Priority class name for gatekeeper-update-namespace-label Job ``
postInstall.probeWebhook.enabled Probe webhook API post install. When enabled along with postInstall.labelNamespace.enabled, this probe will run as part of postInstall.labelNamespace Job as an initContainer true
postInstall.probeWebhook.image.repository Image with curl to probe the webhook API curlimages/curl
postInstall.probeWebhook.image.tag Image tag 8.20.0
postInstall.probeWebhook.image.pullPolicy Image pullPolicy IfNotPresent
postInstall.probeWebhook.image.pullSecrets Image pullSecrets []
postInstall.probeWebhook.waitTimeout Total time to wait for the webhook API to become available 60
postInstall.probeWebhook.httpTimeout HTTP client timeout 2
postInstall.probeWebhook.insecureHTTPS Ignore server SSL certificate false
postInstall.probeWebhook.priorityClassName Priority class name for gatekeeper-probe-webhook-post-install Job ``
postInstall.affinity The affinity to use for pod scheduling in postInstall hook jobs {}
postInstall.tolerations The tolerations to use for pod scheduling in postInstall hook jobs []
postInstall.nodeSelector The node selector to use for pod scheduling in postInstall hook jobs kubernetes.io/os: linux
postInstall.resources The resource request/limits for the container image in postInstall hook jobs {}
postInstall.securityContext Security context applied on the container { "allowPrivilegeEscalation": false, "capabilities": "drop": [all], "readOnlyRootFilesystem": true, "runAsGroup": 999, "runAsNonRoot": true, "runAsUser": 1000 }
postUpgrade.labelNamespace.enabled Add labels to the namespace during post upgrade hooks false
postUpgrade.labelNamespace.extraNamespaces The extra namespaces that need to have the label during post upgrade hooks []
postUpgrade.labelNamespace.extraAnnotations Extra annotations added to the post upgrade Job {}
postUpgrade.labelNamespace.image.repository Image with kubectl to label the namespace openpolicyagent/gatekeeper-crds
postUpgrade.labelNamespace.image.tag Image tag Current release version: v3.24.0-beta.0
postUpgrade.labelNamespace.image.pullPolicy Image pullPolicy IfNotPresent
postUpgrade.labelNamespace.image.pullSecrets Image pullSecrets []
postUpgrade.labelNamespace.priorityClassName Priority class name for gatekeeper-update-namespace-label-post-upgrade Job ``
postUpgrade.affinity The affinity to use for pod scheduling in postUpgrade hook jobs {}
postUpgrade.tolerations The tolerations to use for pod scheduling in postUpgrade hook jobs []
postUpgrade.nodeSelector The node selector to use for pod scheduling in postUpgrade hook jobs kubernetes.io/os: linux
postUpgrade.resources The resource request/limits for the container image in postUpgrade hook jobs {}
postUpgrade.securityContext Security context applied on the container { "allowPrivilegeEscalation": false, "capabilities": "drop": [all], "readOnlyRootFilesystem": true, "runAsGroup": 999, "runAsNonRoot": true, "runAsUser": 1000 }
preInstall.crdRepository.image.repository Image with kubectl to update the CRDs. If not set, the image.crdRepository is used instead. null
preInstall.crdRepository.image.tag Image tag Current release version: v3.24.0-beta.0
preUninstall.deleteWebhookConfigurations.enabled Delete webhooks before gatekeeper itself is uninstalled false
preUninstall.deleteWebhookConfigurations.image.repository Image with kubectl to delete the webhooks openpolicyagent/gatekeeper-crds
preUninstall.deleteWebhookConfigurations.image.tag Image tag Current release version: v3.24.0-beta.0
preUninstall.deleteWebhookConfigurations.image.pullPolicy Image pullPolicy IfNotPresent
preUninstall.deleteWebhookConfigurations.image.pullSecrets Image pullSecrets []
preUninstall.deleteWebhookConfigurations.extraRules Extra rules for the gatekeeper-delete-webhook-configs Role []
preUninstall.deleteWebhookConfigurations.priorityClassName Priority class name for gatekeeper-delete-webhook-configs Job ``
preUninstall.affinity The affinity to use for pod scheduling in preUninstall hook jobs {}
preUninstall.tolerations The tolerations to use for pod scheduling in preUninstall hook jobs []
preUninstall.nodeSelector The node selector to use for pod scheduling in preUninstall hook jobs kubernetes.io/os: linux
preUninstall.resources The resource request/limits for the container image in preUninstall hook jobs {}
preUninstall.securityContext Security context applied on the container { "allowPrivilegeEscalation": false, "capabilities": "drop": [all], "readOnlyRootFilesystem": true, "runAsGroup": 999, "runAsNonRoot": true, "runAsUser": 1000 }
upgradeCRDs.enabled Upgrade CRDs using pre-install/pre-upgrade hooks true
upgradeCRDs.extraRules Extra rules for the gatekeeper-admin-upgrade-crds ClusterRole []
upgradeCRDs.priorityClassName Priority class name for gatekeeper-update-crds-hook Job ``
crds.affinity The affinity to use for pod scheduling in crds hook jobs {}
crds.tolerations The tolerations to use for pod scheduling in crds hook jobs []
crds.nodeSelector The node selector to use for pod scheduling in crds hook jobs kubernetes.io/os: linux
crds.resources The resource request/limits for the container image in crds hook jobs {}
crds.securityContext Security context applied to the container { "allowPrivilegeEscalation": false, "capabilities": "drop": [all], "readOnlyRootFilesystem": true, "runAsGroup": 65532, "runAsNonRoot": true, "runAsUser": 65532 }
auditInterval The frequency with which audit is run 60
constraintViolationsLimit The maximum # of audit violations reported on a constraint 20
auditFromCache Take the roster of resources to audit from the audit cache false
auditChunkSize (alpha) Chunk size for listing cluster resources for audit 500
auditMatchKindOnly Only check resources of the kinds specified in all constraints defined in the cluster. false
disableAudit Disable audit controller false
disableMutation Disable mutation false
disableValidatingWebhook Disable the validating webhook false
validatingWebhookName The name of the ValidatingWebhookConfiguration gatekeeper-validating-webhook-configuration
validatingWebhookTimeoutSeconds The timeout for the validating webhook in seconds 3
validatingWebhookFailurePolicy The failurePolicy for the validating webhook Ignore
validatingWebhookAnnotations The annotations to add to the ValidatingWebhookConfiguration {}
validatingWebhookObjectSelector The label selector to further refine which namespaced resources will be selected by the webhook. Please note that an exemption label means users can circumvent Gatekeeper's validation webhook unless measures are taken to control how exemption labels can be set. {}
validatingWebhookMatchConditions The match conditions written in CEL to further refine which resources will be selected by the webhook. All match conditions must evaluate to true for the webhook to be called []
validatingWebhookCheckIgnoreFailurePolicy The failurePolicy for the check-ignore-label validating webhook Fail
validatingWebhookExemptNamespacesLabels Additional namespace labels that will be exempt from the validating webhook. Please note that anyone in the cluster capable to manage namespaces will be able to skip all Gatekeeper validation by setting one of these labels for their namespace. {}
validatingWebhookCustomRules Custom rules for selecting which API resources trigger the webhook. Mutually exclusive with enableDeleteOperations. NOTE: If you change this, ensure all your constraints are still being enforced. {}
validatingWebhookSubResources Rule for selecting which API subresources trigger the webhook. ['pods/ephemeralcontainers', 'pods/exec', 'pods/log', 'pods/eviction', 'pods/portforward', 'pods/proxy', 'pods/attach', 'pods/binding', 'pods/resize', 'deployments/scale', 'replicasets/scale', 'statefulsets/scale', 'replicationcontrollers/scale', 'serviceaccounts/token', 'services/proxy', 'nodes/proxy', 'certificatesigningrequests/approval', 'services/status']
validatingWebhookURL Custom URL for Kubernetes API server to use to reach the validating webhook pod. If not set, the default of connecting via the kubernetes service endpoint is used. null
validatingWebhookScope The scope for the validating webhook. Does not work with validatingWebhookCustomRules *
additionalValidatingWebhookConfigsToRotateCerts The name of additional ValidatingWebhookConfigurations that the gatekeeper cert rotator should manage certs for []
enableDeleteOperations Enable validating webhook for delete operations. Does not work with validatingWebhookCustomRules false
enableConnectOperations Enable validating webhook for connect operations. false
enableExternalData Enable external data true
enableGeneratorResourceExpansion Enable generator resource expansion (beta feature) true
enableTLSHealthcheck Enable probing webhook API with certificate stored in certDir false
maxServingThreads Limit the number of concurrent calls the validation backend made by the validation webhook. -1 limits this value to GOMAXPROCS. Configuring this value may lower max RAM usage and limit CPU throttling, Tuning it can optimize serving capacity. -1
metricsBackends Metrics exporters to use. Valid exporters are: prometheus, stackdriver, and opentelemetry starting from Gatekeeper 3.15 ["prometheus"]
otlpEndpoint The OTLP endpoint for the OpenTelemetry metric exporter ""
otlpMetricInterval The interval to read metrics for the OpenTelemetry exporter "10s"
stackdriverOnlyWhenAvailable Only attempt to start the stackdriver exporter if credentials are available false
stackdriverMetricInterval Interval to read metrics for stackdriver exporter "10s"
mutatingWebhookName The name of the MutatingWebhookConfiguration gatekeeper-mutating-webhook-configuration
mutatingWebhookFailurePolicy The failurePolicy for the mutating webhook Ignore
mutatingWebhookReinvocationPolicy The reinvocationPolicy for the mutating webhook Never
mutatingWebhookAnnotations The annotations to add to the MutatingWebhookConfiguration {}
mutatingWebhookExemptNamespacesLabels Additional namespace labels that will be exempt from the mutating webhook. Please note that anyone in the cluster capable to manage namespaces will be able to skip all Gatekeeper validation by setting one of these labels for their namespace. {}
mutatingWebhookObjectSelector The label selector to further refine which namespaced resources will be selected by the webhook. Please note that an exemption label means users can circumvent Gatekeeper's mutation webhook unless measures are taken to control how exemption labels can be set. {}
mutatingWebhookMatchConditions The match conditions written in CEL to further refine which resources will be selected by the webhook. All match conditions must evaluate to true for the webhook to be called []
mutatingWebhookTimeoutSeconds The timeout for the mutating webhook in seconds 3
mutatingWebhookCustomRules Custom rules for selecting which API resources trigger the webhook. NOTE: If you change this, ensure all your constraints are still being enforced. {}
mutatingWebhookSubResources Rule for selecting which API subresources trigger the webhook. ['pods/ephemeralcontainers', 'pods/exec', 'pods/log', 'pods/eviction', 'pods/portforward', 'pods/proxy', 'pods/attach', 'pods/binding', 'pods/resize', 'deployments/scale', 'replicasets/scale', 'statefulsets/scale', 'replicationcontrollers/scale', 'serviceaccounts/token', 'services/proxy', 'nodes/proxy', 'certificatesigningrequests/approval', 'services/status']
mutatingWebhookURL Custom URL for Kubernetes API server to use to reach the mutating webhook pod. If not set, the default of connecting via the kubernetes service endpoint is used. null
mutatingWebhookScope The scope for the mutating webhook. Does not work with mutatingWebhookCustomRules *
additionalMutatingWebhookConfigsToRotateCerts The name of additional MutatingWebhookConfigurations that the gatekeeper cert rotator should manage certs for []
emitAdmissionEvents (alpha) Emit K8s events in configurable namespace for admission violations false
emitAdmissionAuditAnnotations (alpha) Add bounded violation details to API server audit events for Gatekeeper validation webhook and native validation-failure annotations for generated VAPs false
admissionAuditAnnotationsIncludeSuccess (alpha) When admission audit annotations are enabled, also annotate zero-violation webhook requests and add evaluation markers to generated VAPs. Deny, warn, and dryrun violations remain audited regardless of this setting. false
emitAuditEvents (alpha) Emit K8s events in configurable namespace for audit violations false
logStatsAdmission (alpha) Log stats for admission webhook false
logStatsAudit (alpha) Log stats metrics for the audit run false
enableK8sNativeValidation Enable the K8s Native Validating driver to allow constraint templates to use rules written in VAP-style CEL true
defaultK8sNativeValidationFailurePolicy (beta) Failure policy to use when a K8sNativeValidation source omits failurePolicy. Allowed values are Fail or Ignore. This is separate from validatingWebhookFailurePolicy, which controls the Kubernetes webhook configuration failure policy. Fail
defaultCreateVAPForTemplates (beta) Create VAP resource for template containing VAP-style CEL source. Allowed values are false: do not create Validating Admission Policy unless generateVAP: true is set on constraint template explicitly, true: create Validating Admission Policy unless generateVAP: false is set on constraint template explicitly. true
defaultCreateVAPBindingForConstraints (beta) Create VAPBinding resource for constraint of the template containing VAP-style CEL source. Allowed values are false: do not create Validating Admission Policy Binding, true: create Validating Admission Policy Binding. true
vapGenerationMode (alpha) VAP generation topology. template creates one parameterized VAP per ConstraintTemplate. constraint creates one VAP with inlined parameters per Constraint. template
defaultWaitForVAPBGeneration (beta) Wait time in seconds before generating a ValidatingAdmissionPolicyBinding after a constraint CRD is created. 30
auditEventsInvolvedNamespace Emit audit events for each violation in the involved objects namespace, the default (false) generates events in the namespace Gatekeeper is installed in. Audit events from cluster-scoped resources will continue to generate events in the namespace that Gatekeeper is installed in false
admissionEventsInvolvedNamespace Emit admission events for each violation in the involved objects namespace, the default (false) generates events in the namespace Gatekeeper is installed in. Admission events from cluster-scoped resources will continue to generate events in the namespace that Gatekeeper is installed in false
logDenies Log detailed info on each deny false
logLevel Minimum log level INFO
image.pullPolicy The image pull policy IfNotPresent
image.repository Image repository openpolicyagent/gatekeeper
image.release The image release tag to use Current release version: v3.24.0-beta.0
image.pullSecrets Specify an array of imagePullSecrets []
resources The resource request/limits for the container image limits: 1 CPU, 512Mi, requests: 100mCPU, 256Mi
nodeSelector The node selector to use for pod scheduling kubernetes.io/os: linux
controllerManager.containerName Container name for controller manager manager
controllerManager.podLabels The labels to add to the controller manager pod {}
controllerManager.affinity The node affinity to use for controller manager pod scheduling {}
controllerManager.topologySpreadConstraints The topology spread constraints to use for controller manager pod scheduling []
controllerManager.tolerations The tolerations to use for controller manager pod scheduling []
controllerManager.healthPort Health port for controller manager 9090
controllerManager.port Webhook-server port for controller manager 8443
controllerManager.metricsPort Metrics port for controller manager 8888
controllerManager.readinessTimeout Timeout in seconds for the controller manager's readiness probe 1
controllerManager.livenessTimeout Timeout in seconds for the controller manager's liveness probe 1
controllerManager.logLevel The minimum log level for the controller manager, takes precedence over logLevel when specified null
controllerManager.priorityClassName Priority class name for controller manager system-cluster-critical
controllerManager.podSecurityContext Security context on pod level for controller manager {fsGroup: 999, suplementalGroups: [999]}
controllerManager.exemptNamespaces The exact namespaces to exempt by the admission webhook []
controllerManager.exemptNamespacePrefixes The namespace prefixes to exempt by the admission webhook []
controllerManager.hostNetwork Enables controllerManager to be deployed on hostNetwork false
controllerManager.dnsPolicy Set the dnsPolicy for controllerManager pods ClusterFirst
controllerManager.securityContext Security context applied on the container { "allowPrivilegeEscalation": false, "capabilities": "drop": [all], "readOnlyRootFilesystem": true, "runAsGroup": 999, "runAsNonRoot": true, "runAsUser": 1000 }
controllerManager.tlsMinVersion Set the minimum supported TLS version for validating and mutating webhook servers 1.3
controllerManager.extraRules Extra rules for the gatekeeper-manager-role Role []
controllerManager.networkPolicy.enabled Should a network policy for the controller manager be created false
controllerManager.networkPolicy.ingress Additional ingress rules to be added to the controller manager network policy {}
controllerManager.strategyType The strategy type to use for Controller Manager deployment RollingUpdate
controllerManager.strategyRollingUpdate RollingUpdate configuration for Controller Manager deployment {}
controllerManager.disableWebhookOperation Disable webhook operation for controller manager false
controllerManager.disableGenerateOperation Disable generate operation for controller manager true
audit.containerName Container name for audit manager
audit.podLabels The labels to add to the audit pod {}
audit.affinity The node affinity to use for audit pod scheduling {}
audit.topologySpreadConstraints The topology spread constraints to use for audit pod scheduling []
audit.tolerations The tolerations to use for audit pod scheduling []
audit.priorityClassName Priority class name for audit controller system-cluster-critical
audit.podSecurityContext Security context for audit on pod level {fsGroup: 999, suplementalGroups: [999]}
audit.hostNetwork Enables audit to be deployed on hostNetwork false
audit.dnsPolicy Set the dnsPolicy for audit pods ClusterFirst
audit.securityContext Security context applied on the container { "allowPrivilegeEscalation": false, "capabilities": "drop": [all], "readOnlyRootFilesystem": true, "runAsGroup": 999, "runAsNonRoot": true, "runAsUser": 1000 }
audit.healthPort Health port for audit 9090
audit.metricsPort Metrics port for audit 8888
audit.readinessTimeout Timeout in seconds for audit's readiness probe 1
audit.livenessTimeout Timeout in seconds for the audit's liveness probe 1
audit.logLevel The minimum log level for audit, takes precedence over logLevel when specified null
audit.disableGenerateOperation Disable generate operation for audit false
audit.disableAuditOperation Disable audit operation for audit false
audit.disableAuditSidecar Disable audit sidecar false
audit.disableStatusOperation Disable status operation for audit false
enableViolationExport (alpha) Enable exporting audit violations to external systems false
enableAdmissionViolationExport (alpha) Enable best-effort export of admission violations using the disk driver; Dapr and other backends are not supported false
admission.exportSidecar Optional sidecar that streams and removes completed admission disk segments from the audit export volume See values.yaml
admission.disableExportSidecar Disable the admission disk reader sidecar true
audit.connection (alpha) Connection name shared by audit and admission violation export audit-connection
audit.channel (alpha) Channel name shared by audit and admission violation export audit-channel
audit.exportVolume (alpha) Volume for audit pod to export violations. {"name":"tmp-violations","emptyDir":{}}
audit.exportVolumeMount.path (alpha) VolumeMount for audit pod manager container to export violations and sidecar container to read from. /tmp/violations
audit.exportSidecar (alpha) Sidecar container to read violations from disk. {"name":"reader","image":"ghcr.io/open-policy-agent/fake-reader:latest","imagePullPolicy":"Always","securityContext":{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsGroup":999,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"volumeMounts":[{"mountPath":"/tmp/violations","name":"tmp-violations"}]}
audit.exportConnection.path (alpha) Path for audit pod manager container to export violations and sidecar container to read from. Must be a child of volume mount path so the parent is writable. /tmp/violations/topics
audit.exportConnection.maxAuditResults (alpha) Maximum number of audit results that can be stored in the export path. 3
exportBackend (alpha) Backend to use for exporting violations, i.e. dapr, disk. ""
replicas The number of Gatekeeper replicas to deploy for the webhook 3
podAnnotations The annotations to add to the Gatekeeper pods container.seccomp.security.alpha.kubernetes.io/manager: runtime/default
podLabels The labels to add to the Gatekeeper pods {}
podCountLimit The maximum number of Gatekeeper pods to run 100
secretAnnotations The annotations to add to the Gatekeeper secrets {}
pdb.controllerManager.minAvailable The number of controller manager pods that must still be available after an eviction 1
service.type Service type ClusterIP
service.loadBalancerIP The IP address of LoadBalancer service ``
service.ipFamilyPolicy The IP family policy used for the service, for more information see Configure dual-stack. ``
service.ipFamilies List of IP families for the service. Can be "IPv4" and/or "IPv6" ``
service.healthzPort Service port to gatekeeper Webhook health port 9090
rbac.create Enable the creation of RBAC resources true
externalCertInjection.enabled Enable the injection of an external certificate. This disables automatic certificate generation and rotation false
externalCertInjection.secretName Name of secret for injected certificate gatekeeper-webhook-server-cert
externaldataProviderResponseCacheTTL TTL for the external data provider response cache. Specify the duration in 'h', 'm', or 's' for hours, minutes, or seconds respectively. 3m
commonAnnotations Additional annotations added to manager and audit Deployments only {}
extraVolumeMounts Extra volumeMounts injected into controller-manager and audit containers []
extraVolumes Extra volumes injected into controller-manager and audit pods []
controllerManager.serviceAccount.automountServiceAccountToken Whether to automount the service account token for controller-manager pods true
audit.serviceAccount.automountServiceAccountToken Whether to automount the service account token for audit pods true
syncVAPEnforcementScope (beta) Synchronize ValidatingAdmissionPolicy enforcement scope with Gatekeeper's admission validation scope. When enabled, VAP resources inherit match criteria, conditions, and namespace exclusions from Gatekeeper's webhook configuration, Config resource and exempt namespace flags. This ensures consistent policy enforcement between Gatekeeper and VAP but triggers constraint template reconciliation on scope changes in Config resource or webhook configuration. This flag is set to true by default. It is deprecated and will be removed in Gatekeeper v3.24. true
logLevelKey JSON key for the log level field. Only set when specified. Not set (default: level)
logLevelEncoder Encoder for the value of the log level field. Valid values: lower, capital, color, capitalcolor. Only set when specified. Not set (default: lower)
metricsAddr The address the metric endpoint binds to. Only set when specified. Not set (default: 0)
readinessRetries The number of resource ingestion attempts allowed before the resource is disregarded. A value of -1 will retry indefinitely. Only set when specified. Not set (default: 0)
enablePprof Enable pprof profiling. Only set when specified. Not set (default: false)
pprofPort Port for pprof profiling. Only set when specified. Not set (default: 6060)
disableEnforcementActionValidation Disable validation of the enforcementAction and scopedEnforcementActions field of a constraint. Only set when specified. Not set (default: false)
enableReferentialRules Enable referential rules. Set to false to disallow referential constraints. Referential constraints read objects other than the object-under-test and may be subject to race conditions. Only set when specified. Not set (default: true)
controllerManager.host The host address the webhook server listens on. Only set when specified. Not set (default: all addresses)
controllerManager.certDir The directory where certs are stored. Not set (default: /certs)
controllerManager.certServiceName The service name used to generate the TLS cert's hostname. Not set (default: gatekeeper-webhook-service)
controllerManager.clientCAName Name of the certificate authority bundle to authenticate the Kubernetes API server requests against. Only set when specified. Not set
controllerManager.clientCNName Expected CN name on the client certificate attached by apiserver in requests to the webhook. Only set when specified. Not set (default: kube-apiserver)
audit.apiCacheDir The directory where audit from api server cache are stored. Only set when specified. Not set (default: /tmp/audit)
shutdownDelay Time in seconds the controller runtime shutdown gets delayed after receiving a pod termination event. Prevents failing webhooks on pod shutdown. Only set when specified. Not set (default: 10)
enableRemoteCluster (alpha) Enable remote cluster mode where Gatekeeper operates against a target cluster specified via kubeconfig while running in the local cluster. Requires kubeconfig to also be set, and the kubeconfig file to be mounted via extraVolumes/extraVolumeMounts. Only set when specified. Not set (default: false)
kubeconfig Path to kubeconfig file inside the container for remote cluster mode. The file must be mounted into the container using extraVolumes and extraVolumeMounts. Only set when specified. Not set

Contributing Changes

Please refer
to Contributing to Helm Chart
for modifying the Helm chart.


Intro (`website/docs/intro.md`)


id: intro title: Introduction sidebar_label: Introduction slug: /

Goals

Every organization has policies. Some are essential to meet governance and legal requirements. Others help ensure adherence to best practices and institutional conventions. Attempting to ensure compliance manually would be error-prone and frustrating. Automating policy enforcement ensures consistency, lowers development latency through immediate feedback, and helps with agility by allowing developers to operate independently without sacrificing compliance.

Kubernetes allows decoupling policy decisions from the inner workings of the API Server by means of admission controller webhooks, which are executed whenever a resource is created, updated or deleted. Gatekeeper is a validating and mutating webhook that enforces CRD-based policies executed by Open Policy Agent, a policy engine for Cloud Native environments hosted by CNCF as a graduated project.

In addition to the admission scenario, Gatekeeper's audit functionality allows administrators to see what resources are currently violating any given policy.

Finally, Gatekeeper's engine is designed to be portable, allowing administrators to detect and reject non-compliant commits to an infrastructure-as-code system's source-of-truth, further strengthening compliance efforts and preventing bad state from slowing down the organization.

Validation and mutation

Gatekeeper supports both validation and mutation, but they are configured with different policy resources and have different effects:

Area Policy resources Effect
Validation ConstraintTemplate and Constraint resources Evaluates resources and can deny, warn, dry-run, audit, or report violations without changing the resource.
Mutation AssignMetadata, Assign, ModifySet, and AssignImage mutator resources Changes matching resources during admission before validation evaluates the final object.

Some concepts apply to both areas, such as match criteria and namespace exclusions. Other concepts are specific to validation constraints, including enforcementAction, audit results, and violation status.

Looking for sample policies?

Please visit Gatekeeper policy library to find a collection of sample policies.

How is Gatekeeper different from OPA?

Compared to using OPA with its sidecar kube-mgmt (aka Gatekeeper v1.0), Gatekeeper introduces the following functionality:

  • An extensible, parameterized policy library
  • Native Kubernetes CRDs for instantiating the policy library (aka "constraints")
  • Native Kubernetes CRDs for extending the policy library (aka "constraint templates")
  • Native Kubernetes CRDs for mutation support
  • Audit functionality
  • External data support

Backwards Compatibility (`docs/Backwards_Compatibility.md`)

Gatekeeper Backwards Compatibility

Background

The Gatekeeper project specified a formalization of its backwards compatibility
goals as one of its requirements for reaching its GA milestone. Specifying a
strong stance on backwards compatibility is necessary for establishing a degree
of confidence as to what features users can rely on as they seek to use the
product and avoid churn. This document assumes
Kubernetes' API compatibility guidelines
as the default stance and explores exceptions unique to the Gatekeeper project.

Exclusions

The following areas are out-of-scope for backwards compatibility as Gatekeeper
either has no control over their implementation, does not consider them to be
part of its API, or (in the case of security incidents), must be pragmatic in
weighing risk over convenience.

  • Rego/OPA
    • Gatekeeper does not control the design or implementation of Rego or OPA,
      rather it is a consumer of the project. As such, Gatekeeper will be
      subject to any backwards-incompatible changes to the Rego language.
  • Internal-only resources
    • These resources are intended only for consumption by the Gatekeeper
      project itself and are not intended for consumption by the user.
      These resources are subject to change at any time.
  • Constraint Template Library
    • The library is a consumer of Gatekeeper. As such, it is not bound by
      Gatekeeper's development constraints. That being said, the constraint
      template library should also establish its own philosophy on backwards
      compatibility. The documentation on
      constraint template versioning
      would be a good place to start.
  • Gatekeeper Manifest / Helm template
    • The Gatekeeper manifest and Helm templates are reference deployment
      configurations for Gatekeeper, they are not themselves part of
      Gatekeeper's API. The ability to cleanly upgrade Gatekeeper by re-applying
      the manifest without additional work is subject to breakage. Examples of
      breaking manifest changes include: adding, removing or modifying resource
      kinds, names and contents. Such changes should be highlighted in the
      release notes for the version that creates them. The Gatekeeper binary is
      intended to be written in such a way that it is always possible to upgrade
      from version N to version N+1 without downtime, but the specifics for how
      to do so are out-of-scope for the project.
  • Security Incidents
    • In the event that there is a security incident that is impossible to fix
      without breaking either the schema or the behavior of the Gatekeeper
      API, we would not maintain 100% backwards compatibility at the cost of
      compromising security. We would expect such changes to be rare and
      limited in scope to only what is necessary to address the underlying
      security issue.

Compatibility of the API

As of GA, Gatekeeper will adopt Kubernetes'
guidelines on compatibility
where possible for user-facing resources. The following section discusses places
where Gatekeeper intentionally deviates due to the above exceptions as well as
extra compatibility requirements it is adopting.

Constraint Templates

Because the constraint template embeds Rego source code as an opaque string
which is then interpreted by OPA under-the-hood, it is possible for the meaning
of that string to change between releases if Rego itself makes a
non-backwards-compatible release. However, the Rego interface that Gatekeeper
controls is expected to remain stable. This means:

  • The expected rule signature for violation[{"msg": "string", "details": <object>}]
    should be backwards compatible across releases.
  • The data provided to constraint template source code via the input variable
    should be backwards compatible.
  • The storage location of objects in data.inventory should be backwards
    compatible, though the backwards compatibility of the schema of the objects
    themselves is not controlled by Gatekeeper and is therefore out-of-scope.

Constraints

Because constraints are partially defined by constraint templates, it is always
possible for an applied change to a constraint template to break an existing
constraint. Constraints also have a transitive dependency on Rego, so any
backwards-incompatible changes to the language could lead to changes in the
behaviour of the constraint it backs.

However, absent any user-initiated changes to the backing constraint template
or changes to Rego, constraints should have a stable interface. Specifically
this means:

  • The parameters schema
  • The match schema
    • Note that there is an implicit assumption that the match schema is
      defined for Kubernetes. This will need to be accounted for if we pursue
      multi-target constraints per
      Expanding the Constraint Framework
  • enforcementAction

enforcementAction is an interesting beast. It is not an enum, though it looks
like one at first glance. It is an opaque string whose value is given meaning by
the particular enforcement point evaluating the constraint. Its behavior is:

  • the default value is "deny"
  • unknown values should be ignored

The upshot of these differences (particularly "unknown values should be ignored")
is that unlike enums, which have a bounded set of allowed values, the set of
valid enforcement actions is unbounded. This makes it more akin to a
union set.

Current Gatekeeper is deviating slightly from the enforcementAction spec by
defaulting to rejecting constraints with an unknown enforcement action (this
behavior can be disabled via the --disable-enforcementaction-validation flag).
This should be a non-issue as a missing constraint and an ignored constraint are
functionally equivalent. The reason for rejection is to make typos more visible
to the user. Ultimately, unrecognized values should be returned to the user as a
warning once it is possible for validating webhooks to do so.

Internal-Only Resources

The following resources are for intended for internal use only and are therefore
not subject to the backwards compatibility design:

  • ConstraintPodStatus
  • ConstraintTemplatePodStatus

Semantic Logging

Semantic logging
should be considered part of our API and subject to backwards compatibility
requirements.

Practical Effects

Project velocity

Adopting this stance on backwards compatibility will necessarily constrain
project velocity wherever its API is concerned. Because we are now attempting to
provide a stable API, changes to our API become either very expensive or
impossible to undo.

This means that any changes to non-alpha resources should be thoroughly vetted
before they are approved.

Contributions

Our backwards compatibility requirements should be made readily available to
contributors as part of our contributor guide. Reviewers should be on guard
against any changes that may affect backward compatibility.


Metrics (`docs/Metrics.md`)

This document has been moved to https://open-policy-agent.github.io/gatekeeper/website/docs/metrics.


RELEASE (`docs/RELEASE.md`)

Release Process

Overview

The release process consists of three phases: versioning, building, and publishing.

Versioning involves maintaining the following files:

  • Makefile - the Makefile contains a VERSION variable that defines the version of the project.
  • manager.yaml - the controller-manager deployment yaml contains the latest release tag image of the project.
  • gatekeeper.yaml - the gatekeeper.yaml contains all gatekeeper resources to be deployed to a cluster including the latest release tag image of the project.

The steps below explain how to update these files. In addition, the repository should be tagged with the semantic version identifying the release.

Building involves obtaining a copy of the repository and triggering a build as part of the GitHub Actions CI pipeline.

Publishing involves creating a release tag and creating a new Release on GitHub.

Cherry-picking

There is an optional script for cherry-picking PRs that should make the process easier.

Prerequisites:

  • hub binary is installed. If not, hub can be installed by go get github.com/github/hub.
  • Set GitHub username with export GITHUB_USER=<your GitHub username>
  • Set fork remote with export FORK_REMOTE=<your fork remote name, by default it is "origin">
  • Set upstream remote with export UPSTREAM_REMOTE=

Usage: ./third_party/k8s.io/kubernetes/hack/cherry_pick_pull.sh upstream/release-3.1 123
For example, this will cherry-pick PR #123 into upstream/release-3.1 branch and will create a PR for you.

You can also combine multiple PRs by separating them with spaces (./third_party/k8s.io/kubernetes/hack/cherry_pick_pull.sh upstream/release-3.1 123 456)

If you want to run the script with dry run mode, set DRY_RUN to true.

Cherry-pick script is copied over from https://github.com/kubernetes/kubernetes/blob/master/hack/cherry_pick_pull.sh. For more information, see https://github.com/kubernetes/community/blob/master/contributors/devel/sig-release/cherry-picks.md

Release Cadence

There are three version tracks for the Gatekeeper project:

  1. Beta (vX.Y.Z-beta.A)
  2. Release Candidate (vX.Y.Z-rc.B)
  3. Stable (vX.Y.Z)

After a minor release (vX.Y.0) has been released, we will immediately tag the master branch with vX.Y+1.Z-beta.0 in preparation for our next minor release (vX.Y+1.0). We will periodically increment the beta version (e.g. vX.Y+1.Z-beta.0 -> vX.Y+1.Z-beta.1) to give the users a chance to test bug fixes and experimental features we introduce to the master branch. However, we do not recommend using beta versions of Gatekeeper in a production environment.

Once we are ready to release the new minor version, we will create a release branch (release-X.Y+1) from the master branch and create a release candidate tag (vX.Y+1.Z-rc.0) based on the release branch. This allows us to isolate the release candidates in its own branch so that future pull requests that are being merged to the master branch will not cause any regressions. Unlike beta, we only increment our release candidates (e.g. vX.Y+1.Z-rc.0 -> vX.Y+1.Z-rc.1) when we decided to cherry-pick merged pull requests to the release branch. We also do not recommend using release candidates of Gatekeeper in a production environment.

Once we are confident in our latest release candidate, we will tag the release branch with a stable release (vX.Y+1.0), and the development cycle will start all over again. We recommend using stable releases of Gatekeeper in a production environment. We also encourage users to upgrade to the latest stable patch releases (vX.Y+1.Z) for bug fixes, security fixes, and performance improvements.

For patch releases (e.g. vX.Y.1), refer to the cherry-picking section on how to cherry-pick merged pull requests to an existing release branch.

Release Pull Requests

Before we cut a release, we need to create a release pull request against the HEAD of the release branch. Once it is merged, we will create the release tag on the latest commit. We have a GitHub Actions workflow that automates the creation of release pull requests.

  1. Click the "Actions" tab.

  2. Click the "create_release_pull_request" workflow.

  3. Click "Run workflow" and input the version you would like to create a release pull request for.

During the workflow, it will create the release manifests, promote the manifests from manifest_staging/charts and manifest_staging/deploy to charts and deploy folders respectively, and create the release pull request on behalf of the release author.

Additional Pre-Release Checklist

Before creating a release pull request, ensure the following steps are completed:

  1. Dependabot PRs

    • Ensure all Dependabot PRs are reviewed and merged. This keeps dependencies up to date and secure.
  2. Dependency Updates

    • Confirm that the following dependencies are updated to their latest stable versions or commits:
    • Reference the relevant repositories and check for new releases.
  3. Vulnerability Scanning

Building and releasing

  1. Once the release pull request is merged to master or release branch (`` below), tag that commit with release version and push tags to remote repository.
shell
git checkout <BRANCH NAME>
git pull origin <BRANCH NAME>
git tag -a <NEW VERSION> -m '<NEW VERSION>'
git push origin <NEW VERSION>
  1. Pushing the release tag will trigger GitHub Actions to trigger tagged-release job.
    This will build the openpolicyagent/gatekeeper image automatically, then publish the new release image tag and the latest image tag to the openpolicyagent/gatekeeper repository. Finally, verify step will run e2e tests to verify the newly released tag.

Publishing

  1. GitHub Action will create a new release, review and edit it at https://github.com/open-policy-agent/gatekeeper/releases

Additional Post-Release Checklist

After a major or minor release, review and release the following dependencies if they contain changes since their last release:

  • frameworks/constraint: If there are unreleased changes, cut a new release and tag the same commit ID that is referenced in Gatekeeper's go.mod with the next appropriate release tag.
  • cert-controller: If there are unreleased changes, cut a new release and tag the same commit ID that is referenced in Gatekeeper's go.mod with the next appropriate release tag.

Release Management (`docs/Release_Management.md`)

Release Management

Overview

This document describes Gatekeeper project release management, which includes release versioning, supported releases, and supported upgrades.

Legend

  • X.Y.Z refers to the version (git tag) of Gatekeeper that is released. This is the version of the Gatekeeper image and the Chart version.
  • Breaking changes refer to schema changes, flag changes, and behavior changes of Gatekeeper that may require a clean installation during upgrade, and it may introduce changes that could break backward compatibility.
  • Milestone should be designed to include feature sets to accommodate 3 months release cycles including test gates. GitHub's milestones are used by maintainers to manage each release. PRs and Issues for each release should be created as part of a corresponding milestone.
  • Patch releases refer to applicable fixes, including security fixes, may be backported to support releases, depending on severity and feasibility.
  • Test gates should include soak tests and upgrade tests from the last minor version.

Release Versioning

All releases will be of the form vX.Y.Z where X is the major version, Y is the minor version and Z is the patch version. This project strictly follows semantic versioning.

The rest of the doc will cover the release process for the following kinds of releases:

Major Releases

No plan to move to 4.0.0 unless there is a major design change like an incompatible API change in the project

Minor Releases

  • X.Y.0-alpha.W, W >= 0 (Branch: master)
    • Released as needed before we cut a beta X.Y release
    • Alpha release, cut from master branch
  • X.Y.0-beta.W, W >= 0 (Branch: master)
    • Released as needed before we cut a stable X.Y release
    • More stable than the alpha release to signal users to test things out
    • Beta release, cut from master branch
  • X.Y.0-rc.W, W >= 0 (Branch: release-X.Y)
    • Released as needed before we cut a stable X.Y release
    • Soak for a total of ~2 weeks before cutting a stable release
    • Release candidate release, cut from release-X.Y branch
  • X.Y.0 (Branch: release-X.Y)
    • Released every ~3 months
    • Stable release, cut from release-X.Y branch when X.Y milestone is complete

Patch Releases

  • Patch Releases X.Y.Z, Z > 0 (Branch: release-X.Y, only cut when a patch is needed)
    • No breaking changes
    • Applicable fixes, including security fixes, may be cherry-picked from master into the latest supported minor release-X.Y branches.
    • Patch release, cut from a release-X.Y branch

Supported Releases

Applicable fixes, including security fixes, may be cherry-picked into the release branch, depending on severity and feasibility. Patch releases are cut from that branch as needed.

We expect users to stay reasonably up-to-date with the versions of Gatekeeper they use in production, but understand that it may take time to upgrade. We expect users to be running approximately the latest patch release of a given minor release and encourage users to upgrade as soon as possible.

We expect to "support" n (current) and n-1 major.minor releases. "Support" means we expect users to be running that version in production. For example, when v3.3.0 comes out, v3.1.x will no longer be supported for patches, and we encourage users to upgrade to a supported version as soon as possible.

Supported Kubernetes Versions

Gatekeeper is assumed to be compatible with the current Kubernetes Supported Versions per Kubernetes Supported Versions policy.

For example, if Gatekeeper supported versions are v3.7 and v3.8, and Kubernetes supported versions are v1.22, v1.23, v1.24, then all supported Gatekeeper versions (v3.7, v3.8) are assumed to be compatible with all supported Kubernetes versions (v1.22, v1.23, v1.24). If Kubernetes v1.25 is released later, then Gatekeeper v3.7 and v3.8 will be assumed to be compatible with v1.25 if those Gatekeeper versions are still supported at that time.

If you choose to use Gatekeeper with a version of Kubernetes that it does not support, you are using it at your own risk.

Upgrades

Users should be able to run both X.Y and X.Y + 1 simultaneously in order to support gradual rollouts.

Acknowledgement

This document builds on the ideas and implementations of release processes from projects like Kubernetes and Helm.


Deprecated Docs Ingress Host Conflict (`deprecated/docs/ingress-host-conflict.md`)

gatekeeper

Kubernetes compliance is enforced at the “runtime” via tools such as network policy and pod security policy. gatekeeper extends the compliance enforcement at “create” event not at “run“ event, some of the examples are "Minimum replica count enforcement", "White listed/ black listed registries", "not allowing conflicting hosts for ingresses". Kubernetes allows decoupling complex logic such as policy decision from the inner working of API Server by means of "admission controllers”. Admission control is a custom logic executed by a webhook. Kubernetes policy controller is a mutating and a validating webhook which gets called for matching Kubernetes API server requests by the admission controller to enforce semantic validation of objects during create, update, and delete operations. It uses Open Policy Agent (OPA) is a policy engine for Cloud Native environments hosted by CNCF as a sandbox level project.

The administrator of the cluster defines the policy which is enforced by gatekeeper. There are two type of policies namely validation e.g. white listed registries and mutation e.g. annotating objects created in a namespace.

Lets lets look at the example below which implements a validation policy to ensure Ingress hostnames must be unique across Namespaces.

deploy `gatekeeper' on a Kubernetes cluster

Prerequisites are that you have a Kubernetes cluster
To implement admission control rules that validate Kubernetes resources during create, update, and delete operations, you must enable the ValidatingAdmissionWebhook when the Kubernetes API server is started. the admission controller is included in the recommended set of admission controllers to enable

1. create opa namespace

bash
kubectl create ns opa

2. create tls secret for `gatekeeper`

bash
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -days 100000 -out ca.crt -subj "/CN=admission_ca"
cat >server.conf <<EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, serverAuth
EOF
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/CN=opa.opa.svc" -config server.conf
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 100000 -extensions v3_req -extfile server.conf

kubectl -n opa create secret tls opa-server --cert=./server.crt --key=./server.key

kubectl apply -n opa -f ./opa.yaml

3. deploy default kubernetes policies

bash
cat > ./matches.rego <<EOF
package k8s
import data.kubernetes

matches[[kind, namespace, name, resource]] {
    resource := kubernetes[kind][namespace][name]
}
EOF
kubectl -n opa create configmap kubernetes-matches --from-file=./matches.rego

4. enable webhook configuration

bash
cat > ./validating-webhook-configuration.yaml <<EOF
kind: ValidatingWebhookConfiguration
apiVersion: admissionregistration.k8s.io/v1beta1
metadata:
  name: validating.gatekeeper
webhooks:
  - name: validating.webhook.gatekeeper
    rules:
      - operations: ["CREATE", "UPDATE"]
        apiGroups: ["*"]
        apiVersions: ["*"]
        resources: ["*"]
    clientConfig:
      caBundle: $(cat ./ca.crt | base64 | tr -d '\n')
      service:
        namespace: opa
        name: opa
        path: "/v1/validate"
EOF

kubectl -n opa apply -f ./validating-webhook-configuration.yaml

apply the ingress conflict policy definition

The policy is defined in Rego a policy language used by the Open Policy Agent. Each validation policy is a deny rule. In this case, there is a violation if any two ingresses in different namespaces have the same host.

Store the policy in Kubernetes as a ConfigMap. This is automatically uploaded to the policy engine.

bash
cat > ./ingress-conflict.rego <<EOF
deny[{
    "id": "ingress-conflict",
    "resource": {"kind": "ingresses", "namespace": namespace, "name": name},
    "message": "ingress host conflicts with an existing ingress",
}] {
    # gets the ingress matching the ingress that needs to be validated
    matches[["ingresses", namespace, name, matched_ingress]]
    # gets any other ingress which is already a part of the cluster
    matches[["ingresses", other_ns, other_name, other_ingress]]
    # filters to ingresses in other namespaces
    namespace != other_ns
    other_ingress.spec.rules[_].host == matched_ingress.spec.rules[_].host
}
EOF
kubectl create configmap ingress-conflict --from-file=ingress-conflict.rego

create ingress in default namespace

This operation will succeed as there is no conflicting ingress host in any other namespace

bash
cat > ./ingress-host.yaml <<EOF
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-host
spec:
  rules:
  - host: acmecorp.com
    http:
      paths:
      - backend:
          serviceName: nginx
          servicePort: 80
EOF
kubectl apply -f ./ingress-host.yaml

Create ingress with same host in another namespace (This should fail)

Create a test namespace

bash
kubectl create ns test

Try create a

bash
kubectl -n test apply -f ./ingress-host.yaml

This is the error message returned by gatekeeper

bash
Error from server: error when creating "ingress-host.yaml": admission webhook "validating.webhook.gatekeeper" denied the request: [
  {
    "id": "ingress-conflict",
    "message": "ingress host conflicts with an existing ingress"
  }

Summary

If you have reached this stage you have successfully created a policy for your cluster using gatekeeper.


Deprecated Docs Post (`deprecated/docs/post.md`)

Kubernetes clusters governance made simple

Overview

Every organization has some rules. Some of these are essential to meet governance, and legal requirements and other are based on learning from past experience and not repeating the same mistakes. These decisions cannot tolerate human response time as they need near a real-time action. Services that are policy enabled to make the organization agile and are essential for long-term success as they are more adaptable as violations and conflicts can be discovered consistently as they are not prone to human error.

Kubernetes compliance is enforced at the “runtime” via tools such as network policy and pod security policy. gatekeeper extends the compliance enforcement at “create” event not at “run“ event. For example, a kubernetes service could answer questions like :

  • Can we whitelist / blacklist registries.
  • Not allow conflicting hosts for ingresses.
  • Label objects based on a user from a department.
  • What are the policies that my cluster is violating.

We are happy to announce gatekeeper which allows enforcing custom semantic rules on objects during create, update and delete operations without recompiling or reconfiguring the Kubernetes API server. The controller is backed by the Open Policy Agent(OPA) which is a lightweight, general purpose policy engine for Cloud-Native environments.

How policy works with `gatekeeper`

Policy-enablement empowers users to read, write, and manage these rules without needing specialized development. Policy rules can be selected from a library of pre-created rules or can be added to the library using Rego using OPA’s native query language Rego following a standard template to define validation and mutation policies. Below is one example of a validation policy:

python
deny[{
    "id": "ingress-conflict",
    "resource": {"kind": "ingresses", "namespace": namespace, "name": name},
    "resolution": {"message": "ingress host conflicts with an existing ingress"},
}] {
    # gets the ingress matching the ingress that needs to be validated
    matches[["ingresses", namespace, name, matched_ingress]]
    # gets any other ingress which is already a part of the cluster
    matches[["ingresses", other_ns, other_name, other_ingress]]
    # filters to ingresses in other namespaces
    namespace != other_ns
    other_ingress.spec.rules[_].host == matched_ingress.spec.rules[_].host
}

Architecture of `gatekeeper`

Kubernetes allows decoupling complex logic such as policy decision from the inner working of API Server by means of "admission controllers”. Admission control is a custom logic executed by a webhook. Kubernetes policy controller is a mutating and a validating webhook which gets called for matching Kubernetes API server requests by the admission controller to enforce semantic validation of objects during create, update, and delete operations. It uses Open Policy Agent (OPA) is a policy engine for Cloud-Native environments hosted by CNCF as a sandbox level project.

They are following components that for gatekeeper

  • Admission controller Webhook: This is the service that receives CRUD requests from the API server

  • OPA service and Kubernetes management pod: The OPA service is a generic policy engine which evaluates configured policy rules on new objects and eventual consistent state of current objects synced using the Kubernetes management pod.

What's next

We are on a journey to make policy enforcement on Kubernetes cluster simple and reliable. Try out the gatekeeper, contribute to writing policies and share your scenarios and ideas! To be sure, there is a lot more to do here, and we look forward to working together to get it done!

We’ll see you on GitHub!


Design Admission Violation Export (`docs/design/admission-violation-export.md`)

Admission Violation Export Design

Status: Proposed
Tracking Issue: #2798
Created: 2026-07-29
Last Updated: 2026-07-30

Summary

Gatekeeper can export violations found during admission so that denied, warned,
and dry-run requests can be consumed outside the Kubernetes admission response.
Admission export reuses the existing Connection resource, export system, disk
driver configuration, channel, path, and volume used by audit export.

The admission webhook is latency-sensitive. Export is therefore best-effort and
asynchronous: the webhook encodes each violation and attempts a non-blocking send
to a bounded in-memory queue. A controller-runtime runnable drains the queue and
publishes records through the existing export system. Backend latency or failure
cannot change the admission decision, and the webhook never waits for backend I/O
or queue capacity. JSON encoding and the non-blocking enqueue attempt still run
on the admission path.

The supported alpha deployment uses the disk driver. Admission records are
written as durable JSON Lines segments with a separate filename prefix and
lifecycle from audit files. Audit and webhook publishers report health
independently in ConnectionPodStatus.

This design builds on the following existing designs:

Motivation

Audit export observes resources that already exist in the cluster. It cannot
fully represent admission-time policy outcomes:

  • A denied object is never persisted and cannot later be found by audit.
  • Admission warnings and dry-run violations occur in the request path.
  • Consumers may need request identity, operation, requested resource, and
    subresource information that is not available from a stored object.

Calling an export backend synchronously from the validating webhook would couple
cluster write availability and latency to the backend. A slow disk, unavailable
connection, or blocked consumer could delay every matching Kubernetes request.
Admission export needs a bounded failure domain that preserves admission
availability while making loss observable.

Goals

  • Export one record for each valid constraint violation produced by the
    validating webhook.
  • Include admission-request metadata needed to understand violations for objects
    that may never be persisted.
  • Keep backend I/O off the admission response path.
  • Bound webhook memory and shutdown work.
  • Reuse the existing Connection, export system, audit route, disk path, and
    volume rather than expose a second transport configuration.
  • Allow audit and admission files to coexist without either reader or retention
    policy consuming the other source's files.
  • Report audit and webhook publishing health independently per pod.
  • Surface overload and loss through metrics and rate-limited logs.
  • Recover complete records from abandoned disk segments after a crash.

Non-Goals

  • Exactly-once or at-least-once delivery.
  • Retrying an individual failed admission publish.
  • Persisting the in-memory webhook queue across pod restarts.
  • Blocking or changing an admission response because export failed.
  • Supporting admission export through Dapr in the alpha Helm configuration.
  • Multiple admission Connections or channels.
  • Admission-specific public spool tuning in the alpha API.
  • Coordinating one shared writable disk directory across Gatekeeper pods.
  • Providing a production log shipper. The included reader sidecar is a
    demonstration and end-to-end test utility.

Terminology

  • Admission violation: a valid constraint result evaluated by the validating
    webhook, regardless of whether its enforcement action denies, warns, or
    records a dry-run result.
  • Producer: the webhook request goroutine that constructs and attempts to
    enqueue a violation record.
  • Publisher: the single background runnable that removes queued records and
    calls the export system.
  • Ready segment: a finalized admission .log file available to a reader.
  • Open segment: a locked admission .open file receiving JSON Lines records.
  • Source: the component reporting publish status, currently audit or
    webhook.

Configuration

Admission export is disabled by default and enabled independently from audit
export:

text
--enable-admission-violation-export=true

Admission export deliberately reuses the audit route:

text
--audit-connection=audit-connection
--audit-channel=audit-channel

Only one Connection and one channel are supported for this alpha design. The
Helm configuration is:

yaml
enableAdmissionViolationExport: true
exportBackend: disk
audit:
  connection: audit-connection
  channel: audit-channel
  exportConnection:
    path: /tmp/violations/topics
    maxAuditResults: 3
  exportVolumeMount:
    path: /tmp/violations
  exportVolume:
    name: tmp-violations
    emptyDir: {}
admission:
  disableExportSidecar: true

The chart uses one top-level exportBackend for both audit and admission
export; there is no admission-specific backend setting. The chart creates the
shared disk Connection, mounts the audit export volume in each
controller-manager pod, and can optionally add the demonstration admission
reader sidecar when exportBackend is disk. Helm rendering fails when
admission export is enabled with any other backend. Operators deploying without
the chart remain responsible for the Connection and storage wiring.

Helm source changes belong under cmd/build/helmify; make manifests generates
manifest_staging. Root charts and deploy directories are release outputs
and are not source files for this feature.

Architecture

mermaid
flowchart LR
    A[Kubernetes admission request] --> B[Constraint evaluation]
    B --> C[Admission response processing]
    B --> D{Reserve message slot}
    D -->|accepted| E[Build shared request fields lazily]
    E --> F[Build and encode one ExportMsg]
    F --> G{Reserve bytes and enqueue}
    D -->|full| H[Drop metric and rate-limited log]
    G -->|accepted| I[Per-pod in-memory queue]
    G -->|rejected| H
    I --> J[Collect up to 64 ready records]
    J --> K[PublishBatch or sequential Publish fallback]
    K --> L[Disk Connection]
    L --> M[Locked admission .open segment]
    M --> N[Atomic rename to admission-*.log]
    C --> O[Allow, warn, or deny response]

The export path branches after evaluation but before the admission response is
returned. TryExport reserves count capacity before invoking its message
builder, has no error return, and does not perform backend I/O. The admission
response is derived from the same validated enforcement actions, but its outcome
is independent of export success.

Manager Wiring

When admission export is enabled, webhook setup:

  1. Requires an export system and a function that returns the current pod.
  2. Constructs a connectionStatusReporter using the API reader, client, scheme,
    and pod getter.
  3. Constructs a queuedAdmissionViolationExporter using the shared Connection
    and channel.
  4. Adds the exporter to the controller-runtime manager as a runnable.
  5. Injects the same exporter into the validation handler.

The export Connection controller is installed when either audit or admission
export is enabled. It reconciles the Connection into the process-local export
system for each Gatekeeper pod that performs export.

Admission Message Construction

The handler validates a result, then passes a lazy builder to TryExport. If the
exporter reserves count capacity, the builder decodes request metadata and
constructs the request-level base message on first use. Later accepted results
reuse that base and copy it before adding constraint-specific fields. A full or
stopped queue therefore rejects the result without decoding request metadata or
building the record. Invalid enforcement actions and results without a
Constraint are not exported.

The JSON record uses ExportMsg. Common violation fields include:

json
{
  "id": "admission-request-uid",
  "eventType": "violation_admission",
  "group": "constraints.gatekeeper.sh",
  "version": "v1beta1",
  "kind": "K8sRequiredLabels",
  "name": "require-team-label",
  "message": "you must provide labels: team",
  "enforcementAction": "deny",
  "constraintAnnotations": {"owner": "platform"},
  "details": {"missingLabels": ["team"]},
  "resourceAPIVersion": "v1",
  "resourceKind": "Pod",
  "resourceNamespace": "default",
  "resourceName": "example",
  "resourceLabels": {"app": "example"},
  "timestamp": "2026-07-29T12:00:00.000000000Z",
  "operation": "CREATE",
  "requestResource": "pods",
  "requestUsername": "[email protected]",
  "requestUserUID": "uid",
  "requestUserGroups": ["developers"],
  "dryRun": false
}

The AdmissionReview UID is used as the record ID. A timestamp is used if the UID
is empty. The original requested resource and subresource are preferred when
Kubernetes equivalent-resource matching converted the request. Resource name and
namespace fall back to decoded object metadata when request fields are empty.
The kubectl.kubernetes.io/last-applied-configuration constraint annotation is
omitted because it may contain a large serialized object.
enforcementActions is emitted for scoped enforcement; ordinary deny, warn,
and dryrun records use only enforcementAction. Empty optional fields are
omitted from the encoded record.

A request can produce multiple records with the same request ID, one for each
valid constraint violation.

Queue Design

Each controller-manager pod owns one queue and one publisher. Queue limits are
private alpha constants:

Limit Value
Maximum in-progress or queued messages 1,024
Maximum encoded queued bytes 16 MiB
Maximum encoded JSON record 64 KiB
Maximum publish batch 64 messages
Status reporting interval 10 seconds
Unchanged healthy status heartbeat 1 minute
Status write timeout 2 seconds
Shutdown drain timeout 5 seconds
Repeated log interval 1 minute

The 64 KiB limit applies to the complete JSON object produced by
json.Marshal(ExportMsg), not only to message. Field names and JSON escaping,
policy-provided details, constraint annotations, resource labels, and request
identity all consume the same byte budget. KiB measures encoded bytes rather
than characters. This leaves room for normal policy and request metadata while
ensuring one user-influenced result cannot consume a disproportionate share of
the 16 MiB queue or disk spool. Records over the limit are dropped before
enqueue with reason message_too_large.

Producer Path

For each message, TryExport:

  1. Takes a read lock that prevents shutdown from starting during the enqueue.
  2. Atomically reserves one of 1,024 count slots before constructing the message.
  3. Rejects new messages after shutdown starts or when all count slots are held.
  4. Builds the message and computes a bounded lower estimate of its encoded size.
  5. Drops messages that cannot fit within 64 KiB, then marshals the remainder.
  6. Applies the exact 64 KiB encoded limit.
  7. Atomically reserves the encoded size against the 16 MiB byte limit.
  8. Attempts a non-blocking send to the buffered channel.
  9. On any failure, releases all count and byte reservations.

The non-blocking send is required: waiting for queue capacity would put backend
backpressure on the admission path. Count and byte limits are independent. The
count limit includes both builders in progress and queued records, bounding
concurrent construction and channel overhead. The byte limit protects retained
encoded data when records are large. The lower-bound preflight does not replace
the final marshal and exact length check; unknown custom values fall through so
encoding errors keep their normal classification.

Queue depth and byte gauges are approximate snapshots. Concurrent producers may
change one value between observations. Byte accounting can briefly include a
producer that reserved bytes but has not yet completed its channel send.

Publisher Path

Start is the single queue consumer. It drains up to 64 records that are already
available without waiting to fill a batch. A driver can implement the optional
source-neutral batch interface; other drivers retain the single-message
Publish fallback. Batch values use the same arbitrary message representations
as Publish, allowing admission to pass pre-encoded json.RawMessage values and
future audit batching to pass typed ExportMsg values. Calls remain serialized,
which preserves file ordering and per-Connection driver state. On dequeue, queue
bytes and gauges are reduced before backend I/O; gauges therefore represent
messages waiting in the queue, not a batch currently in flight.

Publishing is attempted once. A backend error is logged, counted, and aggregated
for status, but the individual message is not retried.

Shutdown

Manager cancellation is given priority over a continuously ready queue. If
cancellation occurs while a normal publish batch is already in flight, that
batch has already been removed from queue accounting. The publisher stops
waiting when its context is canceled and records publish errors for the batch;
it does not requeue those records. A backend call that ignores cancellation may
finish later in its isolated goroutine, but the late result is not applied.

Shutdown then handles records that remain queued:

  1. Acquires the queue lock and rejects future enqueues.
  2. Creates a fresh background context with a five-second deadline because the
    manager context is already canceled.
  3. Publishes any message selected concurrently with cancellation.
  4. Drains queued messages until empty or the deadline expires.
  5. Counts remaining messages as dropped with reason shutdown.
  6. Flushes final publishing status with a two-second timeout.

The runnable returns nil; backend and status failures are operational state,
not manager-fatal errors. The chart's 60-second pod termination grace period is
longer than the bounded drain and final status flush, but SIGKILL, OOM
termination, and node failure bypass this graceful path. Those events lose all
queued and in-flight records. Disk records already appended and synced can be
recovered only if the volume survives.

Export Routing

System maps a Connection name to a driver. A single ready record uses:

text
Publish(context, connectionName, channel, encodedMessage)

A ready group of two to 64 records uses the optional batch extension:

text
PublishBatch(context, connectionName, channel, []any{json.RawMessage, ...})

System.PublishBatch calls BatchDriver.PublishBatch when the selected driver
implements it. Otherwise, it invokes Publish sequentially with each original
value. The returned error slice corresponds one-to-one with input records in
input order. The batch contract is source-neutral so future audit batching can
pass typed ExportMsg values, but the current webhook producer supplies only
pre-encoded admission json.RawMessage values and the disk batch path accepts
only violation_admission records.

The generic export system does not know whether the message came from audit or
the webhook. The disk driver decodes only an eventType header from raw
admission messages. violation_admission selects the admission JSON Lines path;
audit control and violation messages retain the existing audit file path.

This keeps source policy out of the transport abstraction. The supported alpha
configuration restricts admission export to disk through Helm validation even
though the generic export system remains driver-agnostic.

Disk Format and Lifecycle

Audit and admission files share a Connection directory and channel directory but
use distinct protocols:

Source Open form Ready form
Audit <audit-run-id>.txt <audit-run-id>.log
Admission admission--<random>.open admission--<random>.log

The admission timestamp has nanosecond precision and the random suffix contains
eight random bytes encoded as hexadecimal. Files are created with O_EXCL and
O_NOFOLLOW, then locked with a non-blocking exclusive flock. Topic and audit
path segments are validated to prevent traversal. Directories and files use
group-readable/writeable permissions (0770 and 0660).

Write Path

Before appending a batch, the driver:

  1. Validates the topic.
  2. Checks each record against the same 64 KiB complete-record limit as the queue
    and the 1 MiB segment limit.
  3. Checks cached ready-file accounting while reserving room for the complete
    batch, scanning completed segments only when accounting is uninitialized or
    a retention limit requires cleanup.
  4. Checks filesystem blocks available to the current user and preserves a 16 MiB
    free-space reserve in addition to the complete batch.
  5. Rotates the current segment before a write when adding the record would
    exceed a rotation limit.
  6. Appends each encoded JSON record plus one newline.
  7. Calls fsync once for each segment touched by the batch.

A short or partial write is rolled back to the previous complete boundary. If
rollback fails, the segment is marked poisoned and later recovery publishes only
complete newline-delimited records.

Rotation

A segment rotates when any condition is met:

Limit Value
Segment bytes 1 MiB
Segment records 1,000
Segment age 1 minute

A timer rotates idle segments without waiting for another admission request. An
empty segment is closed and removed. A non-empty segment is synced, unlocked,
closed, and atomically renamed from .open to .log. Failed rotation is retried
after 30 seconds without requiring new traffic. Timer callbacks include the
expected topic and open path so stale callbacks cannot rotate a newer stream.

Retention

Admission retention is independent from audit retention. maxAuditResults
applies only to audit run files. Private admission limits are:

Limit Value
Ready segments 20
Total admission bytes, including open stream and pending record 20 MiB
Ready segment maximum age 24 hours
Minimum filesystem free-space reserve 16 MiB

Cleanup takes one snapshot of all completed admission segments across topic
directories, sorts candidates globally by modification time and then path, and
removes the oldest files until age, count, and byte constraints all hold. One
retention-cleanup invocation removes at most 256 ready segments. Recovery may
also remove abandoned empty .open files and .deleting remnants before
retention runs. Directory reads use batches of 128 entries while still
exhausting every directory.

The Connection caches ready-segment count, bytes, and oldest modification time
from recovery or cleanup. Appends to an unchanged open segment use that cache;
rotation and recovery invalidate it before the next cleanup snapshot. This
avoids a directory scan for each record while preserving count, byte, and TTL
enforcement.

These limits bound the unconsumed backlog; they do not guarantee 24-hour
retention. Count or byte pressure can remove a ready segment before it reaches
the maximum age. If one-minute age rotation is the limiting condition, 20 ready
segments represent roughly 20 minutes of backlog; segment byte or record limits
can rotate files more quickly. A healthy reader removes completed segments after
consumption, so cumulative throughput can exceed the spool size as long as the
reader keeps up. The demonstration reader logs every complete record and deletes
the segment after a successful read; it is not a durable archive.

A reader holds an exclusive lock while consuming a ready file. Cleanup takes the
same lock non-blockingly; if the oldest required file is being read, publishing
receives backpressure instead of deleting a file under the reader. Before
unlinking, cleanup renames the file to .deleting so another scan cannot select
it.

The byte reservation used during pre-write cleanup is accounting for the pending
JSON Lines record; it is not filesystem preallocation. The separate free-space
check enforces actual filesystem headroom.

Recovery

On Connection creation, recovery scans channel directories once. Recurring
cleanup starts only when recovery finds admission artifacts or the first
admission record opens a stream, and stops when no artifacts remain:

  • Locked .open files are treated as live and left untouched.
  • Abandoned .open files are locked, limited to the maximum segment size,
    truncated to their last complete newline, synced, and renamed to a recovered
    .log file.
  • Files with no complete record are removed.
  • .deleting remnants are removed.
  • Retention is applied after recovery.

Connection Lifecycle and Cleanup

The disk driver serializes operations per Connection while a global mutex guards
Connection maps, path reservations, and failed cleanup state. Paths being removed
or migrated are reserved so a concurrent create/update cannot recreate or delete
the same directory.

Connection close attempts to release audit and admission resources even if one
fails. A failed close moves state to a bounded retry map. Cleanup retries use
exponential backoff, enforce retry and TTL limits, and run slow filesystem work
outside the global mutex. State that still owns an open descriptor is retained
regardless of retry count or TTL because dropping it would lose the only cleanup
owner. New disk Connections are rejected while 32 failed Connection states are
retained. Failures from Connections that were already open can temporarily take
the retry map above that threshold because descriptor-owning state cannot be
safely discarded.

Status API

ConnectionPodStatus is written per Gatekeeper pod and copied into
Connection.status.byPod. The alpha status separates Connection reconciliation
errors from source-owned publishing state:

yaml
status:
  byPod:
  - id: gatekeeper-controller-manager-abc
    connectionUID: 00000000-0000-0000-0000-000000000000
    operations:
    - webhook
    observedGeneration: 3
    connectionErrors: []
    publishStatuses:
    - source: webhook
      active: true
      lastAttemptTime: "2026-07-29T12:00:00Z"
      lastSuccessTime: "2026-07-29T12:00:00Z"
      errors: []

connectionErrors reports Connection create/update failures. publishStatuses
is a list-map keyed by source, with a maximum of two entries: audit and
webhook. Each source replaces only its own entry, preventing a successful audit
run from clearing a webhook failure or vice versa.

active means the source completed at least one publish in the latest reported
window. lastAttemptTime and lastSuccessTime distinguish current traffic from
stale status. A source with no new publish attempt leaves its prior entry
unchanged.

Status updates retry Kubernetes conflicts. Each retry reads the latest
Connection and ConnectionPodStatus, then merges only the reporting source. A
Connection generation change clears old source status before accepting the first
report for the new generation.

Connection and publish error arrays are limited to 500 entries. Publish failures
are coalesced by the text before the first colon. The first 499 distinct classes
are retained; the final entry reports additional publish error classes omitted.
The stored message is the coalesced class key and is not otherwise
length-truncated.

Webhook publishing results are accumulated independently from the publisher. A
dedicated reporter checks every ten seconds, reports errors and health
transitions, and coalesces unchanged healthy traffic to a one-minute heartbeat.
The API call never blocks queue publishing. Failed status writes merge their
snapshot back with newer results for a later retry. Publish-only status updates
do not trigger export Connection reconciliation. Audit reports its source entry
at the end of an audit run.

Observability

Metrics

Admission export adds the following OpenTelemetry instruments. Exporters expose
them with the normal Gatekeeper metric prefix.

Instrument Type Meaning
admission_export_queued_count monotonic counter Records accepted by the in-memory queue
admission_export_queue_full_count monotonic counter Enqueue rejection due to count or byte capacity
admission_export_published_count monotonic counter Successful backend publishes
admission_export_publish_error_count monotonic counter Backend publish errors
admission_export_dropped_count{reason} monotonic counter Records discarded before successful publishing
admission_export_queue_depth observable gauge Current messages waiting in the channel
admission_export_queue_bytes observable gauge Current encoded bytes reserved by queued producers

Drop reasons are marshal_error, message_too_large, queue_bytes_full,
queue_full, and shutdown. Backend publish errors increment the publish-error
counter, not the dropped counter.

Gauges are updated after successful enqueue, failed channel enqueue rollback,
dequeue, and shutdown draining. The observable callbacks read atomics when the
metrics SDK collects. Queue depth excludes the message currently being
published. Gauges are advisory snapshots and may reflect slightly different
instants under concurrent producers.

Logging

Drops and publish/status failures are logged at most once per minute for each log
class. Metrics are the source for sustained-loss alerting; rate limiting prevents
an unavailable backend from flooding webhook logs.

Security and Privacy

Admission records may contain usernames, user UIDs, groups, resource labels,
constraint annotations, policy-provided details, and messages influenced by user
input. The export destination is security-sensitive and must be protected like
admission and audit logs.

The disk implementation:

  • Rejects empty, absolute, nested, and traversal path segments.
  • Uses O_NOFOLLOW and rejects symlink topic directories and ready files.
  • Uses exclusive file creation and file locks.
  • Bounds record, segment, spool, queue, and each retention-cleanup invocation,
    and rejects new Connections while the failed-state threshold is reached.
  • Preserves filesystem free space before accepting writes.
  • Avoids logging full exported records in the manager.

The default emptyDir volume isolates each pod but is ephemeral. Durable storage
must provide a separate directory or volume per pod. Multiple pods must not write
to one shared directory because Connection removal owns and deletes its configured
path.

Failure Semantics

Failure Behavior User signal
JSON marshal failure Drop before enqueue Drop counter and rate-limited log
Encoded JSON record over 64 KiB Drop before enqueue Drop counter and rate-limited log
Queue count or byte limit Drop without blocking admission Queue/drop counters, gauges, log
Connection unavailable Single publish fails; no message retry Publish-error counter, log, webhook source status
Disk full/reserve reached Single publish fails Publish-error counter, log, webhook source status
Reader locks required oldest file Publish backpressures through an error, not admission latency Publish error and status
Partial disk write Roll back; poison and recover if rollback fails Publish error; complete records preserved
Rotation failure Retain stream and schedule retry Log and publish error where applicable
Graceful shutdown with queued records Attempt queued records for up to five seconds, then drop the remainder shutdown drop counter and rate-limited log
Cancellation with a batch already in flight Stop waiting, report publish errors, and do not requeue; an uncancelable backend call may finish after its result is no longer observed Publish-error counter, log, webhook source status
SIGKILL, OOM termination, or node failure Lose queued and in-flight records because no drain runs Counters reset with process; no per-record signal survives
Container crash with surviving volume Lose the in-memory queue; recover complete open-file records already written to disk Recovered ready file
Status conflict Re-read and retry merge No cross-source overwrite
Status write failure Retain accumulated reporting window Rate-limited log; retry next interval/shutdown

Admission allow/deny/warn behavior is unchanged in every export failure case.

Compatibility and Rollout

The feature and Connection API are alpha. Admission export is opt-in and defaults
to disabled. Audit export remains independently controlled by
--enable-violation-export.

With the default emptyDir, replacing the pod removes its spool. Recovery covers
process or container restart only when the configured volume and files remain
available.

This design changes the alpha Connection status shape by replacing aggregate
active and errors fields with connectionErrors and source-specific
publishStatuses. Status is controller-owned and reconstructed; clients of the
alpha status must update their queries. During a rolling upgrade, status may be
temporarily absent or incomplete until each source publishes for the current
Connection generation.

The message schema is additive relative to audit ExportMsg; admission-only
fields are omitted from audit records. Audit file names, content, and retention
remain separate and unchanged. Admission-specific limits remain private while
operational experience is collected.

Alternatives Considered

Synchronous Backend Publishing

Rejected because backend latency and availability would become part of admission
latency and cluster write availability.

Blocking Queue Enqueue

Rejected because a full queue would backpressure Kubernetes admission requests.
Bounded non-blocking enqueue makes overload explicit and observable.

Unbounded Queue

Rejected because a backend outage or slow disk could exhaust webhook memory.
Both message count and encoded bytes are bounded.

Persistent Local Queue

Not selected for alpha. It would improve restart durability but add a second disk
protocol, replay, corruption, and lifecycle concerns before the existing driver
is invoked. The current contract is explicitly best-effort.

Admission-Specific Connection and Channel

Deferred. Reusing audit Connection/channel/path/volume minimizes public alpha
configuration. Audit and webhook pods still own separate spools and demonstration
reader sidecars; source-specific filenames and status preserve separation where
both protocols share a channel directory. The design can add independent routing
later if operational use cases require it.

Dapr Admission Export

Not supported in the alpha chart. Helm rendering fails when admission export is
enabled with Dapr or any backend other than disk. Keeping the generic export
system source-agnostic avoids hard-coding driver capability into the controller,
while the supported deployment surface remains disk-only until delivery,
cancellation, and overload semantics are defined for other drivers.

One File per Violation

Rejected because high admission volume would create excessive filesystem metadata
operations. Bounded JSON Lines segments amortize file creation and rotation.

Reusing Audit Run Files

Rejected because admission is continuous and includes denied objects with no
audit run ID. Separate prefixes, lifecycle, readers, and retention prevent one
source from consuming or deleting the other.

One Aggregate Connection Health Field

Rejected because audit and webhook report on different schedules. One producer's
success could clear the other producer's failure. Source-keyed status preserves
ownership and freshness.

Public Admission Spool Knobs

Deferred while the feature is alpha. Fixed conservative limits avoid committing
to an operator-facing API before usage data identifies which controls are needed.

Test Plan

The implementation is covered at several boundaries:

  • Payload tests verify request metadata, enforcement actions, annotations,
    details, nil results, DELETE/CONNECT behavior, and one record per violation.
  • Queue tests verify lazy construction, marshal failure, count and byte
    rejection, message-size preflight, non-blocking drops, bounded batch draining,
    asynchronous status, bounded shutdown drain, logging rate limits, and bounded
    error classes.
  • Export system tests verify audit and admission share one disk Connection and
    channel while producing distinct files.
  • Disk tests verify JSON Lines content, batch sync behavior, .open to .log
    rotation, timer retry, audit/admission separation, partial-write rollback,
    crash recovery, cached retention accounting, lazy janitor lifecycle, reader
    backpressure, free-space reserve, path safety, cleanup work limits, and
    concurrent lifecycle behavior under the race detector.
  • Status tests verify source isolation, generation reset, conflict retry, error
    bounds, and propagation into Connection.status.byPod.
  • Metrics tests verify counter and observable-gauge values.
  • Targeted Helm render checks should verify disabled defaults, disk-only
    validation, shared route, Connection creation, volume/sidecar rendering, and
    rejection of unsupported configuration.
  • End-to-end tests deny a pod, observe the reader record, and verify the current
    webhook source status.

Required validation includes unit tests, disk and webhook race tests, pinned
lint, generated-manifest parity, Helm lint and targeted render checks, and disk
export E2E.

Graduation Considerations

Before beta, the project should collect operational evidence for queue occupancy,
drop rates, segment sizes, cleanup duration, disk reserve behavior, and status
freshness. Beta should require:

  • Documented SLO-oriented alerts for drop and publish errors.
  • Upgrade and rollback testing across supported Gatekeeper versions.
  • Evidence that fixed queue and spool limits are suitable or a reviewed public
    configuration API for the limits that need tuning.
  • A supported production consumption pattern for disk segments.
  • A decision on whether additional drivers and independent admission routing are
    in scope.
  • Stable message and status schemas with compatibility guarantees.

Open Questions

  • Should admission export support a Connection/channel independent from audit?

Design Gator Policy (`docs/design/gator-policy.md`)

Gator Policy Design and Implementation

Status: MVP Complete, Post-MVP Proposed
Author: Sertac Ozercan
Created: 2026-01-08
Last Updated: 2026-01-08

Table of Contents


Overview

The gator policy command provides a brew-inspired interface for managing Gatekeeper policies from the official gatekeeper-library. It enables users to discover, install, upgrade, and manage ConstraintTemplates and Constraints in their Kubernetes clusters.


Mental Model

This section defines core terminology used throughout this document:

  • Policy: A single ConstraintTemplate definition. When you install a "policy" (without --bundle), you get only the template—no Constraint instances are created. The user must create their own Constraint CRs to activate enforcement.
  • Bundle: A curated collection of policies plus pre-configured Constraint instances. Bundles are opinionated, ready-to-use policy sets (e.g., pod-security-baseline). Installing a bundle creates both ConstraintTemplates and their associated Constraints.
  • Catalog: A YAML index file that describes available policies and bundles. It contains metadata (names, versions, descriptions) and paths pointing to the actual template/constraint YAML files in the library repository. The catalog itself contains no policy logic—only references.

Key distinction: Policies give you building blocks; bundles give you turnkey enforcement.


Part 1: Design

MVP Design

Command Structure

text
gator policy
├── search <query>              # Search available policies (required query)
├── list                        # List installed policies
├── install <policy...>         # Install one or more policies
│   --bundle <name>             # Install a policy bundle
│   --enforcement-action <act>  # Override enforcement action
│   --dry-run                   # Preview only
├── uninstall <policy...>       # Remove policies
│   --dry-run                   # Preview only
├── update                      # Refresh policy catalog
├── upgrade [policy...]         # Upgrade installed policies
│   --all                       # Upgrade all policies
│   --dry-run                   # Preview only
└── generate-catalog            # Generate catalog from gatekeeper-library
    --library-path <path>       # Path to library repository (default: ".")
    --output, -o <file>         # Output file path (default: "catalog.yaml")
    --name <name>               # Catalog name (default: "gatekeeper-library")
    --version <ver>             # Catalog version (default: "v1.0.0")
  --base-url <url>            # Convert local template/constraint paths to URLs
    --bundles <file>            # Bundles definition file (optional)
    --validate                  # Validate generated catalog (default: true)

MVP Commands

Command Description Requires Cluster
search Search available policies No (uses cached catalog)
list List installed policies Yes
install Install policies/bundles Yes
uninstall Remove policies Yes
update Refresh catalog No
upgrade Upgrade policies Yes
generate-catalog Generate catalog from library No

Installation Model

ConstraintTemplates vs Constraints

  • Individual policy install: Installs ConstraintTemplate only
  • Bundle install: Installs ConstraintTemplates AND pre-configured Constraints
bash
# Template only
gator policy install k8srequiredlabels

# Bundle includes templates + constraints
gator policy install --bundle pod-security-baseline

Bundles

Bundles are curated sets of policies with pre-configured constraints. They are opinionated and ready-to-use.

Bundle Description
pod-security-baseline Pod Security Standards - Baseline level
pod-security-restricted Pod Security Standards - Restricted level

Policy Catalog Schema

The policy catalog is a YAML file that provides metadata about available policies.

Catalog Location

  • Production: https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/catalog.yaml
  • Local/Testing: File path via GATOR_CATALOG_URL=file:///path/to/catalog.yaml

Schema Definition

text
/* Detailed source-code truncated for AI context efficiency. */

Field Descriptions

Metadata

Field Type Required Description
name string Yes Catalog identifier
version string Yes Catalog schema version (semver)
updatedAt string Yes ISO 8601 timestamp
repository string Yes Source repository URL

Bundle

Field Type Required Description
name string Yes Bundle identifier
description string Yes Human-readable description
inherits string No Parent bundle to inherit policies from
policies []string Yes List of policy names in this bundle

Policy

Field Type Required Description
name string Yes Policy identifier
version string Yes Policy version (semver)
description string Yes Human-readable description
category string Yes Category: general, pod-security, etc.
templatePath string Yes Relative path to ConstraintTemplate YAML
bundleConstraints map[string]string No Maps bundle names to their constraint YAML paths. Different bundles may need different constraint configurations for the same template (e.g., baseline vs restricted PSS profiles).
sampleConstraintPath string No Relative path to sample Constraint
documentationUrl string No Link to documentation
bundles []string No Bundle(s) this policy belongs to

Path Resolution Rules

The templatePath, bundleConstraints, and sampleConstraintPath fields are always relative to the library repository root, regardless of catalog location:

Catalog Source Path Resolution
https://... URL Paths are appended to the repository's raw content URL. E.g., templatePath: library/general/requiredlabels/template.yaml resolves to https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/requiredlabels/template.yaml
file:///path/to/catalog.yaml Paths are resolved relative to the parent directory of the catalog file (assumed to be the library root). E.g., if catalog is at /repos/gatekeeper-library/catalog.yaml, then library/general/... resolves to /repos/gatekeeper-library/library/general/...

Important: Paths must not start with / or ./. They are always library-root-relative.

Versioning

Version Terminology

Term Field Description Format
Catalog Schema Version metadata.version Format/contract version of the catalog structure itself. Increment when adding/removing fields. semver (e.g., v1.0.0)
Catalog Content Version metadata.updatedAt Timestamp indicating when this catalog snapshot was generated from the library. ISO 8601 timestamp
Policy Version policies[].version Version of the individual ConstraintTemplate/Constraint. Tracks policy logic changes. semver (e.g., v1.2.0)

Version Behavior

Policies are always installed at the version currently available in the catalog. There is no support for installing older versions of policies.

Limitation: The gatekeeper-library does not maintain historical versions of policy templates. Each policy has exactly one version at any given time. When the library updates a policy, the previous version is replaced. As a result, gator policy install always installs the current version from the catalog.

If you need a specific older version of a policy, you must:

  1. Check out the gatekeeper-library at the desired git tag/commit
  2. Use gator policy generate-catalog to create a catalog from that version
  3. Configure GATOR_CATALOG_URL to point to your custom catalog

Configuration

Environment Variables

Variable Description Default
GATOR_HOME Cache directory ~/.config/gator/
GATOR_CATALOG_URL Catalog source URL https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/catalog.yaml
KUBECONFIG Kubernetes config ~/.kube/config

Cache Structure

text
$GATOR_HOME/              # Default: ~/.config/gator/
├── catalog.yaml          # Cached catalog
└── config.yaml           # User preferences (future)

Cache Behavior Rules

Scenario Behavior
First run, no cache search, install, upgrade will error with a message to run update first. Only update and generate-catalog work without a cache.
Staleness / TTL No automatic refresh. The cache is used until explicitly replaced by update. Users control when to refresh.
update behavior Always fetches from GATOR_CATALOG_URL and overwrites the cached catalog on success.
update failure If fetch fails, the existing cache is preserved (last-known-good). An error is returned but the cache remains usable.
Cache corruption If the cached file is invalid YAML, commands will error. Run update to replace it.

Resource Management

Labels and Annotations

All managed resources receive these metadata:

yaml
metadata:
  labels:
    gatekeeper.sh/managed-by: gator
    gatekeeper.sh/bundle: pod-security-baseline  # If installed via bundle
  annotations:
    gatekeeper.sh/policy-version: v1.0.0
    gatekeeper.sh/policy-source: gatekeeper-library
    gatekeeper.sh/installed-at: "2026-01-08T10:30:00Z"

Conflict Resolution

Scenario Behavior
Resource doesn't exist Install it
Resource exists, managed by gator Update if version differs
Resource exists, NOT managed by gator Error - refuse to modify
Resource exists, same version Skip (already installed)

Error Handling

Exit Codes

Code Meaning
0 Success
1 General error (network, invalid args)
2 Cluster error (not found, permission denied)
3 Conflict (resource exists, not managed by gator)
4 Partial success (some resources failed)

Command Examples

bash
$ gator policy search labels

NAME                  VERSION   CATEGORY   DESCRIPTION
k8srequiredlabels     v1.2.0    general    Requires specified labels on resources

Install

bash
# Single policy
$ gator policy install k8srequiredlabels
✓ k8srequiredlabels (v1.2.0) installed

# Bundle with enforcement override
$ gator policy install --bundle pod-security-baseline --enforcement-action=warn
Installing pod-security-baseline bundle (8 policies)...
✓ k8spspprivilegedcontainer (v1.0.0)
...
✓ Installed 8 templates, 8 constraints

Update & Upgrade

bash
$ gator policy update
Fetching catalog from gatekeeper-library...
Updated catalog to version v1.5.0

$ gator policy upgrade --all
✓ k8srequiredlabels upgraded (v1.1.0 → v1.2.0)

Generate Catalog

bash
$ gator policy generate-catalog --library-path=/path/to/gatekeeper-library
Scanning library directory...
Found 45 policies across 5 categories.
Catalog written to catalog.yaml

User Experience Principles

  1. No confirmation prompts - Use --dry-run for safety preview
  2. Fail fast - Stop on first error, allow re-run (idempotent)
  3. Line-by-line progress - Immediate feedback, CI-friendly
  4. Explicit over implicit - --bundle flag, --all for upgrade
  5. Scriptable - JSON output, meaningful exit codes

Output Format Options

All commands support --output / -o flag:

Format Flag Description
Table -o table (default) Human-readable table output
JSON -o json Machine-readable JSON output

JSON Schema Stability:

  • The JSON output schema is versioned and follows the catalog apiVersion (e.g., gator.gatekeeper.sh/v1alpha1).
  • Fields in v1alpha1 may change without notice. Stability guarantees begin at v1beta1.
  • JSON output always includes a apiVersion field for schema identification.

Table Column Stability:

  • Columns NAME, VERSION, STATUS are stable across versions.
  • Additional columns (e.g., CATEGORY, BUNDLE) are best-effort and may change.

Flag Precedence and Mutual Exclusivity

Scenario Behavior
upgrade --all vs explicit policies Error: mutually exclusive. Either upgrade all or specify names.
install --bundle X with positional policies Allowed: installs the bundle AND the additional policies. Bundle policies get both templates and constraints; additional positional policies get template-only (no constraints). Duplicates are deduplicated.
install --bundle X --bundle Y Allowed: both bundles are installed. Duplicate policies are deduplicated.

Bundle Inheritance Semantics

When a bundle specifies inherits: , the following rules apply:

  1. Recursive expansion: Inheritance chains are resolved recursively (e.g., A inherits B, B inherits C → A gets C + B + A policies).
  2. Order: Parent policies are processed before child policies (depth-first, parent-first).
  3. Deduplication: If the same policy appears in multiple bundles, it is installed once. The first occurrence (from the deepest ancestor) wins for any policy-level settings.
  4. Cycle detection: Circular inheritance is detected at catalog load time and results in a validation error.
  5. Maximum depth: Inheritance is limited to 10 levels to prevent runaway expansion.

Conflict Resolution (Detailed)

How "managed by gator" is determined:

  • A resource is considered managed if it has both:
    • Label: gatekeeper.sh/managed-by: gator
    • Annotation: gatekeeper.sh/policy-source (any value)

Conflict scenarios:

Resource State Behavior
Does not exist Create it
Exists with gator labels Update if version differs; skip if same
Exists without gator labels Conflict error (exit code 3)
Exists with partial gator labels (only one of label/annotation) Conflict error - treat as unmanaged

Adopt flow: MVP does not support adopting unmanaged resources. Post-MVP may add --adopt flag to take ownership of existing resources by adding gator labels.

Enforcement Action Override Behavior

Scenario --enforcement-action Behavior
Template-only install (no --bundle) Flag is ignored with a warning. Templates don't have enforcement actions; only Constraints do.
Bundle install (new constraints) Flag value is applied to all newly created Constraints.
Bundle install (existing managed constraints) Existing constraints are updated to the new enforcement action.
Upgrade (managed constraints) Enforcement action is preserved from the installed constraint. To change it, use --enforcement-action explicitly.
Upgrade with --enforcement-action Overrides the enforcement action on all upgraded constraints.

Error Handling (Detailed)

"Fail fast" vs "Partial success" clarification:

  • Default behavior (MVP): Fail fast. On the first error, stop processing and return the appropriate exit code.
  • Exit code 4 (partial success) occurs only with --continue-on-error flag (if implemented) or in specific batch operations where some resources succeed before a failure.
  • For MVP, most operations use fail-fast. Exit code 4 is reserved for future batch modes.
Exit Code Condition
0 All operations succeeded
1 General error (invalid args, network failure, invalid catalog)
2 Cluster error (unreachable, permission denied, Gatekeeper not installed)
3 Conflict (resource exists but not managed by gator)
4 Partial success (some operations succeeded, some failed) - future use

Security and Integrity (MVP)

Catalog Trust Model:

  • HTTPS required: By default, only https:// catalog URLs are accepted.
  • file:// allowed: For local development/testing only. Not recommended for production.
  • Plain HTTP rejected: http:// URLs are rejected with an error. Use --insecure flag to override (with warning).

Path Traversal Protection:

  • All content paths (e.g., templatePath, constraintPath in the catalog) are validated.
  • Paths containing .. sequences are rejected to prevent directory traversal attacks.
  • Both URL paths (for HTTP fetches) and file paths (for file:// fetches) are validated.

Resource Validation:

  • Before applying any resource, gator validates:
    • apiVersion matches expected Gatekeeper API groups (templates.gatekeeper.sh, constraints.gatekeeper.sh)
    • kind is ConstraintTemplate or a valid Constraint kind
    • YAML is well-formed and passes basic structural validation
  • Invalid resources are rejected before any cluster modification.

Sensitive Data:

  • --dry-run output redacts any fields matching common secret patterns (e.g., password, token, secret).
  • Logs never include full resource specs at INFO level; use DEBUG for full output.

Performance Considerations

API Call Expectations:

Operation Approximate API Calls
list 1 (list ConstraintTemplates with label selector)
install (1 policy) 2-3 (get, create/update template, optionally constraint)
install --bundle (N policies) 2N + overhead (batched where possible)
upgrade --all (N policies) N+1 (list + update each)

Batching Strategy:

  • Multiple resources are applied sequentially (not in parallel) to ensure deterministic ordering and clear error attribution.
  • Server-side apply is used where supported to minimize round-trips.
  • Future optimization: batch multiple ConstraintTemplates in a single apply for large bundles.

Latency Targets:

  • search / list: < 500ms (local cache / single API call)
  • install (single policy): < 2s
  • install --bundle (10 policies): < 10s

Known Limitations & Deferred Items

The following capabilities were identified during the MVP review process and are intentionally deferred to post-MVP. They are documented here so they are not lost and can be prioritized in future iterations.

1. `scoped` Enforcement Action (Install & Upgrade)

The scoped enforcement action (which delegates enforcement decisions to individual constraints) is not supported in the MVP. Both install --enforcement-action and upgrade --enforcement-action reject scoped with an error.

Rationale: scoped requires additional per-constraint configuration that does not fit the current simplified install model. Supporting it properly requires a way to specify per-constraint enforcement scopes, likely through the declarative PolicyFile approach (see Post-MVP).

Workaround: Users who need scoped enforcement can manually edit the Constraint CR after installation.

2. `--exclude-policies` for `generate-catalog`

The generate-catalog command currently includes all discovered policies from the library. There is no mechanism to exclude specific policies or categories during catalog generation.

Rationale: The initial catalog generation is expected to be comprehensive. Selective inclusion/exclusion adds complexity without clear MVP use cases.

Future: Add --exclude-policies and/or --include-categories flags to generate-catalog.

3. `--all` Flag for `uninstall`

The uninstall command requires explicit policy names or --bundle. There is no --all flag to uninstall every gator-managed resource in one command.

Rationale: Mass uninstall is a destructive operation. Requiring explicit names or bundles reduces the risk of accidental removal of all policies from a cluster.

Future: Add --all flag with a confirmation prompt or require --yes for non-interactive use.

4. Multiple Catalog Sources

The MVP supports a single catalog source configured via GATOR_CATALOG_URL. There is no support for merging policies from multiple catalogs or custom registries.

Rationale: The MVP focuses on the official gatekeeper-library as the sole source. Multi-source support requires a merge strategy, conflict resolution rules, and priority ordering.

Future: Support multiple catalog URLs (e.g., via a config file) with precedence rules. This also relates to the Post-MVP OCI catalog support.

5. Mutex / File Locking for Concurrent Processes

The local catalog cache ($GATOR_HOME/catalog.yaml) has no file locking. Concurrent gator policy update or gator policy install processes running against the same cache directory may race on reads/writes.

Rationale: CLI tools are typically invoked sequentially. Concurrent invocation is uncommon in practice and adds implementation complexity.

Future: Add advisory file locking (e.g., flock) around cache read/write operations, or use atomic rename for writes.

6. Mutation Policy Support in Catalog Generator

The generate-catalog command discovers only validation policies (ConstraintTemplates). Mutation policies (Assign, AssignMetadata, ModifySet) in the mutation/ directory of the gatekeeper-library are not included.

Rationale: The MVP policy management model is built around ConstraintTemplates and Constraints. Mutation resources have a different lifecycle and configuration model.

Future: Extend the catalog schema and generator to support mutation policies as a separate resource type.

7. Cross-Cluster and Multi-Cluster Support

The MVP client operates against a single cluster determined by the current KUBECONFIG context. There is no built-in support for managing policies across multiple clusters.

Rationale: Multi-cluster management significantly increases scope and requires decisions around consistency models, rollout strategies, and failure handling.

Future: The PolicySync controller (see Post-MVP) provides a foundation for multi-cluster management. Additionally, a --context flag could allow targeting specific kubeconfig contexts.


Post-MVP Design

Post-MVP CLI Commands

Command Description
info Show detailed policy information
diff Show differences between installed and latest
apply -f <file> Declarative policy management
doctor Diagnose issues

Declarative Configuration

yaml
# gator.yaml
apiVersion: gator.gatekeeper.sh/v1alpha1
kind: PolicyFile
metadata:
  name: my-cluster-policies
spec:
  bundles:
    - name: pod-security-baseline
      enforcementAction: warn
  policies:
    - name: k8srequiredlabels
      version: ">=1.0.0"
bash
gator policy apply -f gator.yaml
gator policy apply -f gator.yaml --prune  # Remove unlisted

Info Command

bash
$ gator policy info k8srequiredlabels

Name:        k8srequiredlabels
Version:     v1.2.0
Category:    general
Description: Requires specified labels on resources

Parameters:
  - labels (array, required): List of required label keys
  - message (string, optional): Custom violation message

Doctor Command

bash
$ gator policy doctor

✓ Catalog reachable (v1.5.0)
✓ Cluster accessible (gatekeeper v3.15.0)
✓ 12 policies installed

All checks passed.

PolicySync Controller (GitOps Integration)

The PolicySync controller enables GitOps-style policy management with automatic drift detection and remediation.

Motivation

  • Manual synchronization: Currently, users must manually run gator policy install/upgrade commands
  • Drift detection: No mechanism to detect or remediate unauthorized policy changes
  • Multi-cluster management: Difficult to ensure policy consistency across many clusters
  • Version tracking: No centralized view of which policy versions are deployed

Architecture

text
/* Detailed source-code truncated for AI context efficiency. */

PolicySync CRD

yaml
apiVersion: gator.gatekeeper.sh/v1alpha1
kind: PolicySync
metadata:
  name: production-policies
spec:
  catalog:
    url: "https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/catalog.yaml"

  syncInterval: 5m

  policies:
    include:
      - k8srequiredlabels
      - k8scontainerlimits
    exclude:
      - k8sdisallowedtags
    bundles:
      - security-essentials

  constraintDefaults:
    enforcementAction: warn
    match:
      excludedNamespaces:
        - kube-system
        - gatekeeper-system

  policyOverrides:
    - name: k8srequiredlabels
      enforcementAction: deny
      parameters:
        labels:
          - key: owner
            allowedRegex: "^team-.+$"

  drift:
    enabled: true
    action: restore  # restore | warn | ignore
    checkInterval: 1m

  suspended: false

status:
  phase: Synced
  catalog:
    version: "v2026.01.08"
    lastFetched: "2026-01-08T04:00:00Z"
  summary:
    desired: 15
    synced: 14
    failed: 1
  conditions:
    - type: Ready
      status: "True"
      reason: SyncComplete

Sync Controller CLI Commands

bash
# Start the sync controller
gator policy sync-controller [flags]
  --kubeconfig string       Path to kubeconfig
  --metrics-addr string     Address for metrics endpoint (default ":8080")
  --sync-concurrency int    Number of concurrent sync operations (default 5)

# One-shot sync (for CI/CD)
gator policy sync [flags]
  --catalog-url string      Catalog URL
  --policies strings        Policies to sync
  --bundles strings         Bundles to sync
  --dry-run                 Preview changes

# View sync status
gator policy status [policysync-name]

# Trigger immediate sync
gator policy trigger-sync [policysync-name]

Observability

Metrics

prometheus
gator_policy_sync_operations_total{policysync="...", status="success|failure"}
gator_policy_sync_policies_total{policysync="...", phase="synced|failed|pending"}
gator_policy_sync_drift_detected_total{policysync="...", policy="...", action="restore|warn"}

Events

text
Normal  SyncStarted      Syncing 15 policies from catalog v2026.01.08
Normal  PolicySynced     Successfully synced policy k8srequiredlabels (v1.0.2)
Warning DriftDetected    Drift detected in ConstraintTemplate k8srequiredlabels
Normal  DriftRestored    Restored ConstraintTemplate k8srequiredlabels to desired state

Part 2: Implementation

MVP Implementation

Package Structure

text
/* Detailed source-code truncated for AI context efficiency. */

Dependencies

Package Purpose
github.com/spf13/cobra CLI framework (already used by gator)
k8s.io/client-go Kubernetes client (already used)
sigs.k8s.io/yaml YAML parsing (already used)

No new dependencies required.

Core Types

Catalog Types

text
/* Detailed source-code truncated for AI context efficiency. */

Fetcher Interface

go
// pkg/gator/policy/catalog/fetch.go

const (
    DefaultCatalogURL = "https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/catalog.yaml"
    DefaultTimeout    = 30 * time.Second
)

type Fetcher interface {
    Fetch(ctx context.Context, catalogURL string) ([]byte, error)
    FetchContent(ctx context.Context, contentURL string) ([]byte, error)
}

type HTTPFetcher struct {
    client  *http.Client
    baseURL string
    mu      sync.RWMutex // protects baseURL for thread-safety
}

func NewHTTPFetcher(timeout time.Duration) *HTTPFetcher {
    return &HTTPFetcher{
        client: &http.Client{Timeout: timeout},
    }
}

Cache Management

go
// pkg/gator/policy/catalog/cache.go

type Cache struct {
    dir string
}

func NewCache() (*Cache, error) {
    dir := os.Getenv("GATOR_HOME")
    if dir == "" {
        home, _ := os.UserHomeDir()
        dir = filepath.Join(home, ".config", "gator")
    }
    os.MkdirAll(dir, 0755)
    return &Cache{dir: dir}, nil
}

func (c *Cache) CatalogPath() string {
    return filepath.Join(c.dir, "catalog.yaml")
}

func (c *Cache) SaveCatalog(data []byte) error {
    return os.WriteFile(c.CatalogPath(), data, 0644)
}

func (c *Cache) LoadCatalog() ([]byte, error) {
    return os.ReadFile(c.CatalogPath())
}

Kubernetes Client

go
// pkg/gator/policy/client/client.go

type Client interface {
    GatekeeperInstalled(ctx context.Context) (bool, error)
    ListManagedTemplates(ctx context.Context) ([]InstalledPolicy, error)
    GetTemplate(ctx context.Context, name string) (*unstructured.Unstructured, error)
    InstallTemplate(ctx context.Context, template *unstructured.Unstructured) error
    InstallConstraint(ctx context.Context, constraint *unstructured.Unstructured) error
    DeleteTemplate(ctx context.Context, name string) error
}

type InstalledPolicy struct {
    Name        string
    Version     string
    Bundle      string
    InstalledAt string
    ManagedBy   string
}

Labels Helper

go
// pkg/gator/policy/labels/labels.go

const (
    LabelManagedBy        = "gatekeeper.sh/managed-by"
    LabelBundle           = "gatekeeper.sh/bundle"
    AnnotationVersion     = "gatekeeper.sh/policy-version"
    AnnotationSource      = "gatekeeper.sh/policy-source"
    AnnotationInstalledAt = "gatekeeper.sh/installed-at"
    ManagedByValue        = "gator"
    SourceValue           = "gatekeeper-library"
)

func AddManagedLabels(obj *unstructured.Unstructured, version, bundle string)
func IsManagedByGator(obj *unstructured.Unstructured) bool
func GetPolicyVersion(obj *unstructured.Unstructured) string
func GetBundle(obj *unstructured.Unstructured) string
func GetInstalledAt(obj *unstructured.Unstructured) string

Exit Codes

go
// pkg/gator/policy/exitcodes.go

const (
    ExitSuccess        = 0
    ExitGeneralError   = 1
    ExitClusterError   = 2
    ExitConflictError  = 3
    ExitPartialSuccess = 4
)

type ExitError struct {
    Code    int
    Message string
}

func (e *ExitError) Error() string { return e.Message }

func NewExitError(code int, message string) *ExitError
func NewGeneralError(message string) *ExitError
func NewClusterError(message string) *ExitError
func NewConflictError(message string) *ExitError
func NewPartialSuccessError(message string) *ExitError

CLI Structure

go
// cmd/gator/policy/policy.go

var Cmd = &cobra.Command{
    Use:     "policy",
    Short:   "Manage Gatekeeper policies from the policy library",
    Long:    "Install, upgrade, and manage Gatekeeper policies from the official gatekeeper-library.",
    Example: examples,
}

func init() {
    Cmd.AddCommand(
        newSearchCommand(),
        newListCommand(),
        newInstallCommand(),
        newUninstallCommand(),
        newUpdateCommand(),
        newUpgradeCommand(),
        newGenerateCatalogCommand(),
    )
}

Testing Strategy

Local Testing

bash
# Set catalog to local file
export GATOR_CATALOG_URL=file://$(pwd)/test/gator/policy/testdata/catalog.yaml

# Test commands
gator policy update
gator policy search test
gator policy install test-policy-1 --dry-run

CI Testing

yaml
- name: Test gator policy
  run: |
    make gator
    export GATOR_CATALOG_URL=file://${{ github.workspace }}/test/gator/policy/testdata/catalog.yaml
    ./bin/gator policy update
    ./bin/gator policy search test

E2E Testing

bash
# With kind cluster
kind create cluster
kubectl apply -f deploy/gatekeeper.yaml
export GATOR_CATALOG_URL=file://testdata/catalog.yaml
gator policy install test-policy-1
gator policy list
gator policy uninstall test-policy-1

Post-MVP Implementation

PolicySync Controller Implementation

API Types

text
/* Detailed source-code truncated for AI context efficiency. */

Controller Reconciler

go
type PolicySyncReconciler struct {
    client.Client
    Scheme         *runtime.Scheme
    CatalogFetcher *catalog.Fetcher
    PolicyClient   *policyclient.Client
    Recorder       record.EventRecorder
}

func (r *PolicySyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    // 1. Fetch PolicySync resource
    // 2. Check if suspended
    // 3. Fetch catalog (with caching)
    // 4. Resolve desired policies (include/exclude/bundles)
    // 5. Get current state (installed policies with owner ref)
    // 6. Calculate diff (add/update/remove)
    // 7. Apply changes with owner references
    // 8. Update status
    // 9. Requeue after syncInterval
}

RBAC Requirements

yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: gator-sync-controller
rules:
  - apiGroups: ["gator.gatekeeper.sh"]
    resources: ["policysyncs"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["gator.gatekeeper.sh"]
    resources: ["policysyncs/status"]
    verbs: ["update", "patch"]
  - apiGroups: ["templates.gatekeeper.sh"]
    resources: ["constrainttemplates"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  - apiGroups: ["constraints.gatekeeper.sh"]
    resources: ["*"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  - apiGroups: [""]
    resources: ["configmaps", "secrets"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "patch"]

Helm Chart Integration

yaml
# values.yaml additions
policySync:
  enabled: false
  image:
    repository: openpolicyagent/gator
    tag: ""
  replicas: 1
  resources:
    limits:
      cpu: 200m
      memory: 256Mi
  defaultPolicySync:
    enabled: false
    catalog:
      url: "https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/catalog.yaml"
    syncInterval: 5m
    constraintDefaults:
      enforcementAction: warn

Future Considerations

  1. OCI Catalog Support: Fetch catalogs from OCI registries
  2. Signature Verification: Cosign/Notation support for catalog authenticity
  3. Multi-Cluster: Federation support for syncing to multiple clusters
  4. Approval Workflow: Require approval before applying policy changes
  5. Rollback: Automatic rollback on policy failures
  6. UI Dashboard: Visual policy sync status

Design Remote Cluster Routing (`docs/design/remote-cluster-routing.md`)

Remote Cluster Status Resource Routing Design

Status: Proposal implemented
Author: Abhishek Sheth
Created: 2026-05-18
Last Updated: 2026-06-17

Overview

In remote cluster mode, Gatekeeper runs on a local cluster but enforces policies on a separate remote cluster (connected via --kubeconfig). PodStatus resources need to live on the local cluster because:

  1. Garbage collection needs both objects on the same cluster: Each PodStatus has an OwnerReference pointing to the Gatekeeper pod. If the PodStatus is on a different cluster than the pod, Kubernetes can't find the owner and GC breaks.
  2. Automatic cleanup on pod restart: When a Gatekeeper pod restarts, Kubernetes GC deletes the old PodStatus objects automatically.

All other resources (ConstraintTemplates, Constraints, Config, etc.) stay on the remote cluster.

Design

A routing layer wraps the manager's cache and client. On every read or write, it looks at the resource's API group and sends it to the right cluster:

  • status.gatekeeper.sh/* → local cluster
  • Everything else → remote cluster

The routing check is simple:

go
func isLocalClusterResource(gvk schema.GroupVersionKind) bool {
    return gvk.Group == "status.gatekeeper.sh"
}

This covers all 7 PodStatus types across v1alpha1 and v1beta1.

Components

routingCache (pkg/routing/cache.go): wraps cache.Cache, constructed via NewRoutingCache(remoteCluster, localCluster, scheme). Every cache operation (Get, List, GetInformer, GetInformerForKind, RemoveInformer, IndexField) checks the resource type and picks the right underlying cache. It starts and syncs both caches. In non-remote mode, both point to the same cache.

routingClient (pkg/routing/client.go): wraps client.Client, constructed via NewRoutingClient(remoteCluster, localCluster, scheme). It routes all reads (Get, List) and writes (Create, Update, Delete, Patch, DeleteAllOf, Apply) by resource type. Status() and SubResource() are also routed (via a routingSubResourceClient) by the parent object's GVK, so subresource writes on PodStatus objects reach the local cluster too. The local cluster client is cache-backed (it reuses the manager-provided cache reader), so routed PodStatus reads are served from the local cache rather than hitting the API server on every call.

resolveGVK (pkg/routing/routing.go): figures out the GVK of an object. It first checks if the object already has a GVK set, then falls back to looking it up in the scheme (typed objects). If it can't determine the GVK, it returns an error. The shared routeIsLocal helper wraps resolveGVK and isLocalClusterResource, and the error propagates up through clientFor/cacheFor so the operation fails loudly rather than silently routing to the wrong cluster.

main.go

go
// localClusterConfig = where the pod runs (the local/management cluster).
// Only built when remote cluster mode is enabled; nil otherwise.
var localClusterConfig *rest.Config
if controller.RemoteClusterEnabled() {
    localClusterConfig, _ = rest.InClusterConfig()
}

// The manager's primary config is the remote (target) cluster.
mgr, _ := ctrl.NewManager(config, ctrl.Options{
    NewCache:  newCacheFunc(scheme, localClusterConfig),   // nil in non-remote mode
    NewClient: newClientFunc(scheme, localClusterConfig),  // nil in non-remote mode
})

When localClusterConfig is nil (non-remote mode), these return nil and controller-runtime uses its defaults. Nothing changes.

The local cluster cache only watches the gatekeeper-system namespace to keep RBAC permissions minimal.

Design Decisions

  1. Routing happens at the manager level: Controllers, audit, and webhooks don't know about routing. No controller code changes were needed.

  2. Route by API group, not resource name: The API group is stable across versions. No need to maintain a list of resource names.

  3. OwnerReferences are always set: Previously, remote mode skipped setting OwnerReferences (since they wouldn't work cross-cluster). Now that PodStatus lives on the same cluster as the pod, OwnerReferences always work.

  4. Both caches must sync before ready: WaitForCacheSync waits for both caches, so the manager won't serve traffic until both are ready.

  5. Error on unknown GVK: If resolveGVK can't determine the resource type (e.g. unregistered type, empty GVK), the routing layer returns an error and the operation fails. An unresolved GVK means the routing decision can't be made, so failing loudly is safer.

RBAC Requirements (Local Cluster)

The local cluster needs minimal permissions in the gatekeeper-system namespace:

Resource Verbs
pods get (for OwnerReference lookup)
status.gatekeeper.sh/* (all PodStatus types) get, list, watch, create, update, patch, delete

These are scoped to the gatekeeper-system namespace via a Role + RoleBinding (not a ClusterRole). No cluster-wide permissions are needed on the local cluster.

Files Changed

File What it does
pkg/routing/routing.go Routing decision logic (isLocalClusterResource, resolveGVK)
pkg/routing/cache.go routingCache / NewRoutingCache, routes cache reads and informers
pkg/routing/client.go routingClient / NewRoutingClient, routes client reads, writes, and subresources; routeIsLocal helper
main.go Builds the local cluster config, passes routing factories to the manager
pkg/controller/controller.go RemoteClusterEnabled() helper
apis/status/v1*/... OwnerReferences always set (removed conditional skip)

Non-Remote Mode

When --enable-remote-cluster is not set:

  • localClusterConfig is nil
  • Routing factories return nil
  • Manager uses its default cache and client
  • Everything goes to the single cluster
  • Behavior is identical to before this change

Api (`website/docs/api.md`)


id: api title: API Reference

This page lists Gatekeeper's Custom Resource Definitions (CRDs) and provides a quick reference for commonly configured fields. It is not an exhaustive schema reference: use the schemas and examples for complete field definitions and the linked feature guides for usage examples. Required fields and defaults below include Gatekeeper's validation rules, not only OpenAPI schema requirements.

To inspect the live schema in a cluster:

bash
kubectl get crds | grep gatekeeper
kubectl explain configs.config.gatekeeper.sh.spec
kubectl explain constrainttemplates.templates.gatekeeper.sh.spec

Constraint kinds (for example K8sRequiredLabels) are not shipped as static CRDs. Gatekeeper creates those CRDs at runtime when you apply a ConstraintTemplate. Their shared constraint fields are documented below.

CRD catalog

Kind API group Scope Purpose
ConstraintTemplate templates.gatekeeper.sh Cluster Defines Rego/CEL policy and the schema for a constraint kind
Constraint (dynamic) constraints.gatekeeper.sh Cluster Instantiates a template with match criteria, enforcement mode, and parameters
Config config.gatekeeper.sh Namespaced Gatekeeper configuration (sync, exemption matchers, validation traces)
SyncSet syncset.gatekeeper.sh Cluster Declares additional GVKs for Gatekeeper to cache
Assign mutations.gatekeeper.sh Cluster Mutate object fields outside metadata
AssignMetadata mutations.gatekeeper.sh Cluster Add missing metadata labels/annotations
AssignImage mutations.gatekeeper.sh Cluster Mutate container image strings
ModifySet mutations.gatekeeper.sh Cluster Merge or prune list values
ExpansionTemplate expansion.gatekeeper.sh Cluster Expand generator resources (for example Deployments) into child objects for policy
Connection connection.gatekeeper.sh Namespaced Configures export drivers/connections
Provider externaldata.gatekeeper.sh Cluster Registers an external data provider
*PodStatus kinds status.gatekeeper.sh Namespaced Per-pod status reported by Gatekeeper controllers (read-only operational data)

ConstraintTemplate (`templates.gatekeeper.sh`)

Preferred version: v1 (also served: v1beta1, v1alpha1).

Field Type Description
spec.crd.spec.names.kind string Required kind name for constraints created from this template (for example K8sRequiredLabels)
spec.crd.spec.names.shortNames []string Optional short names for kubectl
spec.crd.spec.validation.openAPIV3Schema object Optional schema for the constraint spec.parameters field; v1 templates use a structural schema
spec.crd.spec.validation.legacySchema bool Enable legacy schema mode; defaults to false in v1, but true in v1alpha1 and v1beta1
spec.targets[] []object Required policy target definitions
spec.targets[].target string Required target identifier: admission.k8s.gatekeeper.sh
spec.targets[].rego string Legacy Rego policy source; alternatively use code
spec.targets[].libs []string Optional supporting libraries for the legacy Rego source
spec.targets[].code[] []object Engine-specific policy definitions, each with engine and source
spec.targets[].code[].engine string Required for each code entry: Rego or K8sNativeValidation (CEL)
spec.targets[].code[].source object Required engine-specific source; see Rego v1 and CEL sources
status object Read-only template installation status observed by Gatekeeper

See Constraint Templates for examples and engine selection and field precedence. Defining multiple engines does not mean that all of them are evaluated.


Constraint resources (`constraints.gatekeeper.sh/v1beta1`)

Each installed ConstraintTemplate registers a cluster-scoped constraint CRD whose kind matches spec.crd.spec.names.kind. All constraints share the following fields regardless of parameters schema.

`spec` fields

Field Type Description
spec.match object Optional object selection. Matchers are AND-ed. Empty/undefined match selects everything.
spec.match.kinds []object List of {apiGroups, kinds} groups. A resource needs one matching entry.
spec.match.scope string *, Cluster, or Namespaced (default *)
spec.match.namespaces []string Include only these namespaces (prefix globs like kube-* allowed)
spec.match.excludedNamespaces []string Exclude these namespaces (prefix globs allowed)
spec.match.labelSelector object Standard label selector (matchLabels / matchExpressions) on the object
spec.match.namespaceSelector object Label selector on the object's namespace (or the object itself if it is a Namespace)
spec.match.name string Object name or prefix glob
spec.match.source string Target resource origin: All, Generated, or Original (default All); see expansion matching
spec.parameters object Template-specific inputs; required fields and types depend on the template's OpenAPI schema
spec.enforcementAction string Optional violation handling mode; defaults to deny (see below)
spec.scopedEnforcementActions []object Required when enforcementAction is scoped; ignored otherwise

All match fields are optional. Namespace filters do not exclude non-Namespace cluster-scoped resources; set spec.match.scope: Namespaced to exclude those resources. See matching semantics.

`spec.enforcementAction`

Value Behavior
deny Default. Admission requests that violate the constraint are rejected.
dryrun Violations are recorded (for example on the constraint status during audit) but admission is not blocked.
warn Admission is allowed; clients receive a warning (Kubernetes 1.19+).
scoped Use spec.scopedEnforcementActions to choose different actions per enforcement point.

See Handling Constraint Violations and Enforcement Points.

`spec.scopedEnforcementActions[]`

Field Type Description
action string Required: deny, warn, or dryrun for the listed enforcement points
enforcementPoints[] []object Required non-empty list of enforcement points
enforcementPoints[].name string Required: validation.gatekeeper.sh, audit.gatekeeper.sh, gator.gatekeeper.sh, vap.k8s.io, or * for all points

With enforcementAction: scoped, an enforcement point not named in any entry is excluded unless an entry names *. In particular, audit is not implicitly included. Native Kubernetes enforcement (vap.k8s.io) requires a CEL template and VAP/VAPBinding generation; see Enforcement Points.

`status` fields (observed)

Field Description
status.byPod[].enforced Enforcement status reported by each Gatekeeper controller pod
status.auditTimestamp Timestamp of the latest audit run recorded in this constraint's status, including runs with zero violations
status.violations[] Bounded list of violations from the latest recorded audit run (enforcementAction, kind, name, namespace, message, ...)
status.totalViolations Total violation count from that audit run, including violations omitted from status.violations

The violation list is limited by --constraint-violations-limit (default 20); see Reading Audit Results.


Config (`config.gatekeeper.sh/v1alpha1`)

Singleton configuration object. It must be named config in Gatekeeper's namespace (gatekeeper-system by default). Gatekeeper ignores Config resources with other names or namespaces.

All configuration sections below are optional.

Field Type Description
spec.sync.syncOnly[] []object {group, version, kind} entries to replicate for referential policies; combined with all SyncSets, not an override of them
spec.match[] []object Process-specific configuration such as namespace exemptions
spec.match[].processes []string audit, webhook, sync, mutation-webhook, or * for all processes; see namespace exemptions
spec.match[].excludedNamespaces []string Namespaces excluded for those processes (wildcards supported)
spec.validation.traces[] []object Admission trace requests; each needs user and kind (group, version, kind). Optional dump: All includes OPA state; see Tracing
spec.readiness.statsEnabled bool Enable readiness tracker stats (default false)

See replication with Config.


SyncSet (`syncset.gatekeeper.sh/v1alpha1`)

Cluster-scoped list of GVKs to cache. The effective sync set is the union of all SyncSet objects plus Config.spec.sync.syncOnly.

Field Type Description
spec.gvks[] []object Entries with group, version, and kind

A resource remains cached while any SyncSet or Config still requests its GVK. Removing it from only one source does not stop replication. See replication with SyncSets.


Mutation CRDs (`mutations.gatekeeper.sh`)

Assign, AssignMetadata, and ModifySet use v1 (also served: v1alpha1, v1beta1). AssignImage uses v1alpha1.

Common mutation fields:

Field Type Description
spec.applyTo[] []object Required for Assign, AssignImage, and ModifySet; not supported by AssignMetadata. Each entry needs groups, versions, and kinds; globs are not allowed
spec.applyTo[].operations []string Optional: CREATE, UPDATE, or *. Omitted/empty means all supported mutation operations (currently CREATE and UPDATE); * must appear alone. See operation filtering
spec.match object Optional match criteria; empty/undefined criteria match everything
spec.location string Required object path, for example spec.containers[name: main].image; see path syntax
spec.parameters object Mutator-specific options
spec.parameters.pathTests[] []object Optional subPath + condition (MustExist / MustNotExist) checks; each subpath must be a prefix of location. Supported by Assign, AssignImage, and ModifySet, not AssignMetadata; see conditionals

Omitted operations and * also include any mutation operations supported by future releases. List explicit operations to keep the same scope across upgrades. Operation-filtered mutators only run without admission-operation context (for example during expansion) if they include every supported operation.

Assign (`v1`)

spec.parameters.assign must select exactly one of these value sources:

Field Type Description
spec.parameters.assign.value any Constant value to assign; may be a scalar, list, or object
spec.parameters.assign.fromMetadata.field string name or namespace from the object being mutated; see metadata assignment
spec.parameters.assign.externalData object External provider configuration; see the external-data mutation API for provider selection, data sources, and failure policies

AssignMetadata (`v1`)

Adds only missing metadata.labels / metadata.annotations entries. Existing labels and annotations are not overwritten. applyTo and pathTests are not supported.

Field Description
spec.parameters.assign Exactly one of the Assign value sources; value must be a string. External data only supports dataSource: Username

See AssignMetadata for examples.

AssignImage (`v1alpha1`)

Field Description
spec.parameters.assignDomain Image registry domain (no trailing slash)
spec.parameters.assignPath Image path/repository component
spec.parameters.assignTag Tag or digest; must start with : or @

At least one image component is required. If assignPath could be interpreted as a domain, assignDomain must also be specified. See AssignImage.

ModifySet (`v1`)

Field Description
spec.parameters.operation merge (default) or prune
spec.parameters.values.fromList List values to merge into or prune from the location

See ModifySet.


ExpansionTemplate (`expansion.gatekeeper.sh/v1beta1`)

Also served: v1alpha1.

Field Type Description
spec.applyTo[] []object Generator resource GVKs to expand (for example Deployment, Job)
spec.templateSource string Field on the generator used as the pod/template source (often spec.template)
spec.generatedGVK object {group, version, kind} of the generated resource (for example Pod)
spec.enforcementAction string Optional override for enforcement on expanded resources; empty defers to the constraint

See ExpansionTemplate behavior.


Connection (`connection.gatekeeper.sh/v1alpha1`)

Field Type Description
spec.driver string Required export driver name: dapr or disk
spec.config object Required driver-specific configuration (preserved unknown fields); see export configuration

Create the Connection in Gatekeeper's namespace and select its name with --audit-connection. See Export for driver configuration and enablement.


Provider (`externaldata.gatekeeper.sh`)

Preferred version: v1beta1 (also served: v1alpha1, deprecated).

Registers an external data provider HTTPS service.

Field Type Description
spec.url string Required provider endpoint URL (must use the https:// prefix)
spec.timeout integer Request timeout in seconds when querying the provider
spec.caBundle string Required base64-encoded TLS CA bundle in PEM format; see provider TLS configuration

See the Provider API for examples.


Status CRDs (`status.gatekeeper.sh`)

Gatekeeper writes these namespaced resources so each controller pod can report health and errors. They are controller-owned operational data, not resources that users normally create or edit.

Kind API version
ConfigPodStatus status.gatekeeper.sh/v1beta1
ConnectionPodStatus status.gatekeeper.sh/v1alpha1
ConstraintPodStatus status.gatekeeper.sh/v1beta1
ConstraintTemplatePodStatus status.gatekeeper.sh/v1beta1
ExpansionTemplatePodStatus status.gatekeeper.sh/v1beta1
MutatorPodStatus status.gatekeeper.sh/v1beta1
ProviderPodStatus status.gatekeeper.sh/v1beta1

Viewing schemas and examples


Audit (`website/docs/audit.md`)


id: audit title: Audit

Audit performs periodic evaluations of existing resources against constraints, detecting pre-existing misconfigurations.

Reading Audit Results

There are three ways to gather audit results, depending on the level of detail needed.

Prometheus Metrics

Prometheus metrics provide an aggregated look at the number of audit violations:

  • gatekeeper_audit_last_run_time provides the start time timestamp of the most recent audit run
  • gatekeeper_audit_last_run_end_time provides the end time timestamp of the last completed audit run
  • gatekeeper_violations provides the total number of audited violations for the last audit run, broken down by violation severity

Constraint Status

Violations of constraints are listed in the status field of the corresponding constraint.
Note that only violations from the most recent audit run are reported. Also note that there
is a maximum number of individual violations that will be reported on the constraint
itself. If the number of current violations is greater than this cap, the excess violations
will not be reported (though they will still be included in the totalViolations count).
This is because Kubernetes has a cap on how large individual API objects can grow, which makes
unbounded growth a bad idea. This limit can be configured via the --constraint-violations-limit flag.

Here is an example of a constraint with violations:

yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: ns-must-have-gk
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    labels: ["gatekeeper"]
status:
  auditTimestamp: "2019-05-11T01:46:13Z"
  enforced: true
  violations:
  - enforcementAction: deny
    group: ""
    version: v1
    kind: Namespace
    message: 'you must provide labels: {"gatekeeper"}'
    name: default
  - enforcementAction: deny
    group: ""
    version: v1
    kind: Namespace
    message: 'you must provide labels: {"gatekeeper"}'
    name: gatekeeper-system
  - enforcementAction: deny
    group: ""
    version: v1
    kind: Namespace
    message: 'you must provide labels: {"gatekeeper"}'
    name: kube-public
  - enforcementAction: deny
    group: ""
    version: v1
    kind: Namespace
    message: 'you must provide labels: {"gatekeeper"}'
    name: kube-system

Limitations of getting violations from constraint status:

  • To reduce in-memory consumption of Gatekeeper audit pod and to avoid hitting default etcd limit of 1.5MB per resource, gatekeeper recommends configuring a limit up-to 500 violations(by default 20) on constraint. Because of these limitations, users might not get all the violations from a Constraint resource.

Audit Logs

Violations

The audit pod emits JSON-formatted audit logs to stdout. The following is an example audit event:

json
{
  "level": "info",
  "ts": 1632889070.3075402,
  "logger": "controller",
  "msg": "container <kube-scheduler> has no resource limits",
  "process": "audit",
  "audit_id": "2021-09-29T04:17:47Z",
  "event_type": "violation_audited",
  "constraint_group": "constraints.gatekeeper.sh",
  "constraint_api_version": "v1beta1",
  "constraint_kind": "K8sContainerLimits",
  "constraint_name": "container-must-have-limits",
  "constraint_namespace": "",
  "constraint_action": "deny",
  "constraint_enforcement_actions": [],
  "constraint_annotations": {
    "test-annotation-1": "annotation_1"
  },
  "resource_group": "",
  "resource_api_version": "v1",
  "resource_kind": "Pod",
  "resource_namespace": "kube-system",
  "resource_name": "kube-scheduler-kind-control-plane"
  "resource_labels": {
    "env": "prod",
    "my-app-system": "true"
  }
}

In addition to information on the violated constraint, violating resource, and violation message, the
audit log entries also contain:

  • An audit_id field that uniquely identifies a given audit run. This allows indexing of historical audits
  • An event_type field with a value of violation_audited to make it easy to programmatically identify audit violations

Limitations of getting violations from audit logs:

  • It could be difficult to parse audit pod logs to look for violation messages, as violation logs would be mixed together with other log statements.

Other Event Types

In addition to violations, these other audit events may be useful (all uniquely identified via the event_type field):

  • audit_started marks the beginning of a new audit run
  • constraint_audited marks when a constraint is done being audited for a given run, along with the number of violations found
  • audit_finished marks the end of the current audit run

All of these events (including violation_audited) are marked
with the same audit_id for a given audit run.

Export violations

This feature allows different systems to be provided for Gatekeeper to export audit violations. Thereby, ensuring violations are not subject to reporting limits. Please refer to the export guide to configure audit to push violations via this feature.

Limitations/drawbacks of exporting violations:

  • There is a risk of messages getting dropped. You might not receive all the exported violations. This depends on the type of backend you are using for delivery. For example, using a network as backend to export violation has the risk of messages getting dropped.
  • Additional dependency on the backend system provided. For example, using pubsub tools to export violations.

Running Audit

Why Audit Runs as a Singleton

Gatekeeper audit is designed to run as a singleton because it writes audit results to Constraint status. Multiple audit instances can contend on the same Constraint status fields. In Gatekeeper v3.18 and later, the audit deployment may also run the generate operation, which creates or updates Constraint CRDs and, when VAP/VAPB generation is enabled, ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding resources. The generate operation should also run as a singleton.

If your setup only consumes audit results from logs or exports, setting --constraint-violations-limit=0 avoids storing individual violations in Constraint .status.violations. This does not disable all Constraint status writes: audit still updates fields such as .status.auditTimestamp and .status.totalViolations, so running multiple audit replicas can still cause status update contention. If you scale the audit deployment beyond one replica, ensure that only one instance runs the generate operation, or disable it for the audit deployment, for example with Helm's audit.disableGenerateOperation=true. If VAP/VAPB generation is enabled, ensure it is handled by a singleton generator or disable it with defaultCreateVAPForTemplates=false and defaultCreateVAPBindingForConstraints=false (or the equivalent runtime flags).

For more details on how to deploy audit and
number of instances to run, please refer to operations audit.

Configuring Audit

  • Audit violations per constraint: set --constraint-violations-limit=123 (defaults to 20). NOTE: This flag only impacts when gathering audit results using the constraint status model. If you want to export audit results via other means, please refer to the export guide. Both approaches for getting audit violations can be configured independently and work simultaneously without any interference.
  • Audit chunk size: set --audit-chunk-size=400 (defaults to 500, 0 = infinite) Lower chunk size can reduce memory consumption of the auditing Pod but can increase the number requests to the Kubernetes API server.
  • Audit interval: set --audit-interval=123 (defaults to every 60 seconds). Disable audit interval by setting --audit-interval=0
  • Audit api server cache write to disk (Gatekeeper v3.7.0+): Starting from v3.7.0, by default, audit writes api server cache to the disk attached to the node. This reduces the memory consumption of the audit pod. If there are concerns with high IOPS, then switch audit to write cache to a tmpfs ramdisk instead. NOTE: write to ramdisk will increase memory footprint of the audit pod.
    • helm install --set audit.writeToRAMDisk=true
    • if not using helm, modify the deployment manifest to mount a ramdisk
      yaml
      - emptyDir:
          medium: Memory

By default, audit will request each resource from the Kubernetes API during each audit cycle. To rely on the audit informer cache instead, use the flag --audit-from-cache=true. Note that this requires replication of Kubernetes resources into the audit cache before they can be evaluated against the enforced policies. Refer to the Replicating data section for more information.

Disabling Audit

If you want to completely disable the audit process (for example, to reduce CPU or Memory usage), you must be careful not to break other singleton operations like CRD generation and status aggregation, which are also handled by the audit pod.

Instead of completely removing the audit deployment (e.g., by setting disableAudit: true in Helm), you should disable just the audit operation:

  • In Helm, set audit.disableAuditOperation: true.
  • If not using Helm, remove the --operation=audit flag from the audit deployment manifest.

Audit using kinds specified in the constraints only

By default, Gatekeeper will audit all resources in the cluster. This operation can take some time depending on the number of resources.

If all of your constraints match against specific kinds (e.g. "match only pods"), then you can speed up audit runs by setting --audit-match-kind-only=true flag. This will only check resources of the kinds specified in all constraints defined in the cluster.

For example, defining this constraint will only audit Pod kind:

yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
  name: prod-repo-is-openpolicyagent
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
...

If any of the constraints do not specify kinds, it will be equivalent to not setting --audit-match-kind-only flag (false by default), and will fall back to auditing all resources in the cluster.

Opt-out of Audit in constraints

By default, all constraints are opted-in audit. To opt-out of the audit process at constraint, you can use enforcementAction: scoped and define scopedEnforcementActions without including audit enforcement point.

For example, defining this constraint will opt-out of audit

yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
  name: prod-repo-is-openpolicyagent
spec:
...
  enforcementAction: scoped
  scopedEnforcementActions:
  - action: warn
    enforcementPoints:
    - name: "validation.gatekeeper.sh"
  - action: deny
    enforcementPoints:
    - name: "gator.gatekeeper.sh"
...

Find out more about different enforcement points in Gatekeeper.

Audit UserInfo

When using input.review.userInfo, NOTE the request's user's information, such as username, uid, groups, extra, cannot be populated by Kubernetes for audit reviews and therefore constraint templates that rely on userInfo are not auditable. It is up to the rego author to handle the case where userInfo is unset and empty in order to avoid every matching resource being reported as violating resources.


Cloud Specific (`website/docs/cloud-specific.md`)


id: vendor-specific title: Cloud and Vendor Specific Fixes

Running on private GKE Cluster nodes

By default, firewall rules restrict the cluster master communication to nodes only on ports 443 (HTTPS) and 10250 (kubelet). Although Gatekeeper exposes its service on port 443, GKE by default enables --enable-aggregator-routing option, which makes the master to bypass the service and communicate straight to the POD on port 8443.

Two ways of working around this:

  • create a new firewall rule from master to private nodes to open port 8443 (or any other custom port)
  • make the pod to run on privileged port 443 (need to run pod as root, or have NET_BIND_SERVICE capability)
    • update Gatekeeper deployment manifest spec:
      • add NET_BIND_SERVICE to securityContext.capabilities.add to allow binding on privileged ports as non-root
      • update port from 8443 to 443
      yaml
      containers:
      - args:
        - --port=443
        ports:
        - containerPort: 443
          name: webhook-server
          protocol: TCP
        securityContext:
          capabilities:
            drop: ["all"]
            add: ["NET_BIND_SERVICE"]

Running on OpenShift 4.x

When running on OpenShift, the anyuid scc must be used to keep a restricted profile but being able to set the UserID.

In order to use it, the following section must be added to the gatekeeper-manager-role Role:

yaml
- apiGroups:
  - security.openshift.io
  resourceNames:
    - anyuid
  resources:
    - securitycontextconstraints
  verbs:
    - use

With this restricted profile, it won't be possible to set the container.seccomp.security.alpha.kubernetes.io/manager: runtime/default annotation. On the other hand, given the limited amount of privileges provided by the anyuid scc, the annotation can be removed.


Constrainttemplates (`website/docs/constrainttemplates.md`)


id: constrainttemplates title: Constraint Templates

ConstraintTemplates define a way to validate some set of Kubernetes objects in Gatekeeper's Kubernetes admission controller.

They are part of Gatekeeper's validation policy model. Mutation policies use separate mutator resources.

ConstraintTemplates are made of two main elements:

  1. Rego or CEL policy code in either spec.targets[].rego or spec.targets[].code[] that defines policy violation logic; see field precedence
  2. The schema of the accompanying Constraint object, which represents an instantiation of a ConstraintTemplate

`v1` Constraint Template

In release version 3.6.0, Gatekeeper included the v1 version of ConstraintTemplate. Unlike past versions of ConstraintTemplate, v1 requires the Constraint schema section to be structural.

Structural schemas have a variety of requirements. One such requirement is that the type field be defined for each level of the schema.

For example, users of Gatekeeper may recognize the k8srequiredlabels ConstraintTemplate, defined here in version v1beta1:

yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        # Schema for the `parameters` field
        openAPIV3Schema:
          properties:
            labels:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredlabels

        violation[{"msg": msg, "details": {"missing_labels": missing}}] {
          provided := {label | input.review.object.metadata.labels[label]}
          required := {label | label := input.parameters.labels[_]}
          missing := required - provided
          count(missing) > 0
          msg := sprintf("you must provide labels: %v", [missing])
        }

The parameters field schema (spec.crd.spec.validation.openAPIV3Schema) is not structural. Notably, it is missing the type: declaration:

yaml
openAPIV3Schema:
  # missing type
  properties:
    labels:
      type: array
      items:
        type: string

This schema is invalid by default in a v1 ConstraintTemplate. Adding the type information makes the schema valid:

yaml
openAPIV3Schema:
  type: object
  properties:
    labels:
      type: array
      items:
        type: string

For more information on valid types in JSONSchemas, see the JSONSchema documentation.

Why implement this change?

Structural schemas are required in version v1 of CustomResourceDefinition resources, which underlie ConstraintTemplates. Requiring the same in ConstraintTemplates puts Gatekeeper in line with the overall direction of Kubernetes.

Beyond this alignment, structural schemas yield significant usability improvements. The schema of a ConstraintTemplate's associated Constraint is both more visible and type validated.

As the data types of Constraint fields are defined in the ConstraintTemplate, the API server will reject a Constraint with an incorrect parameters field. Previously, the API server would ingest it and simply not pass those parameters to Gatekeeper. This experience was confusing for users, and is noticeably improved by structural schemas.

For example, see this incorrectly defined k8srequiredlabels Constraint:

yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: ns-must-have-gk
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    # Note that "labels" is now contained in an array item, rather than an object key under "parameters"
    - labels: ["gatekeeper"]

In a v1beta1 ConstraintTemplate, this Constraint would be ingested successfully. However, it would not work. The creation of a new namespace, foobar, would succeed, even in the absence of the gatekeeper label:

shell
$ kubectl create ns foobar
namespace/foobar created

This is incorrect. We'd expect this to fail:

shell
$ kubectl create ns foobar
Error from server ([ns-must-have-gk] you must provide labels: {"gatekeeper"}): admission webhook "validation.gatekeeper.sh" denied the request: [ns-must-have-gk] you must provide labels: {"gatekeeper"}

The structural schema requirement prevents this mistake. The aforementioned type: object declaration would prevent the API server from accepting the incorrect k8srequiredlabels Constraint.

shell
# Apply the Constraint with incorrect parameters schema
$ cat << EOF | kubectl apply -f -
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: ns-must-have-gk
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    # Note that "labels" is now an array item, rather than an object
    - labels: ["gatekeeper"]
EOF
The K8sRequiredLabels "ns-must-have-gk" is invalid: spec.parameters: Invalid value: "array": spec.parameters in body must be of type object: "array"

Fixing the incorrect parameters section would then yield a successful ingestion and a working Constraint.

shell
$ cat << EOF | kubectl apply -f -
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: ns-must-have-gk
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    labels: ["gatekeeper"]
EOF
k8srequiredlabels.constraints.gatekeeper.sh/ns-must-have-gk created
shell
$ kubectl create ns foobar
Error from server ([ns-must-have-gk] you must provide labels: {"gatekeeper"}): admission webhook "validation.gatekeeper.sh" denied the request: [ns-must-have-gk] you must provide labels: {"gatekeeper"}

Enable OPA Rego v1 syntax in ConstraintTemplates

Gatekeeper 3.19 ships with ability to use OPA Rego v1 as policy language in ConstraintTemplates. Using Rego v1 syntax is opt-in, by default only Rego v0 is allowed. You can use below spec to enable Rego v1 syntax:

yaml
...
  targets:
    - target: admission.k8s.gatekeeper.sh
      code:
        - engine: Rego
          source:
            version: "v1"
            rego: |
              <v1-rego-code>
...

:::note
Rego v1 syntax can only be used under targets[_].code[_].[engine: Rego].source with version: "v1". No need to add import rego.v1 to use rego v1 syntax.
:::

Here is a sample ConstraintTemplate using Rego v1 syntax:

yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        # Schema for the `parameters` field
        openAPIV3Schema:
          type: object
          properties:
            message:
              type: string
            labels:
              type: array
              items:
                type: object
                properties:
                  key:
                    type: string
                  allowedRegex:
                    type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      code:
        - engine: Rego
          source:
            version: "v1"
            rego: |
              package k8srequiredlabels

              violation contains 
                {"msg": msg, "details": {"missing_labels": missing}} 
                if {
                  provided := {label | input.review.object.metadata.labels[label]}
                  required := {label | label := input.parameters.labels[_]}
                  missing := required - provided
                  count(missing) > 0
                  msg := sprintf("you must provide labels: %v", [missing])
                }

Built-in variables across all engines

Common variables

Rego variables

Variable Description
input.review Contains input request object under review
input.review.namespaceObject Contains the full Namespace object (with metadata, labels, annotations) for namespaced resources. This is null for cluster-scoped resources.
input.parameters Contains constraint parameters e.g. input.parameters.repos see example
data.lib It serves as an import path for helper functions defined under libs in ConstraintTemplate, e.g. data.lib.exempt_container.is_exempt see example
data.inventory Refers to a structure that stores synced cluster resources. It is used in Rego policies to validate or enforce referential rules based on the current state of the cluster. e.g. unique ingress host example

CEL variables

Variable Description
variables.params Contains constraint parameters e.g. variables.params.labels see example
variables.anyObject Contains either an object or (on DELETE requests) oldObject, see example
namespaceObject Contains the full Namespace object (with metadata, labels, annotations) for namespaced resources. This is null for cluster-scoped resources. This aligns with the Kubernetes Validating Admission Policy namespaceObject variable.

Field Precedence in ConstraintTemplate

ConstraintTemplates support multiple ways to define policy code with the following precedence rules: (1) The legacy spec.targets[].rego field takes precedence over any Rego engine defined in spec.targets[].code[]. (2) When multiple engines are defined in the code array, only one engine is evaluated—the K8sNativeValidation (CEL) engine has higher priority than the Rego engine with no fallback mechanism. Best practice: Use the code array exclusively and define policy logic in only one engine (either Rego for complex policies with referential constraints and external data, or CEL for simpler validations) to avoid confusion about which policy will be evaluated.

For more information on CEL integration and engine precedence, see the Integration with Kubernetes Validating Admission Policy documentation.

Managing Rego at Scale with Helm

When managing many policies or writing complex policies, it is often desirable to keep the Rego logic in separate .rego files. This allows you to leverage tools like opa fmt and opa test for unit testing, and maintain better code readability.

Instead of manually copying and pasting your Rego code into the ConstraintTemplate YAML, you can use Helm to compile the template.

By utilizing the Files.Get function in Helm, you can inject the contents of an external Rego file directly into your ConstraintTemplate at deployment time.

For example, your Helm template (templates/constrainttemplate.yaml) might look like this:

yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        openAPIV3Schema:
          type: object
          properties:
            labels:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        {{ .Files.Get "policies/requiredlabels.rego" | indent 8 }}

With this approach, you can maintain policies/requiredlabels.rego alongside your unit tests in the same repository, and Helm will seamlessly inline it when you install or upgrade the chart.


Customize Admission (`website/docs/customize-admission.md`)


id: customize-admission title: Customizing Admission Behavior

Gatekeeper is a Kubernetes admission webhook
whose default configuration can be found in the gatekeeper.yaml manifest file. By default, it is
a ValidatingWebhookConfiguration resource named gatekeeper-validating-webhook-configuration.

Currently the configuration specifies two webhooks: one for checking a request against
the installed constraints and a second webhook for checking labels on namespace requests
that would result in bypassing constraints for the namespace. The namespace-label webhook
is necessary to prevent a privilege escalation where the permission to add a label to a
namespace is equivalent to the ability to bypass all constraints for that namespace.
You can read more about the ability to exempt namespaces by label here.

Because Kubernetes adds features with each version, if you want to know how the webhook can be configured it
is best to look at the official documentation linked at the top of this section. However, two particularly important
configuration options deserve special mention: timeouts and
failure policy.

Timeouts allow you to configure how long the API server will wait for a response from the admission webhook before it
considers the request to have failed. Note that setting the timeout longer than the overall request timeout
means that the main request will time out before the webhook's failure policy is invoked, causing the
request to fail.

Failure policy controls what happens when a webhook fails for whatever reason. Common
failure scenarios include timeouts, a 5xx error from the server or the webhook being unavailable.
You have the option to ignore errors, allowing the request through, or failing, rejecting the request.
This results in a direct tradeoff between availability and enforcement.

Currently Gatekeeper is defaulting to using Ignore for the constraint requests, which means
constraints will not be enforced at admission time if the webhook is down or otherwise inaccessible.
This is because we cannot know the operational details of the cluster Gatekeeper is running on and
how that might affect webhook uptime. For a more detailed treatment of this topic, see our docs
on failing closed.

The namespace label webhook defaults to Fail, this is to help ensure that policies preventing
labels that bypass the webhook from being applied are enforced. Because this webhook only gets
called for namespace modification requests, the impact of downtime is mitigated, making the
theoretical maximum availability less of an issue.

Because the manifest is available for customization, the webhook configuration can
be tuned to meet your specific needs if they differ from the defaults.

Enable Validation of Delete Operations

Caveats

Deletes are not Auditable

Once a resource is deleted, it is gone. This means that non-compliant deletes cannot be
audited via Gatekeeper's audit mechanism, and increases the importance of webhook-based
enforcement.

Policies Against DELETE May Not be Perfectly Enforced

Since the webhook fails open by default (as described earlier on this page), it is possible
for admission requests to have imperfect enforcement, which means some non-compliant deletes
may still go through despite the policy. Normally such failures of webhook enforcement could
be caught by audit, but deletes are not auditable.

It is possible to improve the likelihood of enforcement by configuring the webhook to
fail closed.

How to Enable Validation of Delete Operations

To enable Delete operations for the validation.gatekeeper.sh admission webhook, add "DELETE" to the list of operations in the gatekeeper-validating-webhook-configuration ValidatingWebhookConfiguration as seen in this deployment manifest of gatekeeper: here

So you have

yaml
operations:
   - CREATE
   - UPDATE
   - DELETE

You can now check for deletes.

How to Enable Validation of Connect Operations

This operation allows gatekeeper to review and validate kubectl exec commands via the validation webhook.

To enable Connect operations for the validation.gatekeeper.sh admission webhook, add "CONNECT" to the list of operations in the gatekeeper-validating-webhook-configuration.

yaml
operations:
    - CREATE
    - UPDATE
    - CONNECT

Customize Startup (`website/docs/customize-startup.md`)


id: customize-startup title: Customizing Startup Behavior

Allow retries when adding objects to OPA

Gatekeeper's webhook servers undergo a bootstrapping period during which they are unavailable until the initial set of resources (constraints, templates, synced objects, etc...) have been ingested. This prevents Gatekeeper's webhook from validating based on an incomplete set of policies. This wait-for-bootstrapping behavior can be configured.

The --readiness-retries flag defines the number of retry attempts allowed for an object (a Constraint, for example) to be successfully added to OPA. The default is 0. A value of -1 allows for infinite retries, blocking the webhook until all objects have been added to OPA. This guarantees complete enforcement, but has the potential to indefinitely block the webhook from serving requests.

Enable profiling using `pprof`

The --enable-pprof flag enables an HTTP server for profiling using the pprof library. By default, it serves to localhost:6060 but the port can be customized with the --pprof-port flag.

Disable certificate generation and rotation for Gatekeeper's webhook

By default, Gatekeeper uses open-policy-agent/cert-controller to handle the webhook's certificate rotation and generation. If you want to use a third-party solution, you may disable the cert-controller feature using --disable-cert-rotation.

Disable OPA built-in functions

The --disable-opa-builtin flag disables specific OPA built-ins functions. Starting with v3.8.0, Gatekeeper disables the http.send built-in function by default. For more information, please see external data.

[Alpha] Emit admission and audit events

The --emit-admission-events flag enables the emission of all admission violations as Kubernetes events. This flag is in alpha stage and it is set to false by default.

The --emit-audit-events flag enables the emission of all audit violation as Kubernetes events. This flag is in alpha stage and it is set to false by default.

The --admission-events-involved-namespace flag controls which namespace admission events will be created in. When set to true, admission events will be created in the namespace of the object violating the constraint. If the object has no namespace (ie. cluster scoped resources), they will be created in the namespace Gatekeeper is installed in. Setting to false will cause all admission events to be created in the Gatekeeper namespace.

The --audit-events-involved-namespace flag controls which namespace audit events will be created in. When set to true, audit events will be created in the namespace of the object violating the constraint. If the object has no namespace (ie. cluster scoped resources), they will be created in the namespace Gatekeeper is installed in. Setting to false will cause all audit events to be created in the Gatekeeper namespace.

There are four types of events that are emitted by Gatekeeper when the emit event flags are enabled:

Event Description
FailedAdmission The Gatekeeper webhook denied the admission request (default behavior).
WarningAdmission When enforcementAction: warn is specified in the constraint.
DryrunViolation When enforcementAction: dryrun is specified in the constraint.
AuditViolation A violation is detected during an audit.

❗ Warning: if the same constraint and violating resource tuple was emitted for more than 10 times in a 10-minute rolling interval, the Kubernetes event recorder will aggregate the events, e.g.

text
39s         Warning   FailedAdmission   namespace/test      (combined from similar events):  Admission webhook "validation.gatekeeper.sh" denied request, Resource Namespace: , Constraint: ns-must-have-gk, Message: you must provide labels: {"gatekeeper"}

Gatekeeper might burst 25 events about an object, but limit the refill rate to 1 new event every 5 minutes. This will help control the long-tail of events for resources that are always violating the constraint.

[Alpha] Emit API server audit annotations for admission evaluations

The --emit-admission-audit-annotations flag adds audit annotations to validation requests that reach Gatekeeper policy evaluation. It is disabled by default. When enabled, it annotates violations only unless --admission-audit-annotations-include-success=true is also set. Requests skipped before policy evaluation, such as excluded namespaces, are not annotated.

The validation webhook returns one evaluation entry in AdmissionResponse.auditAnnotations. The API server prefixes that key with the webhook name, producing validation.gatekeeper.sh/evaluation in the API audit event. Its versioned JSON value contains Gatekeeper's allowed decision, bounded summaries of deny, warn, and dryrun violations, the total and included violation counts, and a truncation indicator. The decision is specific to Gatekeeper; another admission plugin or a later API server error can still reject the request. Request identity belongs to the enclosing audit event and is not repeated in this payload. The AdmissionReview UID, event type, evaluated resource kind/version, resource labels, and Constraint annotations are also intentionally omitted. The value is limited to 10 KiB. Long messages, policy-provided details, or additional violations may be omitted, and the payload reports truncation.

With --admission-audit-annotations-include-success=true, an evaluation with no violations produces this custom annotation value; otherwise it produces no custom annotation:

json
{"schemaVersion":"v1","allowed":true,"violations":[],"totalViolations":0,"includedViolations":0,"truncated":false}

When admission audit annotations are enabled on a process performing the generate operation, generated ValidatingAdmissionPolicyBindings add the Kubernetes Audit action alongside Deny or Warn; dryrun already maps to Audit. Failed native validations therefore produce Kubernetes' standard validation.policy.admission.k8s.io/validation_failure annotation. Kubernetes controls that annotation's content and aggregation; it is not an exhaustive list of all matching bindings. By default, generated ValidatingAdmissionPolicies omit the custom evaluation marker and rely on this native failure annotation.

To also annotate successful evaluations, set --admission-audit-annotations-include-success=true. It defaults to false and has no effect unless --emit-admission-audit-annotations is enabled. Success here means zero violations, not simply an allowed request: deny, warn, and dryrun violations remain annotated regardless of this setting. When both flags are enabled, generated VAPs add an evaluation audit annotation with the value true when at least one binding evaluates the request. Kubernetes deduplicates this constant across matching bindings, keeping the value bounded without listing matching Constraint names. This option does not re-evaluate CEL expressions or change admission decisions. For Helm, set emitAdmissionAuditAnnotations=true for violations-only auditing, and also set admissionAuditAnnotationsIncludeSuccess=true to include successful evaluations. If the webhook and native VAP enforcement points both evaluate a request, both may add audit entries.

For split deployments, configure both flags consistently on the validation webhook process and the process performing the generate operation. Restart the affected processes when changing these startup settings; generated VAPs are reconciled to the selected mode. API server audit logging must be enabled at Metadata or a higher level. Audit annotations do not create Kubernetes Event resources, do not annotate admitted objects, and do not change admission decisions. The violation-export payload is unaffected by these settings. Policy messages and details may contain user-controlled or sensitive data, so protect access to the audit backend.

[Beta] Enable mutation logging and annotations

The --log-mutations flag enables logging of mutation events and errors.

The --mutation-annotations flag adds the following two annotations to mutated objects:

Annotation Value
gatekeeper.sh/mutation-id The UUID of the mutation.
gatekeeper.sh/mutations A list of comma-separated mutations in the format of <MutationType>/<MutationNamespace>/<MutationName>:<MutationGeneration> that are applied to the object.

❗ Note that this will break the idempotence requirement that Kubernetes sets for mutation webhooks. See the Kubernetes docs here for more details

[Alpha] Remote Cluster Mode for Gatekeeper

The --enable-remote-cluster flag enables Gatekeeper to run in a local (management) cluster while enforcing policies on a separate target cluster specified via --kubeconfig. This is designed for hosted control plane architectures where the target cluster's API server runs within the management cluster.

📖 For a full end-to-end setup walkthrough, installing CRDs across both clusters, deploying Gatekeeper, wiring webhook certificates, and a smoke test see the Remote Cluster Mode guide.

When to Use

Use remote cluster mode when:

  • Gatekeeper runs in a local (management) cluster
  • You want to enforce policies on a remote target cluster

Configuration

bash
--enable-remote-cluster            # Enable remote cluster mode
--kubeconfig=/path/to/target.yaml  # Kubeconfig for target cluster

How It Works

Status resources live on the management cluster alongside the Gatekeeper pod, with OwnerReferences pointing to the pod. This enables automatic garbage collection — when a pod restarts, Kubernetes cleans up its old status resources automatically.

RBAC Requirements

Gatekeeper needs permissions on the management/local cluster to resolve its pod identity and manage status resources:

yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: gatekeeper-manager-role
  namespace: gatekeeper-system
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get"]
- apiGroups: ["status.gatekeeper.sh"]
  resources: ["*"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

Migration from Previous Versions

If upgrading from a version where --enable-remote-cluster stored status resources on the target cluster, you may have orphaned status resources on the target cluster. Run the following to clean them up:

bash
kubectl delete constrainttemplatepodstatuses,constraintpodstatuses,mutatorpodstatuses,expansiontemplatepodstatuses,configpodstatuses,providerpodstatuses,connectionpodstatuses \
  -n gatekeeper-system --all --context <target-cluster-context>

This is a one-time cleanup. After upgrading, status resources are automatically managed on the management cluster.

bash
# List all pod names referenced in status resources (repeat for each status type)
kubectl get constrainttemplatepodstatuses -n gatekeeper-system \
  -o jsonpath="{.items[*].metadata.labels['gatekeeper\.sh/pod']}" | tr ' ' '\n' | sort -u

# Compare with running Gatekeeper pods in local cluster
kubectl get pods -n gatekeeper-system -l control-plane=controller-manager -o name

To clean up orphaned resources after identifying old pod names:

bash
# Delete status resources for a specific old pod
OLD_POD="gatekeeper-controller-manager-old-xyz"
kubectl delete constrainttemplatepodstatuses,constraintpodstatuses,mutatorpodstatuses,expansiontemplatepodstatuses,configpodstatuses,providerpodstatuses,connectionpodstatuses \
  -n gatekeeper-system -l gatekeeper.sh/pod=$OLD_POD

Other Configuration Options

For the complete list of configuration flags for your specific version of Gatekeeper, run the Gatekeeper binary with the --help flag. For example:

docker run openpolicyagent/gatekeeper:v3.10.0-beta.0 --help

To ensure you are seeing all relevant flags, be sure the image tag (:3.10.0-beta.0 above) corresponds with the version of Gatekeeper you are running.


Debug (`website/docs/debug.md`)


id: debug title: Debugging

NOTE: Verbose logging with DEBUG level can be turned on with --log-level=DEBUG. By default, the --log-level flag is set to minimum log level INFO. Acceptable values for minimum log level are [DEBUG, INFO, WARNING, ERROR]. In production, this flag should not be set to DEBUG.

Viewing the Request Object

A simple way to view the request object is to use a constraint/template that
denies all requests and outputs the request object as its rejection message.

Example template:

yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sdenyall
spec:
  crd:
    spec:
      names:
        kind: K8sDenyAll
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sdenyall

        violation[{"msg": msg}] {
          msg := sprintf("REVIEW OBJECT: %v", [input.review])
        }

Example constraint:

yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDenyAll
metadata:
  name: deny-all-namespaces
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]

Tracing

In debugging decisions and constraints, a few pieces of information can be helpful:

  • Cached data and existing rules at the time of the request
  • A trace of the evaluation
  • The input document being evaluated

Writing out all the information above for every request would be expensive in terms of memory and load on the Gatekeeper server, which may lead to requests timing out. It would also be hard
to find the relevant logs for a given request.

For tracing, Gatekeeper requires operators to specify
resources and requesting users for which traces will be logged. They can do so by
configuring the Config resource, which lives in the gatekeeper-system namespace.

Below is an example of a config resource:

yaml
apiVersion: config.gatekeeper.sh/v1alpha1
kind: Config
metadata:
  name: config
  namespace: "gatekeeper-system"
spec:
  # Data to be replicated into OPA
  sync:
    syncOnly:
      - group: ""
        version: "v1"
        kind: "Namespace"
  validation:
    # Requests for which we want to run traces
    traces:
        # The requesting user for which traces will be run
        # This field is required.
        # To trace multiple users, feel free to pass in a list.
        # To trace controllers, use the service accounts of those controllers.
      - user: "[email protected]"
        kind:
          # The group, version, kind for which we want to run a trace
          group: ""
          version: "v1"
          kind: "Namespace"
        # If dump is defined and set to `All`, also dump the state of OPA
        dump: "All"

Traces will be written to the stdout logs of the Gatekeeper controller.

If there is an error in the Rego in the ConstraintTemplate, there are cases where it is still created via kubectl apply -f [CONSTRAINT_TEMPLATE_FILENAME].yaml.

When applying the constraint using kubectl apply -f constraint.yaml with a ConstraintTemplate that contains incorrect Rego, and error will occur: error: unable to recognize "[CONSTRAINT_FILENAME].yaml": no matches for kind "[NAME_OF_CONSTRAINT]" in version "constraints.gatekeeper.sh/v1beta1".

To find the error, run kubectl get -f [CONSTRAINT_TEMPLATE_FILENAME].yaml -o yaml. Build errors are shown in the status field.


Developers (`website/docs/developers.md`)


id: developers title: Developers

This section describes how Gatekeeper developers can leverage kind and Tilt for rapid iterative development. Kind allows developers to quickly provision a conformant Kubernetes cluster using Docker and Tilt enables smart rebuilds and live updates of your Kubernetes workload during development time.

Prerequisites

  1. kind v0.11.0 or newer
  2. Tilt v0.25.0 or newer

Getting started

Create a kind cluster with a local registry

Kind cluster with a local registry will enable faster image pushing and pulling:

bash
./third_party/github.com/tilt-dev/kind-local/kind-with-registry.sh

If you would like to customize the local registry port on your machine (the default port is 5000), you can run export KIND_REGISTRY_PORT= to customize it.

Create `tilt-settings.json`

tilt-settings.json contains various settings that developers can customize when deploying gatekeeper to a local kind cluster. Developers can create the JSON file under the project root directory:

json
{
    "helm_values": {
        "controllerManager.metricsPort": 8080,
        "enableExternalData": true
    },
    "trigger_mode": "manual"
}

`tilt-settings.json` fields

  • helm_values (Map, default={}): A map of helm values to be injected when deploying manifest_staging/charts/gatekeeper to the kind cluster.

  • trigger_mode (String, default="auto"): Optional setting to configure if tilt should automatically rebuild on changes. Set to manual to disable auto-rebuilding and require users to trigger rebuilds of individual changed components through the UI.

Run `make tilt`

bash
make tilt

Output

text
/* Detailed source-code truncated for AI context efficiency. */

Start developing!

If you have trigger mode set to auto, any changes in the source code will trigger a rebuild of the gatekeeper manager binary. The build will subsequently trigger a rebuild of the gatekeeper manager image, load it to your kind cluster, and restart the deployment.

If you have trigger mode set to manual, you can trigger a manager build manually in the local Tilt UI portal. By default, it is located at http://localhost:10350/

Tear down the kind cluster

To tear down the kind cluster and its local registry:

bash
./third_party/github.com/tilt-dev/kind-local/teardown-kind-with-registry.sh

Emergency (`website/docs/emergency.md`)


id: emergency title: Emergency Recovery

If a situation arises where Gatekeeper is preventing the cluster from operating correctly,
the webhook can be disabled. This will remove all Gatekeeper admission checks. Assuming
the default webhook name has been used this can be achieved by running:

kubectl delete validatingwebhookconfigurations.admissionregistration.k8s.io gatekeeper-validating-webhook-configuration

Redeploying the webhook configuration will re-enable Gatekeeper.


Enforcement Points (`website/docs/enforcement-points.md`)


id: enforcement-points title: Enforcement points in Gatekeeper

Understanding Enforcement Points

An enforcement point defines the location where enforcement happens. Below are the different enforcement points available in Gatekeeper:

  • validation.gatekeeper.sh indicates that enforcement should be carried out by Gatekeeper's validating webhook for a constraint. Supports templates with CEL and Rego.
  • gator.gatekeeper.sh indicates that enforcement should be carried out in shift-left via gator-cli for a constraint. Supports templates with CEL and Rego.
  • audit.gatekeeper.sh indicates that on-cluster resources should be audited and violations should be reported for the resources that are in violation of constraint. Supports templates with CEL and Rego.
  • vap.k8s.io indicates that enforcement should be carried out by Validating Admission Policy for a constraint. Supports templates with CEL.

How to use different enforcement points in constraint

By default, a constraint will be enforced at all enforcement points with common enforcement action defined in spec.enforcementAction. However, you can choose to enforce a constraint at specific enforcement points with different actions using enforcementAction: scoped and spec.scopedEnforcementActions. Below are examples and use cases that utilize different enforcement actions for different enforcement points.

:::note
spec.enforcementAction: scoped is needed to customize specific enforcement point/enforcement action behavior. If spec.enforcementAction: scoped is not provided, spec.scopedEnforcementActions is ignored and the provided enforcementAction will be applied across all enforcement points.
:::

Deny in shift-left and warn at admission

You are trying out a new constraint template, and you want to deny violating resources in shift-left testing, but do not want to block any resources admitted to clusters to reduce impact for faulty rejections. You may want to use deny action for the gator.gatekeeper.sh shift-left enforcement point and warn for the validation.gatekeepet.sh admission webhook enforcement point. The below constraint satisfies this use case.

yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
  name: prod-repo-is-openpolicyagent
spec:
...
  enforcementAction: scoped
  scopedEnforcementActions:
  - action: warn
    enforcementPoints:
    - name: "validation.gatekeeper.sh"
  - action: deny
    enforcementPoints:
    - name: "gator.gatekeeper.sh"
...

Note: The audit enforcement point is not included unless explicitly added to scopedEnforcementActions.enforcementPoints or if scopedEnforcementActions.enforcementPoints is set to "*".

Only audit

You are depending on external-data or referential policies for validating resources. These type of validation may be latency sensitive and may take longer to evaluate. To avoid such situation you may want to only use audit.gatekeeper.sh enforcement point to not face any delay at admission time, but still get the information about violating resources from Gatekeeper's audit operation. Here is the constraint for only using audit.gatekeeper.sh enforcement point.

yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
  name: prod-repo-is-openpolicyagent
spec:
...
  enforcementAction: scoped
  scopedEnforcementActions:
  - action: deny
    enforcementPoints:
    - name: "audit.gatekeeper.sh"
...

Enforcing through Validating Admission Policy and using Gatekeeper as fall-back validation mechanism

You want to utilize in-tree Validating Admission Policy for faster turn around time. But you want to make sure that in case Validating Admission Policy fails-open, Gatekeeper blocks faulty resources from being created. Here is how you can achieve the same.

yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
  name: prod-repo-is-openpolicyagent
spec:
...
  enforcementAction: scoped
  scopedEnforcementActions:
  - action: deny
    enforcementPoints:
    - name: "vap.k8s.io"
    - name: "validation.gatekeeper.sh"
...

Please refer to VAP/VAPB generation behavior.

Enforcing through Validating Admission Policy and using audit from Gatekeeper

You want to utilize in-tree Validating Admission Policy for faster turn around time and only want to use audit operation from Gatekeeper to get information about violation resources on-cluster. Here is the constraint that users vap.k8s.io and audit.gatekeeper.sh enforcement points.

yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
  name: prod-repo-is-openpolicyagent
spec:
...
  enforcementAction: scoped
  scopedEnforcementActions:
  - action: deny
    enforcementPoints:
    - name: "vap.k8s.io"
    - name: "audit.gatekeeper.sh"
...

Checkout this demo to understand how enforcement points and actions work.


Examples (`website/docs/examples.md`)


id: examples title: Examples

The demo/basic directory contains the above examples of simple constraints, templates and configs to play with.

The demo/agilebank directory contains more complex examples based on a slightly more realistic scenario. Both folders have a handy demo script to step you through the demos.


Exempt Namespaces (`website/docs/exempt-namespaces.md`)


id: exempt-namespaces title: Exempting Namespaces

Feature State: The Config resource is currently alpha.

Exempting Namespaces from Gatekeeper using config resource

The "Config" resource must be named config for it to be reconciled by Gatekeeper. Gatekeeper will ignore the resource if you do not name it config.

The config resource can be used as follows to exclude namespaces from certain processes for all constraints in the cluster. An asterisk can be used for wildcard matching (e.g. kube-*). To exclude namespaces at a constraint level, use excludedNamespaces in the constraint instead.

yaml
apiVersion: config.gatekeeper.sh/v1alpha1
kind: Config
metadata:
  name: config
  namespace: "gatekeeper-system"
spec:
  match:
    - excludedNamespaces: ["kube-*", "my-namespace"]
      processes: ["*"]
    - excludedNamespaces: ["audit-excluded-ns"]
      processes: ["audit"]
    - excludedNamespaces: ["audit-webhook-sync-excluded-ns"]
      processes: ["audit", "webhook", "sync"]
    - excludedNamespaces: ["mutation-excluded-ns"]
      processes: ["mutation-webhook"]
...

Available processes:

  • audit process exclusion will exclude resources from specified namespace(s) in audit results.
  • webhook process exclusion will exclude resources from specified namespace(s) from the admission webhook.
  • sync process exclusion will exclude resources from specified namespace(s) from being synced into OPA.
  • mutation-webhook process exclusion will exclude resources from specified namespace(s) from the mutation webhook.
  • * includes all current processes above and includes any future processes.

Exempting Namespaces from the Gatekeeper Admission Webhook using `--exempt-namespace` flag

Note that the following only exempts resources from the admission webhook. They will still be audited. Editing individual constraints or config resource is
necessary to exclude them from audit.

If it becomes necessary to exempt a namespace from Gatekeeper webhook entirely (e.g. you want kube-system to bypass admission checks), here's how to do it:

  1. Make sure the validating admission webhook configuration for Gatekeeper has the following namespace selector:

    yaml
    namespaceSelector:
        matchExpressions:
        - key: admission.gatekeeper.sh/ignore
          operator: DoesNotExist

    the default Gatekeeper manifest should already have added this. The default name for the
    webhook configuration is gatekeeper-validating-webhook-configuration and the default
    name for the webhook that needs the namespace selector is validation.gatekeeper.sh

  2. Tell Gatekeeper it's okay for the namespace to be ignored by adding a flag to the pod:
    --exempt-namespace=<NAMESPACE NAME>. This step is necessary because otherwise the
    permission to modify a namespace would be equivalent to the permission to exempt everything
    in that namespace from policy checks. This way a user must explicitly have permissions
    to configure the Gatekeeper pod before they can add exemptions.

    In order to add the admission.gatekeeper.sh/ignore label to a namespace, that namespace must be listed under the gatekeeper controllerManager.exemptNamespaces parameter when installing via Helm.

  3. Add the admission.gatekeeper.sh/ignore label to the namespace. The value attached
    to the label is ignored, so it can be used to annotate the reason for the exemption.

Similarly, you can also enable the exemption of entire groups of namespaces using the --exempt-namespace-prefix and --exempt-namespace-suffix flags. Using these flags allows the admission.gatekeeper.sh/ignore label to be added to any namespace that matches the supplied prefix or suffix.

Difference between exclusion using config resource and `--exempt-namespace` flag

The difference is at what point in the admission process an exemption occurs.

If you use --exempt-namespace flag and admission.gatekeeper.sh/ignore label, Gatekeeper's webhook will not be called by the API server for any resource in that namespace. That means that Gatekeeper being down should have no effect on requests for that namespace.

If you use the config method, Gatekeeper itself evaluates the exemption. The benefit there is that we have more control over the syntax and can be more fine-grained, but it also means that the API server is still calling the webhook, which means downtime can have an impact.


Expansion (`website/docs/expansion.md`)


id: expansion title: Validating Workload Resources using ExpansionTemplate

Feature State: Gatekeeper version v3.10+ (alpha), version 3.13+ (beta)

❗This feature is in beta stage. It is enabled by default. To disable the feature,
set the enable-generator-resource-expansion flag to false.

Motivation

A workload resource is a resource that creates other resources, such as a
Deployment or Job. Gatekeeper can be configured to reject workload resources
that create a resource that violates a constraint.

`ExpansionTemplate` explained

An ExpansionTemplate is a custom resource that Gatekeeper will use to create temporary, fake resources and validate the constraints against them. We refer to these resources that Gatekeeper creates for validation purposes as expanded resources. We refer to the Deployment or other workload resource as the parent resource and the act of creating those expanded resources as expansion.

The ExpansionTemplate custom resource specifies:

  • Which workload resource(s) should be expanded, specified by their GVK
  • The GVK of the expanded resources
  • The "source" as defined in the field templateSource on the parent resource, which is used as the blueprint for the expanded resource. For example, in a case where a
    Deployment expands into a Pod, spec.template would typically be the
    source.
  • Optionally, an enforcementAction override can be used when validating expanded
    resources. If this field is set, any violations against the expanded resource
    will use this enforcement action. If an enforcement action is not specified by
    the ExpansionTemplate, the enforcement action set by the Constraint in
    violation will be used.

Here is an example of a ExpansionTemplate that specifies that DaemonSet,
Deployment, Job, ReplicaSet, ReplicationController, and StatefulSet
resources should be expanded into a Pod.

yaml
apiVersion: expansion.gatekeeper.sh/v1alpha1
kind: ExpansionTemplate
metadata:
  name: expand-deployments
spec:
  applyTo:
    - groups: ["apps"]
      kinds: ["DaemonSet", "Deployment", "ReplicaSet", "StatefulSet"]
      versions: ["v1"]
    - groups: [""]
      kinds: ["ReplicationController"]
      versions: ["v1"]
    - groups: ["batch"]
      kinds: ["Job"]
      versions: ["v1"]
  templateSource: "spec.template"
  enforcementAction: "warn" # This will overwrite all constraint enforcement actions for the GVKs below that result from the GVKs above.
  generatedGVK:
    kind: "Pod"
    group: ""
    version: "v1"

With this ExpansionTemplate, any constraints that are configured to target
Pods will also be evaluated on the expanded pods that Gatekeeper creates when a Deployment or ReplicaSet is
being reviewed. Any violations created against these expanded Pods, and only these expanded Pods, will have their
enforcement action set to warn, regardless of the enforcement actions
specified by the Constraint in violation.

To see how to use Mutators and Constraints to exclusively review expanded resources, see the Match Source section below.

Limitations

Sidecars and Mutators

It may not always be possible to build an accurate representation of an
expanded resource by looking at the workload resource alone. For example, suppose a
cluster is using Istio, which will inject a sidecar container on specific
resources. This sidecar configuration is not specified in the config of the
workload resource (i.e. Deployment), but rather injected by Istio's webhook. In
order to accurately represent expanded resources modified by controllers or
webhooks, Gatekeeper leverages its
Mutations
feature to allow expanded resources to be manipulated into their desired form. In
the Istio example, Assign and ModifySet mutators could be configured to
mimic Istio sidecar injection. For further details on mutating expanded resources
see the Match Source section below, or to see a working example,
see the Mutating Example section.

Unknown Data

Any resources configured for expansion will be expanded by both the validating
webhook and
Audit. This
feature will only be enabled if a user creates an ExpansionTemplate that
targets any resources that exist on the cluster.

Note that the accuracy of enforcement depends on how well the expanded resource
resembles the real thing. Mutations can help with this, but 100% accuracy is
impossible because not all fields can be predicted. For instance, Deployments
create pods with random names. Inaccurately expanded resources may lead to over- or under-
enforcement. In the case of under-enforcement, the expanded pod should still be
rejected. Finally, non-state-based policies (those that rely on transient
metadata such as requesting user or time of creation) cannot be enforced
accurately. This is because such metadata would necessarily be different when
creating the expanded resource. For example, a Deployment is created using the
requesting user's account, but the pod creation request comes from the service
account of the Deployment controller.

Configuring Expansion

Expansion behavior is configured through the ExpansionTemplate custom
resource. Optionally, users can create Mutation custom resources to customize
how resources are expanded. Mutators with the source: Generated field will
only be applied when expanding workload resources, and will not mutate real
resources on the cluster. If the source field is not set, the Mutation will
apply to both expanded resources and real resources on the cluster.

Users can test their expansion configuration using the
gator expand CLI
.

Match Source

The source field on the match API, present in the Mutation
and Constraint kinds, specifies if the config should match Generated (
i.e. fake) resources, Original resources, or both. The source field is
an enum which accepts the following values:

  • Generated – the config will only apply to expanded resources, and will not
    apply to any real resources on the cluster
  • Original – the config will only apply to Original resources, and will not
    affect expanded resources
  • All – the config will apply to both Generated and Original resources.
    This is the default value.

For example, suppose a cluster's ReplicaSet controller adds a default value
for fooField when creating Pods that cannot reasonably be added to the
ReplicaSet's spec.template. If a constraint relies on these default values,
a user could create a Mutation custom resource that modifies expanded resources,
like so:

yaml
apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: assign-foo-field
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  location: "spec.containers[name: *].fooField"
  parameters:
    assign:
      value: "Bar"
  match:
    source: "Generated"
    scope: Cluster
    kinds:
      - apiGroups: []
        kinds: []

Similarly, Constraints can be configured to only target fake resources by
setting the Constraint's spec.match.source field to Generated. This can
also be used to define different enforcement actions for expanded resources and
original resources.

For example, suppose a cluster has a policy that blocks all standalone pods, but allows them to be created as part of a workload resource, such as Deployment. A user could create a Constraint that only targets original resources, like so:

yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: block-standalone-pods
metadata:
  name: block-standalone-pods
spec:
  match:
    source: Original
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]

Mutating Example

Suppose a cluster is using Istio, and has a policy configured to ensure
specified Pods have an Istio sidecar. To validate Deployments that would create
Pods which Istio will inject a sidecar into, we need to use mutators to mimic
the sidecar injection.

What follows is an example of:

  • an ExpansionTemplate configured to expand Deployments into Pods
  • an Assign mutator to add the Istio sidecar container to Pods
  • a ModifySet mutator to add the proxy and sidecar args
  • an inbound Deployment, and the expanded Pod

Note that the Mutators set the source: Generated field, which will cause
them to only be applied when expanding resources specified
by ExpansionTemplates. These Mutators will not affect any real resources on
the cluster.

text
/* Detailed source-code truncated for AI context efficiency. */

When expanded, the above configs will produce the following Pod:

yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
spec:
  containers:
  - args:
    - /bin/sh
    image: nginx:1.14.2
    name: nginx
    ports:
    - containerPort: 80
  - args:
    - proxy
    - sidecar
    image: docker.io/istio/proxyv2:1.15.0
    imagePullPolicy: IfNotPresent
    name: istio-proxy
    ports:
    - containerPort: 15090
      name: http-envoy-prom
      protocol: TCP
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop:
        - ALL

Export Driver Walkthrough (`website/docs/export-driver-walkthrough.md`)


id: export-driver title: Export Interface/Driver walkthrough

This guide provides an overview of the driver interface, including details of its structure and functionality. Additionally, it offers instructions on adding a new driver and utilizing different backends to export audit violations.

Driver interface

go
type Driver interface {
	// Publish publishes single message with specific subject using a connection
	Publish(ctx context.Context, connectionName string, data interface{}, subject string) error

	// CloseConnection closes a connection
	CloseConnection(connectionName string) error

	// UpdateConnection updates an existing connection
	UpdateConnection(ctx context.Context, connectionName string, config interface{}) error

	// CreateConnection creates new connection
	CreateConnection(ctx context.Context, connectionName string, config interface{}) error
}

As an example, the Dapr driver implements these methods to publish message and manage connection to do so. Please refer to dapr.go to understand the logic that goes in each of these methods.

How to add new driver to export audit violations to foo backend

A driver must maintain a map of open connections associated with backend foo.

go
type Connection struct {
	// properties needed for individual connection
}

type Foo struct {
	openConnections map[string]Connection
}

const (
	Name = "foo"
)

var Connections = &Foo{
	openConnections: make(map[string]Connection),
}

A driver must implement the Driver interface.

go
func (r *Foo) Publish(ctx context.Context, connectionName string, data interface{}, subject string) error {
  ...
}

func (r *Foo) CloseConnection(connectionName string) error {
  ...
}

func (r *Foo) UpdateConnection(ctx context.Context, connectionName string, config interface{}) error {
  ...
}

func (r *Foo) CreateConnection(ctx context.Context, connectionName string, config interface{}) error {
  ...
}

This newly added driver's Connections exported variable must be added to the map of supportedDrivers in system.go. For example,

go
var supportedDrivers = map[string]driver.Driver{
	dapr.Name: dapr.Connections,
  foo.Name: foo.Connections,
}

And thats it! Exporter system will take the newly added driver into account and a Connection custom resource to establish connection to export message is created.

How to establish connections to different backend

To enable audit to use this driver to publish messages, a Connection custom resource with appropriate config and driver is needed. For example,

yaml
apiVersion: connection.gatekeeper.sh/v1alpha1
kind: Connection
metadata:
  name: audit-connection
  namespace: gatekeeper-system
spec:
  driver: "foo"
  config:
    key: value

The spec.driver field must exist and must match one of the keys of the supportedDrivers map that was defined earlier to use the corresponding driver. The spec.config field in the configuration can vary depending on the driver being used. For dapr driver, spec.config must be component: "pubsub".


Export (`website/docs/export.md`)


id: export title: Exporting violations

Feature State: Audit export in Gatekeeper version v3.13+; admission export in v3.24+ (alpha)

❗ This feature is alpha, subject to change (feedback is welcome!). This feature was previously known as "Consuming violations using Pubsub".

Description

This feature exports audit violations, and optionally admission violations, to a backend from where users can consume violations.

To gain insights into different methods of obtaining audit violations and the respective trade-offs for each approach, please refer to Reading Audit Results.

Enabling Gatekeeper to export audit violations

Install prerequisites such as a pubsub tool, a message broker etc.

Setting up audit to export violations

In the audit deployment, set the --enable-violation-export flag to true to export audit violations. Additionally, use --audit-connection (defaults to audit-connection) and --audit-channel(defaults to audit-channel) flags to allow audit to export violations using desired connection onto desired channel. --audit-connection must be set to the name of the connection config, and --audit-channel must be set to name of the channel where violations should get published.

A Connection custom resource with spec that contains driver and config fields are required to establish connection for sending violations over the channel. Following is an example to establish a connection that uses Dapr to export messages:

yaml
apiVersion: connection.gatekeeper.sh/v1alpha1
kind: Connection
metadata:
  name: audit-connection
  namespace: gatekeeper-system
spec:
  driver: "disk"
  config:
    path: "tmp/violations/topics"
    maxAuditResults: 3
  • driver field determines which tool/driver should be used to establish a connection. Valid values are: dapr, disk
  • config field is an object that configures how the connection is made. E.g. which queue messages should be sent to.

Available drivers

  • Dapr: Export violations using pubsub model provided with Dapr
  • Disk: Export violations to file system.

Status

Upon controller ingestion, the Connection will reflect the state of the export connection on its status sub resource.

yaml
apiVersion: connection.gatekeeper.sh/v1alpha1
kind: Connection
metadata:
  name: audit-connection
  namespace: gatekeeper-system
spec:
  driver: "dapr"
  config:
    component: "pubsub"
status:
  byPod:
  - id: "pod-id"
    connectionUID: "connection-id"
    connectionErrors:
    - type: UpsertConnection
      message: "Error message"
    publishStatuses:
    - source: audit
      active: true
      lastAttemptTime: "2026-07-28T12:00:00Z"
      lastSuccessTime: "2026-07-28T12:00:00Z"
      errors: []
    - source: webhook
      active: false
      lastAttemptTime: "2026-07-28T12:00:05Z"
      errors:
      - type: Publish
        message: "Error message"

The following table describes each property in the status.byPod section:

Property Type Description
id string Unique identifier for the pod handling the connection
connectionUID string Unique identifier for the specific connection instance
connectionErrors array Errors from creating or updating the Connection, limited to 500 entries
connectionErrors[].type string Type of Connection error encountered, such as UpsertConnection
connectionErrors[].message string Human-readable description of the Connection error
publishStatuses array Publishing health grouped by source so one producer cannot clear another producer's state
publishStatuses[].source string Publisher that owns the entry: audit or webhook
publishStatuses[].active boolean Whether the source completed at least one publish in its latest reporting window
publishStatuses[].lastAttemptTime timestamp Most recent publish attempt represented by this entry
publishStatuses[].lastSuccessTime timestamp Most recent successful publish by this source
publishStatuses[].errors array Publish error classes reported by this source, limited to 500 entries

Each publisher replaces only the entry it owns. Audit updates source: audit, while admission violation export from the validation webhook updates source: webhook; either update preserves the other entry. If a source observes more than 500 distinct error classes before a successful status update, the final entry reports that additional classes were omitted. Each stored error message is the coalesced class text before the first colon and is not otherwise length-truncated.

Enabling Gatekeeper to export admission violations

Admission violation export is independent from audit export and is disabled by default. Set --enable-admission-violation-export=true on the controller-manager deployment to enable it. Enabling --enable-violation-export on the audit deployment does not enable admission export.

Admission export uses these routing flags:

  • --audit-connection: Connection resource name shared by audit and admission export. Defaults to audit-connection.
  • --audit-channel: Channel shared by audit and admission export. Defaults to audit-channel.

The equivalent Helm configuration is:

yaml
enableAdmissionViolationExport: true
exportBackend: disk

Admission violation export currently supports only the disk driver. The Helm chart rejects enableAdmissionViolationExport: true unless exportBackend: disk; the Dapr driver is not supported for admission export. The supported configuration routes audit and admission export through the same Connection, channel, path, and volume. Admission segment retention and other spool limits are fixed internal defaults while the feature is alpha; maxAuditResults continues to apply only to audit results. Additional admission-specific configuration can be added later if operational feedback requires it.

The chart mounts audit.exportVolume at audit.exportVolumeMount.path in every controller-manager pod when admission export is enabled. The reader sidecar is disabled by default; configure a production reader or explicitly enable the demonstration reader with admission.disableExportSidecar: false. Each pod writes its own spool, so multiple webhook replicas and separate audit/webhook deployments do not contend for one file.

yaml
enableAdmissionViolationExport: true
exportBackend: disk
audit:
  connection: audit-connection
  channel: audit-channel
  exportConnection:
    path: tmp/violations/topics
    maxAuditResults: 3
admission:
  disableExportSidecar: true

Admission disk files use newline-delimited JSON. Audit and admission files can share the same channel directory because their names are distinct: audit uses <audit-run-id>.log, while admission uses admission-<timestamp>-<random>.log. A writer creates a uniquely named, locked admission .open segment. Reaching the fixed byte, record, or age limit flushes and closes the segment, then atomically renames it to .log. The demonstration reader selects only the admission- prefix in admission mode, so it cannot delete audit run files.

Cleanup removes only completed, unlocked admission- segments and enforces fixed file count, total bytes, and TTL limits. Audit cleanup explicitly ignores that prefix. Startup recovery ignores files locked by another live writer, truncates an abandoned .open file to its final complete JSONL record, and publishes it as a recovered .log. A periodic janitor starts only when recovery finds admission files or the first admission record is written, and stops after all admission artifacts are gone. The driver also reserves filesystem headroom and rejects new records before exhausting the filesystem.

Each retention-cleanup invocation removes at most 256 ready segments. Recovery may also remove abandoned empty .open files and .deleting remnants before retention runs. New disk Connections are rejected while 32 failed cleanup states are retained. Failures from Connections that were already open can temporarily take the retry map above that threshold because descriptor-owning state cannot be safely discarded.

The default chart uses per-pod emptyDir volumes. Unconsumed files are lost when that pod is deleted. For durable custom storage, use a separate directory or volume per pod; do not point multiple pods at the same shared directory because Connection removal owns and cleans its configured path.

Admission export messages use eventType: violation_admission. They include the evaluation timestamp, admission operation (CREATE, UPDATE, DELETE, or CONNECT), API resource and subresource, dry-run state, and requesting user's name, UID, and groups. They can describe denied requests that were never persisted in Kubernetes and include policy-provided details, resource labels, and constraint annotations. Treat the destination as security-sensitive; identity fields and policy or resource data may contain user-controlled or sensitive data.

Default queue and disk limits

Admission export limits are fixed while the feature is alpha and apply independently to each controller-manager pod:

Layer Limit Default
In-memory queue Messages being constructed or waiting to publish 1,024
In-memory queue Total encoded bytes waiting to publish 16 MiB
Publisher Maximum records in one ready batch 64
Record Complete encoded JSON record 64 KiB
Disk segment Maximum bytes 1 MiB
Disk segment Maximum records 1,000
Disk segment Maximum age before rotation 1 minute
Disk spool Completed segments 20
Disk spool Total bytes, including the open segment and pending record 20 MiB
Disk spool Maximum completed-segment age 24 hours
Filesystem Free-space reserve 16 MiB

The 64 KiB record limit applies to the entire JSON object after encoding, not only to the violation message. JSON field names and escaping, policy-provided details, constraint annotations, resource labels, and request identity all count toward the limit. KiB measures bytes, so the number of characters that fit varies with UTF-8 and JSON escaping. The limit leaves room for normal policy and request metadata while preventing one user-influenced result from consuming a disproportionate share of the queue and disk spool. The kubectl.kubernetes.io/last-applied-configuration constraint annotation is omitted, but other annotations still count. An oversized record is dropped before enqueue and reported with drop reason message_too_large.

Segment rotation occurs when any byte, record-count, or age limit is reached. The spool limits bound unconsumed backlog; they do not guarantee that records remain available for 24 hours. Cleanup removes the oldest completed segments until the count, byte, and age limits all hold, so count or byte pressure can remove a segment long before its maximum age. When one-minute age rotation is the limiting factor, 20 completed segments provide roughly 20 minutes of backlog; higher volume can rotate segments sooner. A reader must therefore drain segments fast enough for the workload.

The demonstration admission reader logs each record and deletes its completed segment after a successful read. It demonstrates consumption and is not a retention archive. Retain exported records in the downstream system when longer history is required.

Delivery behavior

Admission export is best-effort. Backend publishing does not delay or change the admission response:

  • Each controller-manager pod has an asynchronous queue limited to 1,024 messages, including records being constructed, and 16 MiB of encoded data.
  • A complete encoded JSON record is limited to 64 KiB.
  • The webhook reserves count capacity before constructing a record. The publisher drains up to 64 records that are already queued without waiting to fill a batch.
  • Messages are dropped when encoding fails or an enqueue limit is reached. A failed publish is not retried.
  • During graceful termination, messages still in the queue are drained for up to five seconds. Messages still queued after that deadline are dropped and reported with the shutdown reason.
  • A batch already removed from the queue when termination begins is not requeued. Gatekeeper stops waiting when its publish context is canceled; a backend call that ignores cancellation may finish later, but its result is not used and the process may exit first.
  • SIGKILL, OOM termination, and node failure do not run the drain. Queued and in-flight records are then lost. Complete disk records already written and synced can be recovered only if the volume survives; the default emptyDir survives a container restart but not pod deletion or replacement.
  • Repeated drop and publish-error logs are limited to one per minute for each class; use the admission export metrics to detect sustained loss.
  • Webhook publish status is reported outside the queue publisher. Errors and health transitions are checked every 10 seconds, while unchanged healthy traffic is coalesced to a one-minute heartbeat. Audit updates only the audit entry at the end of each run.
  • A source with no new publish attempts leaves its previous entry unchanged. A later successful reporting window clears only that source's errors; use lastAttemptTime and lastSuccessTime to evaluate freshness.

Quick start with exporting admission violations to disk

This quick start enables the demonstration admission reader. It logs completed admission records and deletes each segment after reading it, so use it only to verify the feature.

  1. Install or upgrade Gatekeeper with admission export, the disk backend, and the demonstration reader enabled:

    shell
    helm upgrade --install gatekeeper gatekeeper/gatekeeper \
      --namespace gatekeeper-system \
      --create-namespace \
      --set enableAdmissionViolationExport=true \
      --set exportBackend=disk \
      --set admission.disableExportSidecar=false \
      --set audit.connection=audit-connection \
      --set audit.channel=audit-channel
    
    kubectl rollout status deployment/gatekeeper-controller-manager \
      --namespace gatekeeper-system \
      --timeout=2m
  2. Install a test policy that requires the test label on Pods in the nginx namespace:

    shell
    export GATEKEEPER_EXPORT_EXAMPLES=https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/test/export
    
    kubectl create namespace nginx --dry-run=client --output yaml | kubectl apply -f -
    kubectl apply -f "${GATEKEEPER_EXPORT_EXAMPLES}/k8srequiredlabels_ct.yaml"
    until kubectl get crd k8srequiredlabels.constraints.gatekeeper.sh >/dev/null 2>&1; do sleep 1; done
    kubectl apply -f "${GATEKEEPER_EXPORT_EXAMPLES}/pod_must_have_test.yaml"
    until kubectl get k8srequiredlabels pod-must-have-test \
      -o jsonpath='{.status.byPod[*].operations[*]}' 2>/dev/null | grep -qw webhook; do sleep 1; done
  3. Submit a Pod that violates the policy. The command is expected to fail with a denial:

    shell
    kubectl apply -f - <<'EOF'
    apiVersion: v1
    kind: Pod
    metadata:
      name: denied-export-pod
      namespace: nginx
      labels:
        app: denied-export
    spec:
      containers:
      - name: nginx
        image: nginx:latest
    EOF
  4. Confirm that a controller-manager reader observed the admission record:

    shell
    kubectl logs --namespace gatekeeper-system \
      --selector control-plane=controller-manager \
      --container admission-reader \
      --since=5m | grep '"eventType":"violation_admission"' | grep '"resourceName":"denied-export-pod"'

    The webhook publisher also reports health under source: webhook:

    shell
    kubectl get connection audit-connection \
      --namespace gatekeeper-system \
      --output yaml

Quick start with exporting audit violations using Dapr and Redis

Prerequisites for Dapr

  1. Install Dapr

    To install Dapr with specific requirements and configuration, please refer to Dapr docs.

:::important
- Make sure to set SIDECAR_DROP_ALL_CAPABILITIES environment variable on dapr-sidecar injector pod to true to avoid getting PodSecurity violation errors for the injected sidecar container as Gatekeeper by default requires workloads to run with restricted policy. If using helm charts to install Dapr, you can use --set dapr_sidecar_injector.sidecarDropALLCapabilities=true.
- Additionally, configure appropriate seccompProfile for sidecar containers injected by Dapr to avoid getting PodSecurity violation errors. We are setting required Dapr annotation for audit pod while deploying Gatekeeper later in this quick start to avoid getting PodSecurity violation error.
:::

text
> Dapr is installed with mtls enabled by default, for more details on the same please refer to [Dapr security](https://docs.dapr.io/operations/security/mtls/#setting-up-mtls-with-the-configuration-resource).
  1. Install Redis

    Please refer to this guide to install Redis.

    Redis is used for example purposes only. Dapr supports many different state store options. To install Redis with TLS, please refer to this doc.

Configure a sample subscriber to receive violations

  1. Create fake-subscriber namespace and redis secret

    shell
    kubectl create ns fake-subscriber
    # creating redis secret in subscriber namespace to allow Dapr sidecar to connect to redis instance
    kubectl get secret redis --namespace=default -o yaml | sed 's/namespace: .*/namespace: fake-subscriber/' | kubectl apply -f -
  2. Create Dapr pubsub component

    shell
    kubectl apply -f - <<EOF
    apiVersion: dapr.io/v1alpha1
    kind: Component
    metadata:
      name: pubsub
      namespace: fake-subscriber
    spec:
      type: pubsub.redis
      version: v1
      metadata:
      - name: redisHost
        value: redis-master.default.svc.cluster.local:6379
      - name: redisPassword
        secretKeyRef: 
          name: redis
          key: redis-password
    EOF

    Please use this guide to properly configure Redis pubsub component for Dapr.

  3. Deploy subscriber application

    yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: sub
      namespace: fake-subscriber
      labels:
        app: sub
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: sub
      template:
        metadata:
          labels:
            app: sub
          annotations:
            dapr.io/enabled: "true"
            dapr.io/app-id: "subscriber"
            dapr.io/enable-api-logging: "true"
            dapr.io/app-port: "6002"
        spec:
          containers:
          - name: go-sub
            image: ghcr.io/open-policy-agent/fake-subscriber:latest
            imagePullPolicy: Always

    :::important
    The fake-subscriber image is published as part of each Gatekeeper release at ghcr.io/open-policy-agent/fake-subscriber. To build a custom version locally, use the Dockerfile at gatekeeper/test/export/fake-subscriber.
    :::

Configure Gatekeeper with Export enabled with Dapr

  1. Create Gatekeeper namespace, and create Dapr pubsub component and Redis secret in Gatekeeper's namespace (gatekeeper-system by default). Please make sure to update gatekeeper-system namespace for the next steps if your cluster's Gatekeeper namespace is different.

    shell
    kubectl create namespace gatekeeper-system
    kubectl get secret redis --namespace=default -o yaml | sed 's/namespace: .*/namespace: gatekeeper-system/' | kubectl apply -f -
    kubectl apply -f - <<EOF
    apiVersion: dapr.io/v1alpha1
    kind: Component
    metadata:
      name: pubsub
      namespace: gatekeeper-system
    spec:
      type: pubsub.redis
      version: v1
      metadata:
      - name: redisHost
        value: redis-master.default.svc.cluster.local:6379
      - name: redisPassword
        secretKeyRef:
          name: redis
          key: redis-password
    EOF
  2. To upgrade or install Gatekeeper with --enable-violation-export set to true, --audit-connection set to audit-connection, --audit-channel set to audit-channel on audit pod.

    shell
    # auditPodAnnotations is used to add annotations required by Dapr to inject sidecar to audit pod
    echo 'auditPodAnnotations: {dapr.io/enabled: "true", dapr.io/app-id: "audit", dapr.io/metrics-port: "9999", dapr.io/sidecar-seccomp-profile-type: "RuntimeDefault"}' > /tmp/annotations.yaml
    helm upgrade --install gatekeeper gatekeeper/gatekeeper --namespace gatekeeper-system \
    --set enableViolationExport=true \
    --set audit.connection=audit-connection \
    --set audit.channel=audit-channel \
    --values /tmp/annotations.yaml

    Note: Verify that after the audit pod is running there is a Dapr sidecar injected and running along side manager container.

  3. Create connection config to establish a connection.

    shell
    kubectl apply -f - <<EOF
    apiVersion: connection.gatekeeper.sh/v1alpha1
    kind: Connection
    metadata:
      name: audit-connection
      namespace: gatekeeper-system
    spec:
      driver: "dapr"
      config:
        component: "pubsub"
    EOF

    Note: Name of the Connection custom resource must match the value of --audit-connection for it to be used by audit to export violation. At the moment, only one connection can exist for audit.

  4. Create the constraint templates and constraints, and make sure audit ran by checking constraints. If constraint status is updated with information such as auditTimeStamp or totalViolations, then audit has ran at least once. Additionally, populated TOTAL-VIOLATIONS field for all constraints while listing constraints also indicates that audit has ran at least once.

    log
    kubectl get constraint
    NAME                 ENFORCEMENT-ACTION   TOTAL-VIOLATIONS
    pod-must-have-test                        0
  5. Finally, check the subscriber logs to see the violations received.

    log
    kubectl logs -l app=sub -c go-sub -n fake-subscriber 
    2023/07/18 20:16:41 Listening...
    2023/07/18 20:37:20 main.ExportMsg{ID:"2023-07-18T20:37:19Z", Details:map[string]interface {}{"missing_labels":[]interface {}{"test"}}, EventType:"violation_audited", Group:"constraints.gatekeeper.sh", Version:"v1beta1", Kind:"K8sRequiredLabels", Name:"pod-must-have-test", Namespace:"", Message:"you must provide labels: {\"test\"}", EnforcementAction:"deny", ConstraintAnnotations:map[string]string(nil), ResourceGroup:"", ResourceAPIVersion:"v1", ResourceKind:"Pod", ResourceNamespace:"nginx", ResourceName:"nginx-deployment-58899467f5-j85bs", ResourceLabels:map[string]string{"app":"nginx", "owner":"admin", "pod-template-hash":"58899467f5"}}

Quick start with exporting violations on node storage using Disk driver via emptyDir

Configure Gatekeeper with Export enabled to Disk

  1. Deploy Gatekeeper with disk export configurations.

    Below are the default configurations that enable disk export and add a sidecar container to the Gatekeeper audit pod:

    yaml
    audit: 
      exportVolume: 
        name: tmp-violations 
        emptyDir: {} 
      exportVolumeMount: 
        path: /tmp/violations 
      exportSidecar: 
        name: reader
        image: ghcr.io/open-policy-agent/fake-reader:latest
        imagePullPolicy: Always 
        securityContext: 
          allowPrivilegeEscalation: false 
          capabilities: 
            drop: 
            - ALL 
          readOnlyRootFilesystem: true 
          runAsGroup: 999 
          runAsNonRoot: true 
          runAsUser: 1000 
          seccompProfile: 
            type: RuntimeDefault 
        volumeMounts: 
        - mountPath: /tmp/violations 
          name: tmp-violations

    :::warning
    The reader sidecar image ghcr.io/open-policy-agent/fake-reader is published as part of each Gatekeeper release and is intended for demonstration and quickstart purposes only. It is not recommended for production environments. For production use, it is advised to create and configure a custom sidecar image tailored to your specific requirements.
    :::

    shell
    helm upgrade --install gatekeeper gatekeeper/gatekeeper --namespace gatekeeper-system \
    --set enableViolationExport=true \
    --set audit.connection=audit-connection \
    --set audit.channel=audit-channel \
    --set audit.exportConnection.path=tmp/violations/topics \
    --set audit.exportConnection.maxAuditResults=3 \
    --set exportBackend=disk \

    As part of the command above, the Connection resource is installed with the following values and defaults:

    yaml
    apiVersion: connection.gatekeeper.sh/v1alpha1
    kind: Connection
    metadata:
      name: "audit-connection"
      namespace: "gatekeeper-system"
    spec:
      driver: "disk"
      config:
        path: "tmp/violations/topics"
        maxAuditResults: 3
    Property Description Default
    path (alpha) Path for audit pod manager container to export violations and sidecar container to read from. Must be a child of volume mount path so the parent is writable. "tmp/violations/topics"
    maxAuditResults (alpha) Maximum number of audit results that can be stored in the export path. 3
    closedConnectionTTL (alpha) Optional duration string controlling how long a failed closed Connection remains eligible for cleanup retries. Accepted values are "1m" through "10m". The chart leaves it unset and the driver defaults to "10m". "10m"

    Note: After the audit pod starts, verify that it contains two running containers.

    shell
    kubectl get pod -n gatekeeper-system 
    NAME                                             READY   STATUS    RESTARTS        AGE
    gatekeeper-audit-6865f5f56d-vclxw                2/2     Running   0               12s

    :::tip
    The command above deploys the audit pod with a default sidecar reader and volume. To customize the sidecar reader or volume according to your requirements, you can set the following variables in your values.yaml file:

    yaml
    audit: 
      exportVolume: 
        <your-volume>
      exportVolumeMount: 
        path: <volume-mount-path>
      exportSidecar: 
        <your-side-car>

    :::

  2. Create the constraint templates and constraints, and make sure audit ran by checking constraints. If constraint status is updated with information such as auditTimeStamp or totalViolations, then audit has ran at least once. Additionally, populated TOTAL-VIOLATIONS field for all constraints while listing constraints also indicates that audit has ran at least once.

    log
    kubectl get constraint
    NAME                 ENFORCEMENT-ACTION   TOTAL-VIOLATIONS
    pod-must-have-test                        0
  3. Finally, check the sidecar reader logs to see the violations written.

    log
    kubectl logs -l gatekeeper.sh/operation=audit -c go-sub -n gatekeeper-system 
    2025/03/05 00:37:16 {"id":"2025-03-05T00:37:13Z","details":{"missing_labels":["test"]},"eventType":"violation_audited","group":"constraints.gatekeeper.sh","version":"v1beta1","kind":"K8sRequiredLabels","name":"pod-must-have-test","message":"you must provide labels: {\"test\"}","enforcementAction":"deny","resourceAPIVersion":"v1","resourceKind":"Pod","resourceNamespace":"nginx","resourceName":"nginx-deployment-2-79479fc6db-7qbnm","resourceLabels":{"app":"nginx-ingress","app.kubernetes.io/component":"controller","pod-template-hash":"79479fc6db"}}

Violations

The audit pod exports violations in following format:

json
{
  "id": "2023-07-18T21:21:52Z",
  "details": {
    "missing_labels": [
      "test"
    ]
  },
  "eventType": "violation_audited",
  "group": "constraints.gatekeeper.sh",
  "version": "v1beta1",
  "kind": "K8sRequiredLabels",
  "name": "pod-must-have-test",
  "message": "you must provide labels: {\"test\"}",
  "enforcementAction": "deny",
  "resourceAPIVersion": "v1",
  "resourceKind": "Pod",
  "resourceNamespace": "nginx",
  "resourceName": "nginx-deployment-cd55c47f5-2b84x",
  "resourceLabels": {
    "app": "nginx",
    "pod-template-hash": "cd55c47f5"
  }
}

Externaldata (`website/docs/externaldata.md`)


id: externaldata title: External Data

Feature State: Gatekeeper version v3.11+ (beta)

Motivation

Gatekeeper provides various means to mutate and validate Kubernetes resources. However, in many of these scenarios this data is either built-in, static or user defined. With external data feature, we are enabling Gatekeeper to interface with various external data sources, such as image registries, using a provider-based model.

A similar way to connect with an external data source can be done today using OPA's built-in http.send functionality. However, there are limitations to this approach.

  • Gatekeeper does not support Rego policies for mutation, which cannot use the OPA http.send built-in function.
  • Security concerns due to:
    • if template authors are not trusted, it will potentially give template authors access to the in-cluster network.
    • if template authors are trusted, authors will need to be careful on how rego is written to avoid injection attacks.

Key benefits provided by the external data solution:

  • Addresses security concerns by:
    • Restricting which hosts a user can access.
    • Providing an interface for making requests, which allows Gatekeeper to better handle things like escaping strings.
  • Addresses common patterns with a single provider, e.g. image tag-to-digest mutation, which can be leveraged by multiple scenarios (e.g. validating image signatures or vulnerabilities).
  • Provider model creates a common interface for extending Gatekeeper with external data.
    • It allows for separation of concerns between the implementation that allows access to external data and the actual policy being evaluated.
    • Developers and consumers of data sources can rely on that common protocol to ease authoring of both constraint templates and data sources.
    • Makes change management easier as users of an external data provider should be able to tell whether upgrading it will break existing constraint templates. (once external data API is stable, our goal is to have that answer always be "no")
  • Performance benefits as Gatekeeper can now directly control caching and which values are significant for caching, which increases the likelihood of cache hits.
    • For mutation, we can batch requests via lazy evaluation.
    • For validation, we make batching easier via external_data Rego function design.

Providers

Providers are designed to be in-cluster components that can communicate with external data sources (such as image registries, Active Directory/LDAP directories, etc) and return data in a format that can be processed by Gatekeeper.

Example provider template can be found at: https://github.com/open-policy-agent/gatekeeper-external-data-provider

Providers maintained by the community

If you have built an external data provider and would like to add it to this list, please submit a PR to update this page.

If you have any issues with a specific provider, please open an issue in the applicable provider's repository.

The following external data providers are maintained by the community:

Sample providers

The following external data providers are samples and are not supported/maintained by the community:

API (v1beta1)

`Provider`

Provider resource defines the provider and the configuration for it.

yaml
apiVersion: externaldata.gatekeeper.sh/v1beta1
kind: Provider
metadata:
  name: my-provider
spec:
  url: https://<service-name>.<namespace>:<port>/<endpoint> # URL to the external data source (e.g., https://my-provider.my-namespace:8090/validate)
  timeout: <timeout> # timeout value in seconds (e.g., 1). this is the timeout on the Provider custom resource, not the provider implementation.
  caBundle: <caBundle> # CA bundle to use for TLS verification.

When used by the mutating webhook, the effective external data request timeout is the smaller of:

  • the remaining admission webhook request deadline
  • the provider spec.timeout (or Gatekeeper's default timeout when unset)

For example, if the webhook has 1s remaining and spec.timeout is 10s, the provider request is capped at ~1s.

`ProviderRequest`

ProviderRequest is the API request that is sent to the external data provider.

go
// ProviderRequest is the API request for the external data provider.
type ProviderRequest struct {
	// APIVersion is the API version of the external data provider.
	APIVersion string `json:"apiVersion,omitempty"`
	// Kind is kind of the external data provider API call. This can be "ProviderRequest" or "ProviderResponse".
	Kind ProviderKind `json:"kind,omitempty"`
	// Request contains the request for the external data provider.
	Request Request `json:"request,omitempty"`
}

// Request is the struct that contains the keys to query.
type Request struct {
	// Keys is the list of keys to send to the external data provider.
	Keys []string `json:"keys,omitempty"`
}

`ProviderResponse`

ProviderResponse is the API response that a provider must return.

go
// ProviderResponse is the API response from a provider.
type ProviderResponse struct {
	// APIVersion is the API version of the external data provider.
	APIVersion string `json:"apiVersion,omitempty"`
	// Kind is kind of the external data provider API call. This can be "ProviderRequest" or "ProviderResponse".
	Kind ProviderKind `json:"kind,omitempty"`
	// Response contains the response from the provider.
	Response Response `json:"response,omitempty"`
}

// Response is the struct that holds the response from a provider.
type Response struct {
	// Idempotent indicates that the responses from the provider are idempotent.
	// Applies to mutation only and must be true for mutation.
	Idempotent bool `json:"idempotent,omitempty"`
	// Items contains the key, value and error from the provider.
	Items []Item `json:"items,omitempty"`
	// SystemError is the system error of the response.
	SystemError string `json:"systemError,omitempty"`
}

// Items is the struct that contains the key, value or error from a provider response.
type Item struct {
	// Key is the request from the provider.
	Key string `json:"key,omitempty"`
	// Value is the response from the provider.
	Value interface{} `json:"value,omitempty"`
	// Error is the error from the provider.
	Error string `json:"error,omitempty"`
}

Implementation

Provider is an HTTP server that listens on a port and responds to ProviderRequest with ProviderResponse.

As part of ProviderResponse, the provider can return a list of items. Each item is a JSON object with the following fields:

  • Key: the key that was sent to the provider
  • Value: the value that was returned from the provider for that key
  • Error: an error message if the provider returned an error for that key

If there is a system error, the provider should return the system error message in the SystemError field.

📎 Recommendation is for provider implementations to keep a timeout such as maximum of 1-2 seconds for the provider to respond.

Example provider implementation: https://github.com/open-policy-agent/gatekeeper/blob/master/test/externaldata/dummy-provider/provider.go

Provider Response Caching

Starting with v3.13+, Gatekeeper supports caching of responses from external data providers for audit and the validating webhook. Mutation provider responses are also cached when external data is used by mutation. Gatekeeper caches the response based on the Key and Value received as part of the ProviderResponse. By default, the cache is invalidated after 3 minutes, which is the default Time-to-Live (TTL). You can configure the TTL using the --external-data-provider-response-cache-ttl flag. Setting the flag to 0 disables this cache.

External data for Gatekeeper validating webhook

External data adds a custom OPA built-in function called external_data to Rego. This function is used to query external data providers.

external_data is a function that takes a request and returns a response. The request is a JSON object with the following fields:

  • provider: the name of the provider to query
  • keys: the list of keys to send to the provider

e.g.,

rego
# build a list of keys containing images for batching
  my_list := [img | img = input.review.object.spec.template.spec.containers[_].image]

  # send external data request
  response := external_data({"provider": "my-provider", "keys": my_list})

Response example: [["my-key", "my-value", ""], ["another-key", 42, ""], ["bad-key", "", "error message"]]

📎 To avoid multiple calls to the same provider, recommendation is to batch the keys list to send a single request.

Example template:
https://github.com/open-policy-agent/gatekeeper/blob/master/test/externaldata/dummy-provider/policy/template.yaml

External data for Gatekeeper mutating webhook

External data can be used in conjunction with Gatekeeper mutating webhook.

API

You can specify the details of the external data provider in the spec.parameters.assign.externalData field of AssignMetadata and Assign.

Note: spec.parameters.assign.externalData, spec.parameters.assign.value and spec.parameters.assign.fromMetadata are mutually exclusive.

Field Description
providerString The name of the external data provider.
dataSourceDataSource Specifies where to extract the data that will be sent to the external data provider.- ValueAtLocation (default): extracts an array of values from the path that will be modified. See mutation intent for more details.- Username: The name of the Kubernetes user who initiated the admission request.
failurePolicyFailurePolicy The policy to apply when the external data provider returns an error.- UseDefault: use the default value specified in spec.parameters.assign.externalData.default- Ignore: ignore the error and do not perform any mutations.- Fail (default): do not perform any mutations and return the error to the user.
defaultString The default value to use when the external data provider returns an error and the failure policy is set to UseDefault.

`AssignMetadata`

yaml
apiVersion: mutations.gatekeeper.sh/v1beta1
kind: AssignMetadata
metadata:
  name: annotate-owner
spec:
  match:
    scope: Namespaced
    kinds:
    - apiGroups: ["*"]
      kinds: ["Pod"]
  location: "metadata.annotations.owner"
  parameters:
    assign:
      externalData:
        provider: my-provider
        dataSource: Username

Provider response

json
{
  "apiVersion": "externaldata.gatekeeper.sh/v1beta1",
  "kind": "ProviderResponse",
  "response": {
    "idempotent": true,
    "items": [
      {
        "key": "kubernetes-admin",
        "value": "[email protected]"
      }
    ]
  }
}

Mutated object

yaml
...
metadata:
  annotations:
    owner: [email protected]
...

`Assign`

yaml
apiVersion: mutations.gatekeeper.sh/v1beta1
kind: Assign
metadata:
  name: mutate-images
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  match:
    scope: Namespaced
  location: "spec.containers[name:*].image"
  parameters:
    assign:
      externalData:
        provider: my-provider
        dataSource: ValueAtLocation
        failurePolicy: UseDefault
        default: busybox:latest

Provider response

json
{
  "apiVersion": "externaldata.gatekeeper.sh/v1beta1",
  "kind": "ProviderResponse",
  "response": {
    "idempotent": true,
    "items": [
      {
        "key": "nginx",
        "value": "nginx:v1.2.3"
      }
    ]
  }
}

Mutated object

yaml
...
spec:
  containers:
    - name: nginx
      image: nginx:v1.2.3
...

Limitations

There are several limitations when using external data with the mutating webhook:

  • Only supports mutation of string fields (e.g. .spec.containers[name:*].image).
  • AssignMetadata only supports dataSource: Username because AssignMetadata only supports creation of metadata.annotations and metadata.labels. dataSource: ValueAtLocation will not return any data.
  • ModifySet does not support external data.
  • Multiple mutations to the same object are applied alphabetically based on the name of the mutation CRDs. If you have an external data mutation and a non-external data mutation with the same spec.location, the final result might not be what you expected. Currently, there is no way to enforce custom ordering of mutations but the issue is being tracked here.

TLS and mutual TLS support

Since external data providers are in-cluster HTTP servers backed by Kubernetes services, communication is not encrypted by default. This can potentially lead to security issues such as request eavesdropping, tampering, and man-in-the-middle attack.

To further harden the security posture of the external data feature,

  • starting from Gatekeeper v3.9.0, TLS and mutual TLS (mTLS) via HTTPS protocol are supported between Gatekeeper and external data providers
  • starting with Gatekeeper v3.11.0, TLS or mutual TLS (mTLS) via HTTPS protocol are required between Gatekeeper and external data providers with a minimum TLS version of 1.3

In this section, we will describe the steps required to configure them.

Prerequisites

  • A Gatekeeper deployment with version >= v3.9.0.
  • The certificate of your certificate authority (CA) in PEM format.
  • The certificate of your external data provider in PEM format, signed by the CA above.
  • The private key of the external data provider in PEM format.

How to generate a self-signed CA and a keypair for the external data provider

In this section, we will describe how to generate a self-signed CA and a keypair using openssl.

  1. Generate a private key for your CA:
bash
openssl genrsa -out ca.key 2048
  1. Generate a self-signed certificate for your CA:
bash
openssl req -new -x509 -days 365 -key ca.key -subj "/O=My Org/CN=External Data Provider CA" -out ca.crt
  1. Generate a private key for your external data provider:
bash
openssl genrsa -out server.key 2048
  1. Generate a certificate signing request (CSR) for your external data provider:

Replace <service name> and <service namespace> with the correct values.

bash
openssl req -newkey rsa:2048 -nodes -keyout server.key -subj "/CN=<service name>.<service namespace>" -out server.csr
  1. Generate a certificate for your external data provider:
bash
openssl x509 -req -extfile <(printf "subjectAltName=DNS:<service name>.<service namespace>") -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt

How Gatekeeper trusts the external data provider (TLS)

To enable one-way TLS, your external data provider should enable any TLS-related configurations for their HTTP server. For example, for Go's built-in HTTP server implementation, you can use [`Li