├── assets ├── policy_as_code_war.png └── minikube.svg ├── opa-gatekeeper ├── valid-namespace.yaml ├── invalid-namespace.yaml ├── k8srequiredlabels-constraint.yaml ├── k8srequiredlabels-constraint-template.yaml └── deploy.yaml ├── kyverno ├── validating │ ├── valid-deployment.yaml │ ├── requirelabels-clusterpolicy.yaml │ └── invalid-deployment.yaml ├── mutating │ ├── deployment.yaml │ └── set-image-pull-policy-clusterpolicy.yaml └── generating │ └── sync-secret-clusterpolicy.yaml └── README.md /assets/policy_as_code_war.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-guy/policy-as-code-war/HEAD/assets/policy_as_code_war.png -------------------------------------------------------------------------------- /opa-gatekeeper/valid-namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: valid-namespace 5 | labels: 6 | gatekeeper: test 7 | -------------------------------------------------------------------------------- /opa-gatekeeper/invalid-namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | creationTimestamp: null 5 | name: invalid-namespace 6 | spec: {} 7 | status: {} 8 | -------------------------------------------------------------------------------- /opa-gatekeeper/k8srequiredlabels-constraint.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: constraints.gatekeeper.sh/v1beta1 2 | kind: K8sRequiredLabels 3 | metadata: 4 | name: ns-must-have-gk 5 | spec: 6 | match: 7 | kinds: 8 | - apiGroups: [""] 9 | kinds: ["Namespace"] 10 | parameters: 11 | labels: ["gatekeeper"] -------------------------------------------------------------------------------- /kyverno/validating/valid-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/name: nginx 7 | name: nginx 8 | spec: 9 | containers: 10 | - image: nginx 11 | name: nginx 12 | resources: {} 13 | dnsPolicy: ClusterFirst 14 | restartPolicy: Always 15 | status: {} 16 | -------------------------------------------------------------------------------- /kyverno/mutating/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/name: nginx 7 | name: nginx 8 | spec: 9 | containers: 10 | - image: nginx:latest 11 | name: nginx 12 | imagePullPolicy: "IfNotPresent" 13 | resources: {} 14 | dnsPolicy: ClusterFirst 15 | restartPolicy: Always 16 | status: {} 17 | -------------------------------------------------------------------------------- /kyverno/validating/requirelabels-clusterpolicy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: require-labels 5 | spec: 6 | validationFailureAction: enforce 7 | rules: 8 | - name: check-for-labels 9 | match: 10 | resources: 11 | kinds: 12 | - Pod 13 | validate: 14 | message: "label `app.kubernetes.io/name` is required" 15 | pattern: 16 | metadata: 17 | labels: 18 | app.kubernetes.io/name: "?*" -------------------------------------------------------------------------------- /kyverno/validating/invalid-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app: nginx 7 | name: nginx 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: nginx 13 | strategy: {} 14 | template: 15 | metadata: 16 | creationTimestamp: null 17 | labels: 18 | app: nginx 19 | spec: 20 | containers: 21 | - image: nginx 22 | name: nginx 23 | resources: {} 24 | status: {} 25 | -------------------------------------------------------------------------------- /kyverno/generating/sync-secret-clusterpolicy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: sync-secret 5 | spec: 6 | rules: 7 | - name: sync-secret 8 | match: 9 | resources: 10 | kinds: 11 | - Namespace 12 | selector: 13 | matchLabels: 14 | mycorp-rollout: "true" 15 | generate: 16 | kind: Secret 17 | name: corp-secret 18 | namespace: "{{request.object.metadata.name}}" 19 | synchronize : true 20 | clone: 21 | namespace: default 22 | name: corp-secret -------------------------------------------------------------------------------- /kyverno/mutating/set-image-pull-policy-clusterpolicy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: set-image-pull-policy 5 | spec: 6 | rules: 7 | - name: set-image-pull-policy 8 | match: 9 | resources: 10 | kinds: 11 | - Pod 12 | mutate: 13 | overlay: 14 | spec: 15 | containers: 16 | # match images which end with :latest 17 | - (image): "*:latest" 18 | # set the imagePullPolicy to "IfNotPresent" 19 | imagePullPolicy: "Always" 20 | -------------------------------------------------------------------------------- /opa-gatekeeper/k8srequiredlabels-constraint-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: templates.gatekeeper.sh/v1beta1 2 | kind: ConstraintTemplate 3 | metadata: 4 | name: k8srequiredlabels 5 | spec: 6 | crd: 7 | spec: 8 | names: 9 | kind: K8sRequiredLabels 10 | validation: 11 | # Schema for the `parameters` field 12 | openAPIV3Schema: 13 | properties: 14 | labels: 15 | type: array 16 | items: string 17 | targets: 18 | - target: admission.k8s.gatekeeper.sh 19 | rego: | 20 | package k8srequiredlabels 21 | 22 | violation[{"msg": msg, "details": {"missing_labels": missing}}] { 23 | provided := {label | input.review.object.metadata.labels[label]} 24 | required := {label | label := input.parameters.labels[_]} 25 | missing := required - provided 26 | count(missing) > 0 27 | msg := sprintf("you must provide labels: %v", [missing]) 28 | } -------------------------------------------------------------------------------- /assets/minikube.svg: -------------------------------------------------------------------------------- 1 | folder_type_minikube -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![policy_as_code_war](./assets/policy_as_code_war.png) 2 | 3 | # Introduction 4 | In this guide, we are going to demonstrate what OPA Gatekeeper and Kyverno are, what are the differences between them and how we can set up and use them in the Kubernetes cluster by doing hands-on demo. 5 | 6 | So, if you are interested in with one of these topics, please keep reading, there is a lots of good details in the following sections 💪. 7 | 8 | Let's start with defining what Policy-as-Code concept is. 9 | 10 | 11 | 12 | 13 | - 🧰 [Prerequisites](#prerequisites) 14 | - 🛡️ [What is Policy-as-Code?](#what-is-policy-as-code) 15 | - [What is OPA Gatekeeper ?](#what-is-opa-gatekeeper-) 16 | - [What is Kyverno ?](#what-is-kyverno-) 17 | - 🎭 [What are differences between OPA Gatekeeper and Kyverno ?](#what-are-differences-between-opa-gatekeeper-and-kyverno-) 18 | - 🧑‍💻 [Hands On](#hands-on) 19 | - 👀 [References](#references) 20 | 21 | 22 | 23 | # Prerequisites 24 | 25 | * minikube v1.17.1 26 | * kubectl v1.20.2 27 | 28 | # What is Policy-as-Code? 29 | Similar to the concept of `Infrastructure-as-Code (IaC)` and the benefits you get from codifying your infrastructure setup using the software development practices, `Policy-as-Code (PaC)` is the codification of your policies. 30 | 31 | PaC is the idea of writing code in a high-level language to manage and automate policies. By representing policies as code in text files, proven software development best practices can be adopted such as version control, automated testing, and automated deployment. 32 | 33 | The policies you want to enforce come from your organization’s established guidelines or agreed-upon conventions, and best practices within the industry. It could also be derived from tribal knowledge that has accumulated over the years within your operations and development teams. 34 | 35 | PaC is very general, so, it can be applied to any environment that you want to manage and enforce policies but if want to apply it unto the Kubernetes world, there are two tools became very important: OPA Gatekeeper and Kyverno. 36 | 37 | Let's continue with the description of these tools. 38 | 39 | # What is OPA Gatekeeper ? 40 | Before move on with the description of the OPA Gatekeeper, we should explain the OPA (Open Policy Agent) is first. 41 | 42 | The [OPA](https://github.com/open-policy-agent/opa) is an open-source, general-purpose policy engine that can be used to enforce policies on various types of software systems like microservices, CI/CD pipelines, gateways, Kubernetes, etc. OPA was developed by Styra and is currently a part of CNCF. 43 | 44 | The [OPA Gatekeeper](https://github.com/open-policy-agent/gatekeeper) is the policy controller for Kubernetes. More technically, it is a customizable Kubernetes Admission Webhook that helps enforce policies and strengthen governance. 45 | 46 | The important thing that we should notice is the use of OPA is not tied to the Kubernetes alone. OPA Gatekeeper, on the other hand, is specifically built for Kubernetes Admission Control use case of OPA. 47 | 48 | # What is Kyverno ? 49 | [Kyverno](https://github.com/kyverno/kyverno/) is a policy engine designed for Kubernetes. With Kyverno, policies are managed as Kubernetes resources and no new language is required to write policies. This allows using familiar tools such as kubectl, git, and kustomize to manage policies. Kyverno policies can validate, mutate, and generate Kubernetes resources. The Kyverno CLI can be used to test policies and validate resources as part of a CI/CD pipeline. Kyverno is an open-source and a part of CNCF Sandbox Project also. 50 | 51 | # What are differences between OPA Gatekeeper and Kyverno ? 52 | Let's explain these differences with the table format. 53 | 54 | | Features/Capabilities | Gatekeeper | Kyverno | 55 | |--------------------------------------------- |------------ |--------- | 56 | | Validation | ✓ | ✓ | 57 | | Mutation | ✓* | ✓ | 58 | | Generation | X | ✓ | 59 | | Policy as native resources | ✓ | ✓ | 60 | | Metrics exposed | ✓ | ✓ | 61 | | OpenAPI validation schema (kubectl explain) | X | ✓ | 62 | | High Availability | ✓ | ✓ | 63 | | API object lookup | ✓ | ✓* | 64 | | CLI with test ability | ✓** | ✓ | 65 | | Policy audit ability | ✓ | ✓ | 66 | 67 | `* Alpha status` 68 | `** Separate CLI` 69 | 70 | > Credit: https://neonmirrors.net/post/2021-02/kubernetes-policy-comparison-opa-gatekeeper-vs-kyverno/ 71 | 72 | In my opinion, the best advantages of using Kyverno are no need to learn another policy language and the OpenAPI validation schema support that we can use via kubectl explain command. On the other hand side, OPA Gatekeeper has lots of tools developed around the Rego language to help us to write and test our policies such as [conftest](https://github.com/instrumenta/conftest), [konstraint](https://github.com/plexsystems/konstraint) and this is a big plus in my opinion. These are the tools that we can use to implement `Policy-as-Code Pipeline`. Another advantage of using OPA Gatekeeper, therese are lots of libraries that includes ready to use policies written for us such as [gatekeeper-library](https://github.com/open-policy-agent/gatekeeper-library), [konstraint-examples](https://github.com/plexsystems/konstraint/tree/main/examples) and [raspbernetes-policies](https://github.com/raspbernetes/k8s-security-policies/tree/master/policies). 73 | 74 | # Hands On 75 | I created two seperate folders for OPA Gatekeeper and Kyverno resources. We are going to start with the OPA Gatekepeer project first. 76 | 77 | There are various types of installation of OPA Gatekeeper but in this section we are going to use [plain YAML manifest](./opa-gatekeeper/deploy.yaml) to install it. Let's install OPA Gatekeeper using the YAML manifest. In order to do that, we need to start our local Kubernetes cluster using `minikube`, we are going to use two different [Minikube profiles](https://minikube.sigs.k8s.io/docs/commands/profile/) for the OPA Gatekeeper and the Kyverno, that will result with the creating two seperate Kubernetes cluster. 78 | ```bash 79 | $ minikube start -p opa-gatekeeper 80 | 😄 [opa-gatekeeper] minikube v1.17.1 on Darwin 10.15.7 81 | ✨ Using the hyperkit driver based on user configuration 82 | 👍 Starting control plane node opa-gatekeeper in cluster opa-gatekeeper 83 | 🔥 Creating hyperkit VM (CPUs=3, Memory=8192MB, Disk=20000MB) ... 84 | 🌐 Found network options: 85 | ▪ no_proxy=127.0.0.1,localhost 86 | 🐳 Preparing Kubernetes v1.20.2 on Docker 20.10.2 ... 87 | ▪ env NO_PROXY=127.0.0.1,localhost 88 | ▪ Generating certificates and keys ... 89 | ▪ Booting up control plane ... 90 | ▪ Configuring RBAC rules ... 91 | 🔎 Verifying Kubernetes components... 92 | 🌟 Enabled addons: storage-provisioner, default-storageclass 93 | 🏄 Done! kubectl is now configured to use "opa-gatekeeper" cluster and "default" namespace by default 94 | ``` 95 | 96 | Let's apply the manifest. 97 | ```bash 98 | $ kubectl apply -f opa-gatekeeper/deploy.yaml 99 | namespace/gatekeeper-system created 100 | Warning: apiextensions.k8s.io/v1beta1 CustomResourceDefinition is deprecated in v1.16+, unavailable in v1.22+; use apiextensions.k8s.io/v1 CustomResourceDefinition 101 | customresourcedefinition.apiextensions.k8s.io/configs.config.gatekeeper.sh created 102 | customresourcedefinition.apiextensions.k8s.io/constraintpodstatuses.status.gatekeeper.sh created 103 | customresourcedefinition.apiextensions.k8s.io/constrainttemplatepodstatuses.status.gatekeeper.sh created 104 | customresourcedefinition.apiextensions.k8s.io/constrainttemplates.templates.gatekeeper.sh created 105 | serviceaccount/gatekeeper-admin created 106 | podsecuritypolicy.policy/gatekeeper-admin created 107 | role.rbac.authorization.k8s.io/gatekeeper-manager-role created 108 | clusterrole.rbac.authorization.k8s.io/gatekeeper-manager-role created 109 | rolebinding.rbac.authorization.k8s.io/gatekeeper-manager-rolebinding created 110 | clusterrolebinding.rbac.authorization.k8s.io/gatekeeper-manager-rolebinding created 111 | secret/gatekeeper-webhook-server-cert created 112 | service/gatekeeper-webhook-service created 113 | deployment.apps/gatekeeper-audit created 114 | deployment.apps/gatekeeper-controller-manager created 115 | Warning: admissionregistration.k8s.io/v1beta1 ValidatingWebhookConfiguration is deprecated in v1.16+, unavailable in v1.22+; use admissionregistration.k8s.io/v1 ValidatingWebhookConfiguration 116 | validatingwebhookconfiguration.admissionregistration.k8s.io/gatekeeper-validating-webhook-configuration created 117 | ``` 118 | 119 | You should notice that bunch of CRDs created to allow define and enforce policies called `ConstraintTemplate` which describes both the Rego that enforces the constraint and the schema of the constraint. 120 | 121 | In this section, we are going to enforce policy to validate required labels that we want to on resources, if required label exits then we'll approve the request, if not we'll reject it. 122 | 123 | Let's look at the `ConstraintTemplate` that we are going to apply. 124 | ```yaml 125 | apiVersion: templates.gatekeeper.sh/v1beta1 126 | kind: ConstraintTemplate 127 | metadata: 128 | name: k8srequiredlabels 129 | spec: 130 | crd: 131 | spec: 132 | names: 133 | kind: K8sRequiredLabels 134 | validation: 135 | # Schema for the `parameters` field 136 | openAPIV3Schema: 137 | properties: 138 | labels: 139 | type: array 140 | items: string 141 | targets: 142 | - target: admission.k8s.gatekeeper.sh 143 | rego: | 144 | package k8srequiredlabels 145 | 146 | violation[{"msg": msg, "details": {"missing_labels": missing}}] { 147 | provided := {label | input.review.object.metadata.labels[label]} 148 | required := {label | label := input.parameters.labels[_]} 149 | missing := required - provided 150 | count(missing) > 0 151 | msg := sprintf("you must provide labels: %v", [missing]) 152 | } 153 | ``` 154 | 155 | You should notice that the policy that we define with the Rego language is placed under the `.targets[].rego` section. Once we applied this to the cluster, `K8sRequiredLabels` Custom Resource is going to be created and by using this CR we'll define our policy context, means which resources we want to apply the policy on. 156 | 157 | Let's apply it. 158 | ```bash 159 | $ kubectl apply -f opa-gatekeeper/k8srequiredlabels-constraint-template.yaml 160 | constrainttemplate.templates.gatekeeper.sh/k8srequiredlabels created 161 | 162 | $ kubectl get customresourcedefinitions.apiextensions.k8s.io 163 | Found existing alias for "kubectl". You should use: "k" 164 | NAME CREATED AT 165 | configs.config.gatekeeper.sh 2021-02-25T09:06:10Z 166 | constraintpodstatuses.status.gatekeeper.sh 2021-02-25T09:06:10Z 167 | constrainttemplatepodstatuses.status.gatekeeper.sh 2021-02-25T09:06:10Z 168 | constrainttemplates.templates.gatekeeper.sh 2021-02-25T09:06:10Z 169 | k8srequiredlabels.constraints.gatekeeper.sh 2021-02-25T09:19:39Z 170 | ``` 171 | 172 | As you can see, `K8sRequiredLabels` CR is created. Lets define and apply it too. 173 | ```yaml 174 | apiVersion: constraints.gatekeeper.sh/v1beta1 175 | kind: K8sRequiredLabels 176 | metadata: 177 | name: ns-must-have-gk 178 | spec: 179 | match: 180 | kinds: 181 | - apiGroups: [""] 182 | kinds: ["Namespace"] 183 | parameters: 184 | labels: ["gatekeeper"] 185 | ``` 186 | 187 | You should notice that we'll enforce the policy on `Namespace` resource and the label value that we want to be available on the Namespace is `gatekepeer`. 188 | ```bash 189 | $ kubectl apply -f opa-gatekeeper/k8srequiredlabels-constraint.yaml 190 | k8srequiredlabels.constraints.gatekeeper.sh/ns-must-have-gk created 191 | ``` 192 | 193 | Let's test with creating invalid namespace then a valid one. 194 | ```bash 195 | $ kubectl apply -f opa-gatekeeper/invalid-namespace.yaml 196 | Found existing alias for "kubectl apply -f". You should use: "kaf" 197 | Error from server ([denied by ns-must-have-gk] you must provide labels: {"gatekeeper"}): error when creating "opa-gatekeeper/invalid-namespace.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [denied by ns-must-have-gk] you must provide labels: {"gatekeeper"} 198 | ``` 199 | 200 | ```bash 201 | $ kubectl apply -f opa-gatekeeper/valid-namespace.yaml 202 | Found existing alias for "kubectl apply -f". You should use: "kaf" 203 | namespace/valid-namespace created 204 | ``` 205 | 206 | Tadaaaa, it worked 🎉🎉🎉🎉 207 | 208 | Let's move on with the Kyverno, again, there are various way to install it unto the Kubernetes, in this case, we are going to use Helm. We said that we'll start up another Minikub cluster with different profile. 209 | Let's start with it. 210 | ```bash 211 | $ minikube start -p kyverno 212 | 😄 [kyverno] minikube v1.17.1 on Darwin 10.15.7 213 | ✨ Using the hyperkit driver based on user configuration 214 | 👍 Starting control plane node kyverno in cluster kyverno 215 | 🔥 Creating hyperkit VM (CPUs=3, Memory=8192MB, Disk=20000MB) ... 216 | 🌐 Found network options: 217 | ▪ no_proxy=127.0.0.1,localhost 218 | 🐳 Preparing Kubernetes v1.20.2 on Docker 20.10.2 ... 219 | ▪ env NO_PROXY=127.0.0.1,localhost 220 | ▪ Generating certificates and keys ... 221 | ▪ Booting up control plane ... 222 | ▪ Configuring RBAC rules ... 223 | 🔎 Verifying Kubernetes components... 224 | 🌟 Enabled addons: storage-provisioner, default-storageclass 225 | 🏄 Done! kubectl is now configured to use "kyverno" cluster and "default" namespace by default 226 | 227 | $ minikube profile list 228 | |----------------|-----------|---------|---------------|------|---------|---------|-------| 229 | | Profile | VM Driver | Runtime | IP | Port | Version | Status | Nodes | 230 | |----------------|-----------|---------|---------------|------|---------|---------|-------| 231 | | kyverno | hyperkit | docker | 192.168.64.17 | 8443 | v1.20.2 | Running | 1 | 232 | | minikube | hyperkit | docker | 192.168.64.15 | 8443 | v1.20.2 | Stopped | 1 | 233 | | opa-gatekeeper | hyperkit | docker | 192.168.64.16 | 8443 | v1.20.2 | Running | 1 | 234 | |----------------|-----------|---------|---------------|------|---------|---------|-------| 235 | ``` 236 | 237 | Let's install it by using Helm. 238 | ```bash 239 | $ helm repo add kyverno https://kyverno.github.io/kyverno/ 240 | "kyverno" has been added to your repositories 241 | 242 | $ helm repo update 243 | Hang tight while we grab the latest from your chart repositories... 244 | ...Successfully got an update from the "kyverno" chart repository 245 | ...Successfully got an update from the "nats" chart repository 246 | ...Successfully got an update from the "falcosecurity" chart repository 247 | ...Successfully got an update from the "openfaas" chart repository 248 | ...Successfully got an update from the "stable" chart repository 249 | Update Complete. ⎈Happy Helming!⎈ 250 | 251 | $ helm install kyverno --namespace kyverno kyverno/kyverno --create-namespace 252 | NAME: kyverno 253 | LAST DEPLOYED: Thu Feb 25 13:16:21 2021 254 | NAMESPACE: kyverno 255 | STATUS: deployed 256 | REVISION: 1 257 | TEST SUITE: None 258 | NOTES: 259 | Thank you for installing kyverno 😀 260 | 261 | Your release is named kyverno. 262 | 263 | We have installed the "default" profile of Pod Security Standards and set them in audit mode. 264 | 265 | Visit https://kyverno.io/policies/ to find more sample policies. 266 | ``` 267 | 268 | Let's look at the Custom Resource Definitions list. 269 | ```bash 270 | $ kubectl get customresourcedefinitions.apiextensions.k8s.io 271 | Found existing alias for "kubectl". You should use: "k" 272 | NAME CREATED AT 273 | clusterpolicies.kyverno.io 2021-02-25T10:16:16Z 274 | clusterpolicyreports.wgpolicyk8s.io 2021-02-25T10:16:16Z 275 | clusterreportchangerequests.kyverno.io 2021-02-25T10:16:16Z 276 | generaterequests.kyverno.io 2021-02-25T10:16:16Z 277 | policies.kyverno.io 2021-02-25T10:16:16Z 278 | policyreports.wgpolicyk8s.io 2021-02-25T10:16:16Z 279 | reportchangerequests.kyverno.io 2021-02-25T10:16:16Z 280 | ``` 281 | 282 | We can also use `kubectl explain` command to get information easily about the resource using the OpenAPI schema. 283 | ```bash 284 | $ kubectl explain policies 285 | KIND: Policy 286 | VERSION: kyverno.io/v1 287 | 288 | DESCRIPTION: 289 | Policy declares validation, mutation, and generation behaviors for matching 290 | resources. See: https://kyverno.io/docs/writing-policies/ for more 291 | information. 292 | 293 | FIELDS: 294 | apiVersion 295 | APIVersion defines the versioned schema of this representation of an 296 | object. Servers should convert recognized schemas to the latest internal 297 | value, and may reject unrecognized values. More info: 298 | https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 299 | 300 | kind 301 | Kind is a string value representing the REST resource this object 302 | represents. Servers may infer this from the endpoint the client submits 303 | requests to. Cannot be updated. In CamelCase. More info: 304 | https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 305 | 306 | metadata 307 | Standard object's metadata. More info: 308 | https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata 309 | 310 | spec -required- 311 | Spec defines policy behaviors and contains one or rules. 312 | 313 | status 314 | Status contains policy runtime information. 315 | ``` 316 | 317 | Lets look at our first policy definition. In this case we are using validating feature of Kyverno. 318 | ```yaml 319 | apiVersion: kyverno.io/v1 320 | kind: ClusterPolicy 321 | metadata: 322 | name: require-labels 323 | spec: 324 | validationFailureAction: enforce 325 | rules: 326 | - name: check-for-labels 327 | match: 328 | resources: 329 | kinds: 330 | - Pod 331 | validate: 332 | message: "label `app.kubernetes.io/name` is required" 333 | pattern: 334 | metadata: 335 | labels: 336 | app.kubernetes.io/name: "?*" 337 | ``` 338 | You should notice that we enforcing a required label policy on Pod resource. We are ddefining policies using native Kyverno Custom Resource called `ClusterPolicy`. 339 | 340 | Let's apply it. 341 | ```bash 342 | $ kubectl apply -f kyverno/validating/requirelabels-clusterpolicy.yaml 343 | clusterpolicy.kyverno.io/require-labels created 344 | ``` 345 | 346 | Let's test it by creating a Deployment that violates the policy. 347 | ```bash 348 | $ kubectl apply -f kyverno/validating/invalid-deployment.yaml 349 | Found existing alias for "kubectl apply -f". You should use: "kaf" 350 | Error from server: error when creating "kyverno/validating/invalid-deployment.yaml": admission webhook "validate.kyverno.svc" denied the request: 351 | 352 | resource Deployment/default/nginx was blocked due to the following policies 353 | 354 | require-labels: 355 | autogen-check-for-labels: 'validation error: label `app.kubernetes.io/name` is required. Rule autogen-check-for-labels failed at path /spec/template/metadata/labels/app.kubernetes.io/name/' 356 | ``` 357 | 358 | Let's apply valid one. 359 | ```bash 360 | $ kubectl apply -f kyverno/validating/valid-deployment.yaml 361 | pod/nginx created 362 | 363 | $ kubectl get pods 364 | NAME READY STATUS RESTARTS AGE 365 | nginx 0/1 ContainerCreating 0 6s 366 | ``` 367 | 368 | Tadaaaa, it worked 🎉🎉🎉🎉 369 | 370 | # References 371 | * https://medium.com/trendyol-tech/enforce-organizational-policies-and-security-best-practices-to-your-kubernetes-clusters-by-using-dfc085528e07 372 | * https://www.velotio.com/engineering-blog/deploy-opa-on-kubernetes 373 | * https://engineering.mercari.com/en/blog/entry/20201222-enhance-kubernetes-security-with-opa-gatekeeper/ 374 | * https://docs.hashicorp.com/sentinel/concepts/policy-as-code 375 | * https://www.magalix.com/blog/policy-as-code-for-kubernetes 376 | * https://betterprogramming.pub/policy-as-code-on-kubernetes-with-kyverno-b144749f144 377 | * https://itnext.io/fitness-validation-for-your-kubernetes-apps-policy-as-code-7fad698e7dec 378 | -------------------------------------------------------------------------------- /opa-gatekeeper/deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | admission.gatekeeper.sh/ignore: no-self-managing 6 | control-plane: controller-manager 7 | gatekeeper.sh/system: "yes" 8 | name: gatekeeper-system 9 | --- 10 | apiVersion: apiextensions.k8s.io/v1beta1 11 | kind: CustomResourceDefinition 12 | metadata: 13 | annotations: 14 | controller-gen.kubebuilder.io/version: v0.3.0 15 | creationTimestamp: null 16 | labels: 17 | gatekeeper.sh/system: "yes" 18 | name: configs.config.gatekeeper.sh 19 | spec: 20 | group: config.gatekeeper.sh 21 | names: 22 | kind: Config 23 | listKind: ConfigList 24 | plural: configs 25 | singular: config 26 | scope: Namespaced 27 | validation: 28 | openAPIV3Schema: 29 | description: Config is the Schema for the configs API 30 | properties: 31 | apiVersion: 32 | description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 33 | type: string 34 | kind: 35 | description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 36 | type: string 37 | metadata: 38 | type: object 39 | spec: 40 | description: ConfigSpec defines the desired state of Config 41 | properties: 42 | match: 43 | description: Configuration for namespace exclusion 44 | items: 45 | properties: 46 | excludedNamespaces: 47 | items: 48 | type: string 49 | type: array 50 | processes: 51 | items: 52 | type: string 53 | type: array 54 | type: object 55 | type: array 56 | readiness: 57 | description: Configuration for readiness tracker 58 | properties: 59 | statsEnabled: 60 | type: boolean 61 | type: object 62 | sync: 63 | description: Configuration for syncing k8s objects 64 | properties: 65 | syncOnly: 66 | description: If non-empty, only entries on this list will be replicated into OPA 67 | items: 68 | properties: 69 | group: 70 | type: string 71 | kind: 72 | type: string 73 | version: 74 | type: string 75 | type: object 76 | type: array 77 | type: object 78 | validation: 79 | description: Configuration for validation 80 | properties: 81 | traces: 82 | description: List of requests to trace. Both "user" and "kinds" must be specified 83 | items: 84 | properties: 85 | dump: 86 | description: Also dump the state of OPA with the trace. Set to `All` to dump everything. 87 | type: string 88 | kind: 89 | description: Only trace requests of the following GroupVersionKind 90 | properties: 91 | group: 92 | type: string 93 | kind: 94 | type: string 95 | version: 96 | type: string 97 | type: object 98 | user: 99 | description: Only trace requests from the specified user 100 | type: string 101 | type: object 102 | type: array 103 | type: object 104 | type: object 105 | status: 106 | description: ConfigStatus defines the observed state of Config 107 | type: object 108 | type: object 109 | version: v1alpha1 110 | versions: 111 | - name: v1alpha1 112 | served: true 113 | storage: true 114 | status: 115 | acceptedNames: 116 | kind: "" 117 | plural: "" 118 | conditions: [] 119 | storedVersions: [] 120 | --- 121 | apiVersion: apiextensions.k8s.io/v1beta1 122 | kind: CustomResourceDefinition 123 | metadata: 124 | annotations: 125 | controller-gen.kubebuilder.io/version: v0.3.0 126 | creationTimestamp: null 127 | labels: 128 | gatekeeper.sh/system: "yes" 129 | name: constraintpodstatuses.status.gatekeeper.sh 130 | spec: 131 | group: status.gatekeeper.sh 132 | names: 133 | kind: ConstraintPodStatus 134 | listKind: ConstraintPodStatusList 135 | plural: constraintpodstatuses 136 | singular: constraintpodstatus 137 | scope: Namespaced 138 | validation: 139 | openAPIV3Schema: 140 | description: ConstraintPodStatus is the Schema for the constraintpodstatuses API 141 | properties: 142 | apiVersion: 143 | description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 144 | type: string 145 | kind: 146 | description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 147 | type: string 148 | metadata: 149 | type: object 150 | status: 151 | description: ConstraintPodStatusStatus defines the observed state of ConstraintPodStatus 152 | properties: 153 | constraintUID: 154 | description: Storing the constraint UID allows us to detect drift, such as when a constraint has been recreated after its CRD was deleted out from under it, interrupting the watch 155 | type: string 156 | enforced: 157 | type: boolean 158 | errors: 159 | items: 160 | description: Error represents a single error caught while adding a constraint to OPA 161 | properties: 162 | code: 163 | type: string 164 | location: 165 | type: string 166 | message: 167 | type: string 168 | required: 169 | - code 170 | - message 171 | type: object 172 | type: array 173 | id: 174 | type: string 175 | observedGeneration: 176 | format: int64 177 | type: integer 178 | operations: 179 | items: 180 | type: string 181 | type: array 182 | type: object 183 | type: object 184 | version: v1beta1 185 | versions: 186 | - name: v1beta1 187 | served: true 188 | storage: true 189 | status: 190 | acceptedNames: 191 | kind: "" 192 | plural: "" 193 | conditions: [] 194 | storedVersions: [] 195 | --- 196 | apiVersion: apiextensions.k8s.io/v1beta1 197 | kind: CustomResourceDefinition 198 | metadata: 199 | annotations: 200 | controller-gen.kubebuilder.io/version: v0.3.0 201 | creationTimestamp: null 202 | labels: 203 | gatekeeper.sh/system: "yes" 204 | name: constrainttemplatepodstatuses.status.gatekeeper.sh 205 | spec: 206 | group: status.gatekeeper.sh 207 | names: 208 | kind: ConstraintTemplatePodStatus 209 | listKind: ConstraintTemplatePodStatusList 210 | plural: constrainttemplatepodstatuses 211 | singular: constrainttemplatepodstatus 212 | scope: Namespaced 213 | validation: 214 | openAPIV3Schema: 215 | description: ConstraintTemplatePodStatus is the Schema for the constrainttemplatepodstatuses API 216 | properties: 217 | apiVersion: 218 | description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 219 | type: string 220 | kind: 221 | description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 222 | type: string 223 | metadata: 224 | type: object 225 | status: 226 | description: ConstraintTemplatePodStatusStatus defines the observed state of ConstraintTemplatePodStatus 227 | properties: 228 | errors: 229 | items: 230 | description: CreateCRDError represents a single error caught during parsing, compiling, etc. 231 | properties: 232 | code: 233 | type: string 234 | location: 235 | type: string 236 | message: 237 | type: string 238 | required: 239 | - code 240 | - message 241 | type: object 242 | type: array 243 | id: 244 | description: 'Important: Run "make" to regenerate code after modifying this file' 245 | type: string 246 | observedGeneration: 247 | format: int64 248 | type: integer 249 | operations: 250 | items: 251 | type: string 252 | type: array 253 | templateUID: 254 | description: UID is a type that holds unique ID values, including UUIDs. Because we don't ONLY use UUIDs, this is an alias to string. Being a type captures intent and helps make sure that UIDs and names do not get conflated. 255 | type: string 256 | type: object 257 | type: object 258 | version: v1beta1 259 | versions: 260 | - name: v1beta1 261 | served: true 262 | storage: true 263 | status: 264 | acceptedNames: 265 | kind: "" 266 | plural: "" 267 | conditions: [] 268 | storedVersions: [] 269 | --- 270 | apiVersion: apiextensions.k8s.io/v1beta1 271 | kind: CustomResourceDefinition 272 | metadata: 273 | creationTimestamp: null 274 | labels: 275 | controller-tools.k8s.io: "1.0" 276 | gatekeeper.sh/system: "yes" 277 | name: constrainttemplates.templates.gatekeeper.sh 278 | spec: 279 | group: templates.gatekeeper.sh 280 | names: 281 | kind: ConstraintTemplate 282 | plural: constrainttemplates 283 | scope: Cluster 284 | subresources: 285 | status: {} 286 | validation: 287 | openAPIV3Schema: 288 | properties: 289 | apiVersion: 290 | description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 291 | type: string 292 | kind: 293 | description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 294 | type: string 295 | metadata: 296 | type: object 297 | spec: 298 | properties: 299 | crd: 300 | properties: 301 | spec: 302 | properties: 303 | names: 304 | properties: 305 | kind: 306 | type: string 307 | shortNames: 308 | items: 309 | type: string 310 | type: array 311 | type: object 312 | validation: 313 | type: object 314 | type: object 315 | type: object 316 | targets: 317 | items: 318 | properties: 319 | libs: 320 | items: 321 | type: string 322 | type: array 323 | rego: 324 | type: string 325 | target: 326 | type: string 327 | type: object 328 | type: array 329 | type: object 330 | status: 331 | properties: 332 | byPod: 333 | items: 334 | properties: 335 | errors: 336 | items: 337 | properties: 338 | code: 339 | type: string 340 | location: 341 | type: string 342 | message: 343 | type: string 344 | required: 345 | - code 346 | - message 347 | type: object 348 | type: array 349 | id: 350 | description: a unique identifier for the pod that wrote the status 351 | type: string 352 | observedGeneration: 353 | format: int64 354 | type: integer 355 | type: object 356 | type: array 357 | created: 358 | type: boolean 359 | type: object 360 | version: v1beta1 361 | versions: 362 | - name: v1beta1 363 | served: true 364 | storage: true 365 | - name: v1alpha1 366 | served: true 367 | storage: false 368 | status: 369 | acceptedNames: 370 | kind: "" 371 | plural: "" 372 | conditions: [] 373 | storedVersions: [] 374 | --- 375 | apiVersion: v1 376 | kind: ServiceAccount 377 | metadata: 378 | labels: 379 | gatekeeper.sh/system: "yes" 380 | name: gatekeeper-admin 381 | namespace: gatekeeper-system 382 | --- 383 | apiVersion: policy/v1beta1 384 | kind: PodSecurityPolicy 385 | metadata: 386 | annotations: 387 | seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*' 388 | labels: 389 | gatekeeper.sh/system: "yes" 390 | name: gatekeeper-admin 391 | spec: 392 | allowPrivilegeEscalation: false 393 | fsGroup: 394 | ranges: 395 | - max: 65535 396 | min: 1 397 | rule: MustRunAs 398 | requiredDropCapabilities: 399 | - ALL 400 | runAsUser: 401 | rule: MustRunAsNonRoot 402 | seLinux: 403 | rule: RunAsAny 404 | supplementalGroups: 405 | ranges: 406 | - max: 65535 407 | min: 1 408 | rule: MustRunAs 409 | volumes: 410 | - configMap 411 | - projected 412 | - secret 413 | - downwardAPI 414 | --- 415 | apiVersion: rbac.authorization.k8s.io/v1 416 | kind: Role 417 | metadata: 418 | creationTimestamp: null 419 | labels: 420 | gatekeeper.sh/system: "yes" 421 | name: gatekeeper-manager-role 422 | namespace: gatekeeper-system 423 | rules: 424 | - apiGroups: 425 | - "" 426 | resources: 427 | - events 428 | verbs: 429 | - create 430 | - patch 431 | - apiGroups: 432 | - "" 433 | resources: 434 | - secrets 435 | verbs: 436 | - create 437 | - delete 438 | - get 439 | - list 440 | - patch 441 | - update 442 | - watch 443 | --- 444 | apiVersion: rbac.authorization.k8s.io/v1 445 | kind: ClusterRole 446 | metadata: 447 | creationTimestamp: null 448 | labels: 449 | gatekeeper.sh/system: "yes" 450 | name: gatekeeper-manager-role 451 | rules: 452 | - apiGroups: 453 | - '*' 454 | resources: 455 | - '*' 456 | verbs: 457 | - get 458 | - list 459 | - watch 460 | - apiGroups: 461 | - apiextensions.k8s.io 462 | resources: 463 | - customresourcedefinitions 464 | verbs: 465 | - create 466 | - delete 467 | - get 468 | - list 469 | - patch 470 | - update 471 | - watch 472 | - apiGroups: 473 | - config.gatekeeper.sh 474 | resources: 475 | - configs 476 | verbs: 477 | - create 478 | - delete 479 | - get 480 | - list 481 | - patch 482 | - update 483 | - watch 484 | - apiGroups: 485 | - config.gatekeeper.sh 486 | resources: 487 | - configs/status 488 | verbs: 489 | - get 490 | - patch 491 | - update 492 | - apiGroups: 493 | - constraints.gatekeeper.sh 494 | resources: 495 | - '*' 496 | verbs: 497 | - create 498 | - delete 499 | - get 500 | - list 501 | - patch 502 | - update 503 | - watch 504 | - apiGroups: 505 | - mutations.gatekeeper.sh 506 | resources: 507 | - '*' 508 | verbs: 509 | - create 510 | - delete 511 | - get 512 | - list 513 | - patch 514 | - update 515 | - watch 516 | - apiGroups: 517 | - policy 518 | resourceNames: 519 | - gatekeeper-admin 520 | resources: 521 | - podsecuritypolicies 522 | verbs: 523 | - use 524 | - apiGroups: 525 | - status.gatekeeper.sh 526 | resources: 527 | - '*' 528 | verbs: 529 | - create 530 | - delete 531 | - get 532 | - list 533 | - patch 534 | - update 535 | - watch 536 | - apiGroups: 537 | - templates.gatekeeper.sh 538 | resources: 539 | - constrainttemplates 540 | verbs: 541 | - create 542 | - delete 543 | - get 544 | - list 545 | - patch 546 | - update 547 | - watch 548 | - apiGroups: 549 | - templates.gatekeeper.sh 550 | resources: 551 | - constrainttemplates/finalizers 552 | verbs: 553 | - delete 554 | - get 555 | - patch 556 | - update 557 | - apiGroups: 558 | - templates.gatekeeper.sh 559 | resources: 560 | - constrainttemplates/status 561 | verbs: 562 | - get 563 | - patch 564 | - update 565 | - apiGroups: 566 | - admissionregistration.k8s.io 567 | resourceNames: 568 | - gatekeeper-validating-webhook-configuration 569 | resources: 570 | - validatingwebhookconfigurations 571 | verbs: 572 | - create 573 | - delete 574 | - get 575 | - list 576 | - patch 577 | - update 578 | - watch 579 | --- 580 | apiVersion: rbac.authorization.k8s.io/v1 581 | kind: RoleBinding 582 | metadata: 583 | labels: 584 | gatekeeper.sh/system: "yes" 585 | name: gatekeeper-manager-rolebinding 586 | namespace: gatekeeper-system 587 | roleRef: 588 | apiGroup: rbac.authorization.k8s.io 589 | kind: Role 590 | name: gatekeeper-manager-role 591 | subjects: 592 | - kind: ServiceAccount 593 | name: gatekeeper-admin 594 | namespace: gatekeeper-system 595 | --- 596 | apiVersion: rbac.authorization.k8s.io/v1 597 | kind: ClusterRoleBinding 598 | metadata: 599 | labels: 600 | gatekeeper.sh/system: "yes" 601 | name: gatekeeper-manager-rolebinding 602 | roleRef: 603 | apiGroup: rbac.authorization.k8s.io 604 | kind: ClusterRole 605 | name: gatekeeper-manager-role 606 | subjects: 607 | - kind: ServiceAccount 608 | name: gatekeeper-admin 609 | namespace: gatekeeper-system 610 | --- 611 | apiVersion: v1 612 | kind: Secret 613 | metadata: 614 | labels: 615 | gatekeeper.sh/system: "yes" 616 | name: gatekeeper-webhook-server-cert 617 | namespace: gatekeeper-system 618 | --- 619 | apiVersion: v1 620 | kind: Service 621 | metadata: 622 | labels: 623 | gatekeeper.sh/system: "yes" 624 | name: gatekeeper-webhook-service 625 | namespace: gatekeeper-system 626 | spec: 627 | ports: 628 | - port: 443 629 | targetPort: 8443 630 | selector: 631 | control-plane: controller-manager 632 | gatekeeper.sh/operation: webhook 633 | gatekeeper.sh/system: "yes" 634 | --- 635 | apiVersion: apps/v1 636 | kind: Deployment 637 | metadata: 638 | labels: 639 | control-plane: audit-controller 640 | gatekeeper.sh/operation: audit 641 | gatekeeper.sh/system: "yes" 642 | name: gatekeeper-audit 643 | namespace: gatekeeper-system 644 | spec: 645 | replicas: 1 646 | selector: 647 | matchLabels: 648 | control-plane: audit-controller 649 | gatekeeper.sh/operation: audit 650 | gatekeeper.sh/system: "yes" 651 | template: 652 | metadata: 653 | annotations: 654 | container.seccomp.security.alpha.kubernetes.io/manager: runtime/default 655 | labels: 656 | control-plane: audit-controller 657 | gatekeeper.sh/operation: audit 658 | gatekeeper.sh/system: "yes" 659 | spec: 660 | automountServiceAccountToken: true 661 | containers: 662 | - args: 663 | - --operation=audit 664 | - --operation=status 665 | - --logtostderr 666 | command: 667 | - /manager 668 | env: 669 | - name: POD_NAMESPACE 670 | valueFrom: 671 | fieldRef: 672 | apiVersion: v1 673 | fieldPath: metadata.namespace 674 | - name: POD_NAME 675 | valueFrom: 676 | fieldRef: 677 | fieldPath: metadata.name 678 | image: openpolicyagent/gatekeeper:v3.3.0 679 | imagePullPolicy: Always 680 | livenessProbe: 681 | httpGet: 682 | path: /healthz 683 | port: 9090 684 | name: manager 685 | ports: 686 | - containerPort: 8888 687 | name: metrics 688 | protocol: TCP 689 | - containerPort: 9090 690 | name: healthz 691 | protocol: TCP 692 | readinessProbe: 693 | httpGet: 694 | path: /readyz 695 | port: 9090 696 | resources: 697 | limits: 698 | cpu: 1000m 699 | memory: 512Mi 700 | requests: 701 | cpu: 100m 702 | memory: 256Mi 703 | securityContext: 704 | allowPrivilegeEscalation: false 705 | capabilities: 706 | drop: 707 | - all 708 | readOnlyRootFilesystem: true 709 | runAsGroup: 999 710 | runAsNonRoot: true 711 | runAsUser: 1000 712 | nodeSelector: 713 | kubernetes.io/os: linux 714 | serviceAccountName: gatekeeper-admin 715 | terminationGracePeriodSeconds: 60 716 | --- 717 | apiVersion: apps/v1 718 | kind: Deployment 719 | metadata: 720 | labels: 721 | control-plane: controller-manager 722 | gatekeeper.sh/operation: webhook 723 | gatekeeper.sh/system: "yes" 724 | name: gatekeeper-controller-manager 725 | namespace: gatekeeper-system 726 | spec: 727 | replicas: 3 728 | selector: 729 | matchLabels: 730 | control-plane: controller-manager 731 | gatekeeper.sh/operation: webhook 732 | gatekeeper.sh/system: "yes" 733 | template: 734 | metadata: 735 | annotations: 736 | container.seccomp.security.alpha.kubernetes.io/manager: runtime/default 737 | labels: 738 | control-plane: controller-manager 739 | gatekeeper.sh/operation: webhook 740 | gatekeeper.sh/system: "yes" 741 | spec: 742 | affinity: 743 | podAntiAffinity: 744 | preferredDuringSchedulingIgnoredDuringExecution: 745 | - podAffinityTerm: 746 | labelSelector: 747 | matchExpressions: 748 | - key: gatekeeper.sh/operation 749 | operator: In 750 | values: 751 | - webhook 752 | topologyKey: kubernetes.io/hostname 753 | weight: 100 754 | automountServiceAccountToken: true 755 | containers: 756 | - args: 757 | - --port=8443 758 | - --logtostderr 759 | - --exempt-namespace=gatekeeper-system 760 | - --operation=webhook 761 | command: 762 | - /manager 763 | env: 764 | - name: POD_NAMESPACE 765 | valueFrom: 766 | fieldRef: 767 | apiVersion: v1 768 | fieldPath: metadata.namespace 769 | - name: POD_NAME 770 | valueFrom: 771 | fieldRef: 772 | fieldPath: metadata.name 773 | image: openpolicyagent/gatekeeper:v3.3.0 774 | imagePullPolicy: Always 775 | livenessProbe: 776 | httpGet: 777 | path: /healthz 778 | port: 9090 779 | name: manager 780 | ports: 781 | - containerPort: 8443 782 | name: webhook-server 783 | protocol: TCP 784 | - containerPort: 8888 785 | name: metrics 786 | protocol: TCP 787 | - containerPort: 9090 788 | name: healthz 789 | protocol: TCP 790 | readinessProbe: 791 | httpGet: 792 | path: /readyz 793 | port: 9090 794 | resources: 795 | limits: 796 | cpu: 1000m 797 | memory: 512Mi 798 | requests: 799 | cpu: 100m 800 | memory: 256Mi 801 | securityContext: 802 | allowPrivilegeEscalation: false 803 | capabilities: 804 | drop: 805 | - all 806 | readOnlyRootFilesystem: true 807 | runAsGroup: 999 808 | runAsNonRoot: true 809 | runAsUser: 1000 810 | volumeMounts: 811 | - mountPath: /certs 812 | name: cert 813 | readOnly: true 814 | nodeSelector: 815 | kubernetes.io/os: linux 816 | serviceAccountName: gatekeeper-admin 817 | terminationGracePeriodSeconds: 60 818 | volumes: 819 | - name: cert 820 | secret: 821 | defaultMode: 420 822 | secretName: gatekeeper-webhook-server-cert 823 | --- 824 | apiVersion: admissionregistration.k8s.io/v1beta1 825 | kind: ValidatingWebhookConfiguration 826 | metadata: 827 | creationTimestamp: null 828 | labels: 829 | gatekeeper.sh/system: "yes" 830 | name: gatekeeper-validating-webhook-configuration 831 | webhooks: 832 | - clientConfig: 833 | caBundle: Cg== 834 | service: 835 | name: gatekeeper-webhook-service 836 | namespace: gatekeeper-system 837 | path: /v1/admit 838 | failurePolicy: Ignore 839 | name: validation.gatekeeper.sh 840 | namespaceSelector: 841 | matchExpressions: 842 | - key: admission.gatekeeper.sh/ignore 843 | operator: DoesNotExist 844 | rules: 845 | - apiGroups: 846 | - '*' 847 | apiVersions: 848 | - '*' 849 | operations: 850 | - CREATE 851 | - UPDATE 852 | resources: 853 | - '*' 854 | sideEffects: None 855 | timeoutSeconds: 3 856 | - clientConfig: 857 | caBundle: Cg== 858 | service: 859 | name: gatekeeper-webhook-service 860 | namespace: gatekeeper-system 861 | path: /v1/admitlabel 862 | failurePolicy: Fail 863 | name: check-ignore-label.gatekeeper.sh 864 | rules: 865 | - apiGroups: 866 | - "" 867 | apiVersions: 868 | - '*' 869 | operations: 870 | - CREATE 871 | - UPDATE 872 | resources: 873 | - namespaces 874 | sideEffects: None 875 | timeoutSeconds: 3 876 | --------------------------------------------------------------------------------