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
yarn installLocal Development
yarn startThis command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server.
Build
yarn buildThis command generates static content into the build directory and can be served using any static contents hosting service.
Deployment
GIT_USER=<Your GitHub username> USE_SSH=true yarn deployIf you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the gh-pages branch.
Search
Gatekeeper docs website uses Algolia DocSearch service. Please see here for more information.
If the search index has any issues:
- Go to Algolia search dashboard
- Click manage index and export configuration
- Delete the index
- Import saved configuration
- 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
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo updateSee helm repo for command documentation.
Install Chart
# 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-namespaceSee 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.
$ helm uninstall gatekeeper
$ helm install -n gatekeeper-system [RELEASE_NAME] gatekeeper/gatekeeper --create-namespaceOption 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.
$ helm_migrate.sh
$ helm install -n gatekeeper-system gatekeeper gatekeeper/gatekeeperUpgrading from >= v3.4.0
$ helm upgrade -n gatekeeper-system [RELEASE_NAME] gatekeeper/gatekeeperSee 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:
- [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.
- [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:
- Configuring different validation actions for different enforcement points.
- 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
- Admission Violation Export
- Namespace-Scoped Constraints
- Sync Resource
- External Data Response Cache
- Gator SyncSet Support
- Connection config CRD and controller for Pubsub
- Export Audit Violations without pubsub
Implemented
- Gatekeeper V3 Accepted Design
- Dynamic Watch Design for OPA/Gatekeeper
- Architecture Diagram
- Audit Design Doc
- Dry-Run Design Doc
- Constraint Framework Client Interface
- Logging Design Doc
- Namespace Exclusion Design doc
- Metrics Design Issue
- Gatekeeper v1beta1 CRD Deprecation
- Compiler Sharding Design
- Template e2e Testing Design
- ByPod Status Design
- Mutation Design - 4th Edition (current approach)
- External Data
- gator validate
- External Data TLS Support
- Export violations using pub-sub
- Versioning of Constraint Templates
- Gatekeeper-to-VAP User Flow
- Gatekeeper Enforcement Actions
Roadmap (in development)
- See milestones
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.
- This looks at the elements of mutation that allow us to assert that any combination of mutators should converge
- 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.
- A dissection of the mutation problem space, attempting to break down the
- 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.
- Looking at how a mutation webhook may behave during configuration changes and discussing what that might mean in terms
- 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.
- This doc discusses the impact the existence of finalizers has on running
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.
#!/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.
#!/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.
#!/bin/bash
# Will wait until user presses enter
PROMPT_TIMEOUT=0
wait
# Will wait max 5 seconds until user presses
PROMPT_TIMEOUT=5
waitcmd
Enters script into interactive mode and allows newly typed commands to be executed within the script
#!/bin/bash
cmdGetting Started
Create a shell script and include demo-magic.sh
#!/bin/bash
########################
# include the magic
########################
. demo-magic.sh
# hide the evidence
clear
# Put your stuff hereThen 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 afterpandpefunctions-w- set no wait timeout afterpandpefunctions
$ ./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.
#!/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.
# 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.
...
# define waiting points
pe 'git status'
pe 'git log --oneline --decorate -n 20'
wait
pe 'git pull'
pe 'git log --oneline --decorate -n 20'
waitOverview (`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
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo updateSee helm repo for command documentation.
Install Chart
# 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-namespaceSee 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.
$ helm uninstall gatekeeper
$ helm install -n gatekeeper-system [RELEASE_NAME] gatekeeper/gatekeeper --create-namespaceOption 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.
$ helm_migrate.sh
$ helm install -n gatekeeper-system gatekeeper gatekeeper/gatekeeperUpgrading from >= v3.4.0
$ helm upgrade -n gatekeeper-system [RELEASE_NAME] gatekeeper/gatekeeperSee 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.
- Gatekeeper does not control the design or implementation of Rego or OPA,
- 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.
- These resources are intended only for consumption by the Gatekeeper
- 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.
- The library is a consumer of Gatekeeper. As such, it is not bound by
- 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.
- The Gatekeeper manifest and Helm templates are reference deployment
- 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.
- In the event that there is a security incident that is impossible to fix
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
inputvariable
should be backwards compatible. - The storage location of objects in
data.inventoryshould 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
parametersschema - The
matchschema- 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
- Note that there is an implicit assumption that the match schema is
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:
hubbinary is installed. If not,hubcan be installed bygo 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:
- Beta (
vX.Y.Z-beta.A) - Release Candidate (
vX.Y.Z-rc.B) - 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.
Click the "Actions" tab.
Click the "create_release_pull_request" workflow.
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:
Dependabot PRs
- Ensure all Dependabot PRs are reviewed and merged. This keeps dependencies up to date and secure.
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.
Vulnerability Scanning
- Ensure Trivy scans are passing and no vulnerabilities are detected in the latest CI run.
- Review the latest Trivy scan results before proceeding.
Building and releasing
- Once the release pull request is merged to
masterorreleasebranch (`` below), tag that commit with release version and push tags to remote repository.
git checkout <BRANCH NAME>
git pull origin <BRANCH NAME>
git tag -a <NEW VERSION> -m '<NEW VERSION>'
git push origin <NEW VERSION>- Pushing the release tag will trigger GitHub Actions to trigger
tagged-releasejob.
This will build theopenpolicyagent/gatekeeperimage automatically, then publish the new release image tag and thelatestimage tag to theopenpolicyagent/gatekeeperrepository. Finally, verify step will run e2e tests to verify the newly released tag.
Publishing
- 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.modwith 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.modwith 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
kubectl create ns opa2. create tls secret for `gatekeeper`
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.yaml3. deploy default kubernetes policies
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.rego4. enable webhook configuration
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.yamlapply 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.
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.regocreate ingress in default namespace
This operation will succeed as there is no conflicting ingress host in any other namespace
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.yamlCreate ingress with same host in another namespace (This should fail)
Create a test namespace
kubectl create ns testTry create a
kubectl -n test apply -f ./ingress-host.yamlThis is the error message returned by gatekeeper
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:
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.
Related Designs
This design builds on the following existing designs:
- Connection config CRD and controller for Export
- Export Audit Violations without PubSub
- Export violations using pub-sub
- Gatekeeper Resource Status Design
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
.logfile available to a reader. - Open segment: a locked admission
.openfile receiving JSON Lines records. - Source: the component reporting publish status, currently
auditorwebhook.
Configuration
Admission export is disabled by default and enabled independently from audit
export:
--enable-admission-violation-export=trueAdmission export deliberately reuses the audit route:
--audit-connection=audit-connection
--audit-channel=audit-channelOnly one Connection and one channel are supported for this alpha design. The
Helm configuration is:
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: trueThe 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 generatesmanifest_staging. Root charts and deploy directories are release outputs
and are not source files for this feature.
Architecture
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:
- Requires an export system and a function that returns the current pod.
- Constructs a
connectionStatusReporterusing the API reader, client, scheme,
and pod getter. - Constructs a
queuedAdmissionViolationExporterusing the shared Connection
and channel. - Adds the exporter to the controller-runtime manager as a runnable.
- 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:
{
"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 byjson.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:
- Takes a read lock that prevents shutdown from starting during the enqueue.
- Atomically reserves one of 1,024 count slots before constructing the message.
- Rejects new messages after shutdown starts or when all count slots are held.
- Builds the message and computes a bounded lower estimate of its encoded size.
- Drops messages that cannot fit within 64 KiB, then marshals the remainder.
- Applies the exact 64 KiB encoded limit.
- Atomically reserves the encoded size against the 16 MiB byte limit.
- Attempts a non-blocking send to the buffered channel.
- 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-messagePublish 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:
- Acquires the queue lock and rejects future enqueues.
- Creates a fresh background context with a five-second deadline because the
manager context is already canceled. - Publishes any message selected concurrently with cancellation.
- Drains queued messages until empty or the deadline expires.
- Counts remaining messages as dropped with reason
shutdown. - 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:
Publish(context, connectionName, channel, encodedMessage)A ready group of two to 64 records uses the optional batch extension:
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 andO_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:
- Validates the topic.
- Checks each record against the same 64 KiB complete-record limit as the queue
and the 1 MiB segment limit. - 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. - Checks filesystem blocks available to the current user and preserves a 16 MiB
free-space reserve in addition to the complete batch. - Rotates the current segment before a write when adding the record would
exceed a rotation limit. - Appends each encoded JSON record plus one newline.
- Calls
fsynconce 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
.openfiles are treated as live and left untouched. - Abandoned
.openfiles are locked, limited to the maximum segment size,
truncated to their last complete newline, synced, and renamed to a recovered.logfile. - Files with no complete record are removed.
.deletingremnants 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 intoConnection.status.byPod. The alpha status separates Connection reconciliation
errors from source-owned publishing state:
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 andwebhook. 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_NOFOLLOWand 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 aggregateactive and errors fields with connectionErrors and source-specificpublishStatuses. 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,
.opento.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 intoConnection.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
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
# Template only
gator policy install k8srequiredlabels
# Bundle includes templates + constraints
gator policy install --bundle pod-security-baselineBundles
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
/* 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 installalways installs the current version from the catalog.
If you need a specific older version of a policy, you must:
- Check out the gatekeeper-library at the desired git tag/commit
- Use
gator policy generate-catalogto create a catalog from that version - Configure
GATOR_CATALOG_URLto 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
$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:
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
Search
$ gator policy search labels
NAME VERSION CATEGORY DESCRIPTION
k8srequiredlabels v1.2.0 general Requires specified labels on resourcesInstall
# 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 constraintsUpdate & Upgrade
$ 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
$ gator policy generate-catalog --library-path=/path/to/gatekeeper-library
Scanning library directory...
Found 45 policies across 5 categories.
Catalog written to catalog.yamlUser Experience Principles
- No confirmation prompts - Use
--dry-runfor safety preview - Fail fast - Stop on first error, allow re-run (idempotent)
- Line-by-line progress - Immediate feedback, CI-friendly
- Explicit over implicit -
--bundleflag,--allfor upgrade - 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
v1alpha1may change without notice. Stability guarantees begin atv1beta1. - JSON output always includes a
apiVersionfield for schema identification.
Table Column Stability:
- Columns
NAME,VERSION,STATUSare 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:
- Recursive expansion: Inheritance chains are resolved recursively (e.g., A inherits B, B inherits C → A gets C + B + A policies).
- Order: Parent policies are processed before child policies (depth-first, parent-first).
- 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.
- Cycle detection: Circular inheritance is detected at catalog load time and results in a validation error.
- 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)
- Label:
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-errorflag (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--insecureflag to override (with warning).
Path Traversal Protection:
- All content paths (e.g.,
templatePath,constraintPathin 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:
apiVersionmatches expected Gatekeeper API groups (templates.gatekeeper.sh,constraints.gatekeeper.sh)kindisConstraintTemplateor a valid Constraint kind- YAML is well-formed and passes basic structural validation
- Invalid resources are rejected before any cluster modification.
Sensitive Data:
--dry-runoutput 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): < 2sinstall --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
# 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"gator policy apply -f gator.yaml
gator policy apply -f gator.yaml --prune # Remove unlistedInfo Command
$ 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 messageDoctor Command
$ 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/upgradecommands - 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
/* Detailed source-code truncated for AI context efficiency. */PolicySync CRD
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: SyncCompleteSync Controller CLI Commands
# 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
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
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 statePart 2: Implementation
MVP Implementation
Package Structure
/* 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
/* Detailed source-code truncated for AI context efficiency. */Fetcher Interface
// 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
// 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
// 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
// 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) stringExit Codes
// 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) *ExitErrorCLI Structure
// 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
# 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-runCI Testing
- 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 testE2E Testing
# 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-1Post-MVP Implementation
PolicySync Controller Implementation
API Types
/* Detailed source-code truncated for AI context efficiency. */Controller Reconciler
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
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
# 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: warnFuture Considerations
- OCI Catalog Support: Fetch catalogs from OCI registries
- Signature Verification: Cosign/Notation support for catalog authenticity
- Multi-Cluster: Federation support for syncing to multiple clusters
- Approval Workflow: Require approval before applying policy changes
- Rollback: Automatic rollback on policy failures
- 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:
- 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.
- 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:
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
// 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
Routing happens at the manager level: Controllers, audit, and webhooks don't know about routing. No controller code changes were needed.
Route by API group, not resource name: The API group is stable across versions. No need to maintain a list of resource names.
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.
Both caches must sync before ready:
WaitForCacheSyncwaits for both caches, so the manager won't serve traffic until both are ready.Error on unknown GVK: If
resolveGVKcan'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:
localClusterConfigis 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:
kubectl get crds | grep gatekeeper
kubectl explain configs.config.gatekeeper.sh.spec
kubectl explain constrainttemplates.templates.gatekeeper.sh.specConstraint 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) |
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
- CRD YAML with full OpenAPI schemas:
config/crd/bases - Helm-packaged CRDs, including Provider:
charts/gatekeeper/crds - End-to-end samples: Examples and the demo manifests
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_timeprovides the start time timestamp of the most recent audit rungatekeeper_audit_last_run_end_timeprovides the end time timestamp of the last completed audit rungatekeeper_violationsprovides 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:
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-systemLimitations 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:
{
"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_idfield that uniquely identifies a given audit run. This allows indexing of historical audits - An
event_typefield with a value ofviolation_auditedto 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_startedmarks the beginning of a new audit runconstraint_auditedmarks when a constraint is done being audited for a given run, along with the number of violations foundaudit_finishedmarks 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 to20). 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 to500,0= infinite) Lower chunk size can reduce memory consumption of the auditingPodbut can increase the number requests to the Kubernetes API server. - Audit interval: set
--audit-interval=123(defaults to every60seconds). 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 auditpod.- helm install
--set audit.writeToRAMDisk=true - if not using helm, modify the deployment manifest to mount a ramdiskyaml
- emptyDir: medium: Memory
- helm install
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=auditflag 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:
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
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_SERVICEcapability)- update Gatekeeper deployment manifest spec:
- add
NET_BIND_SERVICEtosecurityContext.capabilities.addto allow binding on privileged ports as non-root - update port from
8443to443
yamlcontainers: - args: - --port=443 ports: - containerPort: 443 name: webhook-server protocol: TCP securityContext: capabilities: drop: ["all"] add: ["NET_BIND_SERVICE"] - add
- update Gatekeeper deployment manifest spec:
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:
- apiGroups:
- security.openshift.io
resourceNames:
- anyuid
resources:
- securitycontextconstraints
verbs:
- useWith 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:
- Rego or CEL policy code in either
spec.targets[].regoorspec.targets[].code[]that defines policy violation logic; see field precedence - The schema of the accompanying
Constraintobject, which represents an instantiation of aConstraintTemplate
`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:
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:
openAPIV3Schema:
# missing type
properties:
labels:
type: array
items:
type: stringThis schema is invalid by default in a v1 ConstraintTemplate. Adding the type information makes the schema valid:
openAPIV3Schema:
type: object
properties:
labels:
type: array
items:
type: stringFor 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:
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:
$ kubectl create ns foobar
namespace/foobar createdThis is incorrect. We'd expect this to fail:
$ 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.
# 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.
$ 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$ 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:
...
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:
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:
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
operations:
- CREATE
- UPDATE
- DELETEYou 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.
operations:
- CREATE
- UPDATE
- CONNECTCustomize 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.
text39s 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:
{"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
--enable-remote-cluster # Enable remote cluster mode
--kubeconfig=/path/to/target.yaml # Kubeconfig for target clusterHow 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:
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:
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.
# 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 nameTo clean up orphaned resources after identifying old pod names:
# 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_PODOther 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-levelflag is set to minimum log levelINFO. Acceptable values for minimum log level are [DEBUG,INFO,WARNING,ERROR]. In production, this flag should not be set toDEBUG.
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:
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:
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:
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
Getting started
Create a kind cluster with a local registry
Kind cluster with a local registry will enable faster image pushing and pulling:
./third_party/github.com/tilt-dev/kind-local/kind-with-registry.shIf you would like to customize the local registry port on your machine (the default port is
5000), you can runexport 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:
{
"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 deployingmanifest_staging/charts/gatekeeperto the kind cluster.trigger_mode(String, default="auto"): Optional setting to configure if tilt should automatically rebuild on changes. Set tomanualto disable auto-rebuilding and require users to trigger rebuilds of individual changed components through the UI.
Run `make tilt`
make tiltOutput
/* 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:
./third_party/github.com/tilt-dev/kind-local/teardown-kind-with-registry.shEmergency (`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.shindicates that enforcement should be carried out by Gatekeeper's validating webhook for a constraint. Supports templates with CEL and Rego.gator.gatekeeper.shindicates that enforcement should be carried out in shift-left via gator-cli for a constraint. Supports templates with CEL and Rego.audit.gatekeeper.shindicates 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.ioindicates 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.
:::notespec.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.
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.
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.
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.
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
configfor it to be reconciled by Gatekeeper. Gatekeeper will ignore the resource if you do not name itconfig.
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.
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:
auditprocess exclusion will exclude resources from specified namespace(s) in audit results.webhookprocess exclusion will exclude resources from specified namespace(s) from the admission webhook.syncprocess exclusion will exclude resources from specified namespace(s) from being synced into OPA.mutation-webhookprocess 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:
Make sure the validating admission webhook configuration for Gatekeeper has the following namespace selector:
yamlnamespaceSelector: matchExpressions: - key: admission.gatekeeper.sh/ignore operator: DoesNotExistthe default Gatekeeper manifest should already have added this. The default name for the
webhook configuration isgatekeeper-validating-webhook-configurationand the default
name for the webhook that needs the namespace selector isvalidation.gatekeeper.shTell 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/ignorelabel to a namespace, that namespace must be listed under the gatekeepercontrollerManager.exemptNamespacesparameter when installing via Helm.Add the
admission.gatekeeper.sh/ignorelabel 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 theenable-generator-resource-expansionflag 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
templateSourceon theparent resource, which is used as the blueprint for the expanded resource. For example, in a case where aDeploymentexpands into aPod,spec.templatewould typically be the
source. - Optionally, an
enforcementActionoverride 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
theExpansionTemplate, 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.
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 targetPods 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 thegator 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 clusterOriginal– the config will only apply to Original resources, and will not
affect expanded resourcesAll– the config will apply to bothGeneratedandOriginalresources.
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 theReplicaSet'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:
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:
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
ExpansionTemplateconfigured to expandDeploymentsintoPods - an
Assignmutator to add the Istio sidecar container toPods - a
ModifySetmutator to add theproxyandsidecarargs - an inbound
Deployment, and the expandedPod
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.
/* Detailed source-code truncated for AI context efficiency. */When expanded, the above configs will produce the following Pod:
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:
- ALLExport 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
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.
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.
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,
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,
apiVersion: connection.gatekeeper.sh/v1alpha1
kind: Connection
metadata:
name: audit-connection
namespace: gatekeeper-system
spec:
driver: "foo"
config:
key: valueThe
spec.driverfield must exist and must match one of the keys of thesupportedDriversmap that was defined earlier to use the corresponding driver. Thespec.configfield in the configuration can vary depending on the driver being used. For dapr driver,spec.configmust becomponent: "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:
apiVersion: connection.gatekeeper.sh/v1alpha1
kind: Connection
metadata:
name: audit-connection
namespace: gatekeeper-system
spec:
driver: "disk"
config:
path: "tmp/violations/topics"
maxAuditResults: 3driverfield determines which tool/driver should be used to establish a connection. Valid values are:dapr,diskconfigfield 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.
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 toaudit-connection.--audit-channel: Channel shared by audit and admission export. Defaults toaudit-channel.
The equivalent Helm configuration is:
enableAdmissionViolationExport: true
exportBackend: diskAdmission 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.
enableAdmissionViolationExport: true
exportBackend: disk
audit:
connection: audit-connection
channel: audit-channel
exportConnection:
path: tmp/violations/topics
maxAuditResults: 3
admission:
disableExportSidecar: trueAdmission 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
shutdownreason. - 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 defaultemptyDirsurvives 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
auditentry 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
lastAttemptTimeandlastSuccessTimeto 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.
Install or upgrade Gatekeeper with admission export, the disk backend, and the demonstration reader enabled:
shellhelm 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=2mInstall a test policy that requires the
testlabel on Pods in thenginxnamespace:shellexport 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; doneSubmit a Pod that violates the policy. The command is expected to fail with a denial:
shellkubectl 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 EOFConfirm that a controller-manager reader observed the admission record:
shellkubectl 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:shellkubectl get connection audit-connection \ --namespace gatekeeper-system \ --output yaml
Quick start with exporting audit violations using Dapr and Redis
Prerequisites for Dapr
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.
:::
> 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).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
Create
fake-subscribernamespace and redis secretshellkubectl 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 -Create Dapr pubsub component
shellkubectl 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 EOFPlease use this guide to properly configure Redis pubsub component for Dapr.
Deploy subscriber application
yamlapiVersion: 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
Thefake-subscriberimage is published as part of each Gatekeeper release atghcr.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
Create Gatekeeper namespace, and create Dapr pubsub component and Redis secret in Gatekeeper's namespace (
gatekeeper-systemby default). Please make sure to updategatekeeper-systemnamespace for the next steps if your cluster's Gatekeeper namespace is different.shellkubectl 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 EOFTo upgrade or install Gatekeeper with
--enable-violation-exportset totrue,--audit-connectionset toaudit-connection,--audit-channelset toaudit-channelon 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.yamlNote: Verify that after the audit pod is running there is a Dapr sidecar injected and running along side
managercontainer.Create connection config to establish a connection.
shellkubectl apply -f - <<EOF apiVersion: connection.gatekeeper.sh/v1alpha1 kind: Connection metadata: name: audit-connection namespace: gatekeeper-system spec: driver: "dapr" config: component: "pubsub" EOFNote: Name of the
Connectioncustom resource must match the value of--audit-connectionfor it to be used by audit to export violation. At the moment, only one connection can exist for audit.Create the constraint templates and constraints, and make sure audit ran by checking constraints. If constraint status is updated with information such as
auditTimeStamportotalViolations, then audit has ran at least once. Additionally, populatedTOTAL-VIOLATIONSfield for all constraints while listing constraints also indicates that audit has ran at least once.logkubectl get constraint NAME ENFORCEMENT-ACTION TOTAL-VIOLATIONS pod-must-have-test 0Finally, check the subscriber logs to see the violations received.
logkubectl 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
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:
yamlaudit: 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 imageghcr.io/open-policy-agent/fake-readeris 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.
:::shellhelm 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
Connectionresource is installed with the following values and defaults:yamlapiVersion: connection.gatekeeper.sh/v1alpha1 kind: Connection metadata: name: "audit-connection" namespace: "gatekeeper-system" spec: driver: "disk" config: path: "tmp/violations/topics" maxAuditResults: 3Property 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.
shellkubectl 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:yamlaudit: exportVolume: <your-volume> exportVolumeMount: path: <volume-mount-path> exportSidecar: <your-side-car>:::
Create the constraint templates and constraints, and make sure audit ran by checking constraints. If constraint status is updated with information such as
auditTimeStamportotalViolations, then audit has ran at least once. Additionally, populatedTOTAL-VIOLATIONSfield for all constraints while listing constraints also indicates that audit has ran at least once.logkubectl get constraint NAME ENFORCEMENT-ACTION TOTAL-VIOLATIONS pod-must-have-test 0Finally, check the sidecar reader logs to see the violations written.
logkubectl 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:
{
"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.sendbuilt-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_dataRego 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:
- ratify
- cosign-gatekeeper-provider
- GitHub artifact attestations OPA provider
- JFrog verify evidence OPA provider
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.
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.
// 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.
// 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 providerValue: the value that was returned from the provider for that keyError: 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 querykeys: the list of keys to send to the provider
e.g.,
# 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.valueandspec.parameters.assign.fromMetadataare 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`
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: UsernameProvider response
{
"apiVersion": "externaldata.gatekeeper.sh/v1beta1",
"kind": "ProviderResponse",
"response": {
"idempotent": true,
"items": [
{
"key": "kubernetes-admin",
"value": "[email protected]"
}
]
}
}Mutated object
...
metadata:
annotations:
owner: [email protected]
...`Assign`
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:latestProvider response
{
"apiVersion": "externaldata.gatekeeper.sh/v1beta1",
"kind": "ProviderResponse",
"response": {
"idempotent": true,
"items": [
{
"key": "nginx",
"value": "nginx:v1.2.3"
}
]
}
}Mutated object
...
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
stringfields (e.g..spec.containers[name:*].image). AssignMetadataonly supportsdataSource: UsernamebecauseAssignMetadataonly supports creation ofmetadata.annotationsandmetadata.labels.dataSource: ValueAtLocationwill not return any data.ModifySetdoes 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.
- Generate a private key for your CA:
openssl genrsa -out ca.key 2048- Generate a self-signed certificate for your CA:
openssl req -new -x509 -days 365 -key ca.key -subj "/O=My Org/CN=External Data Provider CA" -out ca.crt- Generate a private key for your external data provider:
openssl genrsa -out server.key 2048- Generate a certificate signing request (CSR) for your external data provider:
Replace
<service name>and<service namespace>with the correct values.
openssl req -newkey rsa:2048 -nodes -keyout server.key -subj "/CN=<service name>.<service namespace>" -out server.csr- Generate a certificate for your external data provider:
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.crtHow 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