{"id":16928190,"url":"https://github.com/dirien/kubernetes-diy-policy-engine","last_synced_at":"2025-08-31T13:38:26.042Z","repository":{"id":49347274,"uuid":"517014984","full_name":"dirien/kubernetes-diy-policy-engine","owner":"dirien","description":"How to build your own policy engine","archived":false,"fork":false,"pushed_at":"2022-07-24T19:46:25.000Z","size":28,"stargazers_count":14,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-25T03:57:02.251Z","etag":null,"topics":["admission-controller","diy","kubernetes","policy-engine"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dirien.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-07-23T09:33:16.000Z","updated_at":"2024-08-13T03:38:05.000Z","dependencies_parsed_at":"2022-09-23T21:10:47.624Z","dependency_job_id":null,"html_url":"https://github.com/dirien/kubernetes-diy-policy-engine","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dirien/kubernetes-diy-policy-engine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dirien%2Fkubernetes-diy-policy-engine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dirien%2Fkubernetes-diy-policy-engine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dirien%2Fkubernetes-diy-policy-engine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dirien%2Fkubernetes-diy-policy-engine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dirien","download_url":"https://codeload.github.com/dirien/kubernetes-diy-policy-engine/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dirien%2Fkubernetes-diy-policy-engine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272988777,"owners_count":25026959,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-31T02:00:09.071Z","response_time":79,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["admission-controller","diy","kubernetes","policy-engine"],"created_at":"2024-10-13T20:36:06.750Z","updated_at":"2025-08-31T13:38:26.000Z","avatar_url":"https://github.com/dirien.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# How To Build A Kubernetes DIY Policy Engine\n\n## Introduction\n\nIn this blog post we want to use one of the biggest advantages of Kubernetes: The huge ability to be super expandable.\nHow\nto better show this superpower in action then to write a dynamic admission controller?\n\nBut before we start, we need to talk about the concept of admission controllers and how they work\n\n### What is an admission controller?\n\nAn admission controller is a piece of code that intercepts requests to the Kubernetes API server prior to persistence of\nthe object, but after the request is authenticated and authorized. There are two special controllers:\n`MutatingAdmissionWebhook` and `ValidatingAdmissionWebhook`. These execute the mutating and validating (respectively)\nadmission control webhooks which are configured in the API.\n\nAdmission controllers may be \"validating\", \"mutating\", or both. Mutating controllers may modify related objects to the\nrequests they admit; validating controllers may not.\n\nAdmission controllers limit requests to create, delete, modify objects or connect to proxy. They do not limit requests\nto read objects.\n\nThe admission control process proceeds in two phases. In the first phase, mutating admission controllers are run. In the\nsecond phase, validating admission controllers are run.\n\nIf any of the controllers in either phase reject the request, the entire request is rejected immediately and an error is\nreturned to the end-user.\n\n## Prerequisites\n\nIn this blog article, I am going to use following tools:\n\n- minikube\n- golang 1.18\n- make\n- ko\n\n### Minikube\n\n`minikube` quickly sets up a local Kubernetes cluster on macOS, Linux, and Windows. I will use it to deploy my admission\ncontrollers onto it. You could use of course any other cluster provider.\n\nCheck the great documentation and installation guide here: https://minikube.sigs.k8s.io/docs/start/\n\nAs I am using a macOS machine, I will install the `minikube` binary via [Homebrew Package Manager](https://brew.sh/) .\n\n```bash\nbrew install minikube\n```\n\n### Golang 1.18\n\nAs programming language of my choice, I am using [Golang](https://go.dev/) in version 1.18. You can of course use any\nother programming language as you just communicate with the Kubernetes API server. I like to use Golang because it has\nalready a good ready to use Kubernetes libraries. The reason is quite simple: Kubernetes is written in Golang too!\n\n### Make\n\nI use `Make` to perform my build and deployment tasks. This is really down to my own taste. If you build your own\nadmission controllers, you can of course the tool of you choice.\n\n### ko\n\nTo build my container images, I am going to use [ko](https://github.com/google/ko). I love this project because I don't\nneed to write any Dockerfile. With a simple command, it will build a container image for me, create an SBOM and push it\ndirectly to my container registry. And this is much better than writing a Dockerfile.\n\n\u003e ko is working only with golang projects.\n\nAnd that's it from the tools I need to be able to build my admission controllers. Yes of course, you should use in a\nreal production environment also a version control system and some pipelines to build your admission controllers. For\nthe sake of this blog post, I will just make everything manually.\n\nDon't do this at home :D\n\n## Write a validating admission controller\n\nThe validating admission controller, is currently only validating that pods who store their container image in\n`Docker Hub` are scheduled. It's a simple example but a still common use case. You may run your own private container\nregistry and what to be sure that only this registry is allowed in your cluster.\n\n### Coding the validating admission controller\n\n\u003ccode\u003e\n\nIf you need to build an admission controller program down, I would say that It's not much more than a webserver. And this\nis also the first parts of the code we're going to write:\n\n```go\nfunc runValidatingWebhookServer(tlsCert, tlsKey string, port int) error {\n\tlogger.Print(\"Starting DIY validating webhook server\")\n\tcert, err := tls.LoadX509KeyPair(tlsCert, tlsKey)\n\tif err != nil {\n\t\tlogger.Fatal(err)\n\t}\n\n\thttp.HandleFunc(\"/validate\", validate)\n\tserver := http.Server{\n\t\tAddr: fmt.Sprintf(\":%d\", port),\n\t\tTLSConfig: \u0026tls.Config{\n\t\t\tCertificates: []tls.Certificate{cert},\n\t\t},\n\t\tErrorLog: logger,\n\t}\n\n\tif err := server.ListenAndServeTLS(\"\", \"\"); err != nil {\n\t\tlogger.Panic(err)\n\t}\n\treturn nil\n}\n```\n\nWe pass the location of the TLS certificates via the `cobra` library as cli flags to our binary. [Cobra](https://github.com/spf13/cobra)\nis a library for creating powerful modern CLI applications.\n\n```go\n...\nvar rootCmd = \u0026cobra.Command{\n\tUse:   \"validating-webhook\",\n\tShort: \"Kubernetes DIY validating webhook\",\n\tLong: `Kubernetes DIY validating webhook.\nExample:\nvalidating-webhook --port \u003cport\u003e --tls-cert \u003ctls_cert\u003e --tls-key \u003ctls_key\u003e`,\n\tRunE: runValidatingWebhook,\n}\n\n...\nfunc init() {\n\trootCmd.Flags().String(\"tls-cert\", \"\", \"TLS Certificate\")\n\trootCmd.Flags().String(\"tls-key\", \"\", \"Key for TLS Certificate\")\n\trootCmd.Flags().Int(\"port\", 8443, \"Port to listen on\")\n}\n...\n```\n\nIn this function, we are creating a simple tls terminated webserver. We can configure the server to use a certificate and\non which port we want to listen.\n\nThe certificate will be provided automatically via `cert-manager`. We will see later how to deploy and configure the `cert-manager` to \ngenerate self-signed certificates and inject so the Kubernetes API server trust them.\n\nAdditionally, we create a router for the `/validate` endpoint.\n\nThe code for the `/validate` endpoint is pretty straight forward.\n\nWebhooks are sent as POST requests, with `Content-Type: application/json`,  with an `AdmissionReview` API object in the \nadmission.k8s.io API group serialized to JSON as the body.\n\nSee an example [AdmissionReview request](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#request)\n\nIn our code we use the k8s runtime schema and deserializer to map to the JSON to the Golang structs. This will be done in\nmy `admissionReviewFromRequest` function.\n\n\n```go\nfunc validate(w http.ResponseWriter, r *http.Request) {\n\tlog.Printf(\"validate request\")\n\n\t// https://godoc.org/k8s.io/apimachinery/pkg/runtime#Scheme\n\tscheme := runtime.NewScheme()\n\n\t// https://godoc.org/k8s.io/apimachinery/pkg/runtime/serializer#CodecFactory\n\tcodecFactory := serializer.NewCodecFactory(scheme)\n\tdeserializer := codecFactory.UniversalDeserializer()\n\n\tadmissionReviewRequest, err := admissionReviewFromRequest(r, deserializer)\n\tif err != nil {\n\t\twriteErrorResponse(w, errors.New(fmt.Sprintf(\"can't retrieve admission review from request: %v\", err)))\n\t\treturn\n\t}\n\n\tpodResource := metav1.GroupVersionResource{Group: \"\", Version: \"v1\", Resource: \"pods\"}\n\tif admissionReviewRequest.Request.Resource != podResource {\n\t\twriteErrorResponse(w, errors.New(fmt.Sprintf(\"review request is not from kind pod, got %s\", admissionReviewRequest.Request.Resource.Resource)))\n\t\treturn\n\t}\n\n\trawRequest := admissionReviewRequest.Request.Object.Raw\n\tpod := corev1.Pod{}\n\tif _, _, err := deserializer.Decode(rawRequest, nil, \u0026pod); err != nil {\n\t\twriteErrorResponse(w, errors.New(fmt.Sprintf(\"can't decode raw pod definition: %v\", err)))\n\t\treturn\n\t}\n\n\tadmissionResponse := \u0026admissionv1.AdmissionResponse{}\n\tadmissionResponse.Allowed = true\n\n\tfor _, container := range pod.Spec.Containers {\n\t\tif !strings.HasPrefix(container.Image, \"docker.io\") {\n\t\t\tadmissionResponse.Allowed = false\n\t\t\tadmissionResponse.Result = \u0026metav1.Status{\n\t\t\t\tMessage: \"only container from docker.io are allowed\",\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\n\tvar admissionReviewResponse admissionv1.AdmissionReview\n\tadmissionReviewResponse.Response = admissionResponse\n\tadmissionReviewResponse.SetGroupVersionKind(admissionReviewRequest.GroupVersionKind())\n\tadmissionReviewResponse.Response.UID = admissionReviewRequest.Request.UID\n\n\tresp, err := json.Marshal(admissionReviewResponse)\n\tif err != nil {\n\t\twriteErrorResponse(w, errors.New(fmt.Sprintf(\"not possible marshall response: %v\", err)))\n\t\treturn\n\t}\n\n\tw.Header().Set(ContentTypeKey, ContentTypeJSON)\n\tw.Write(resp)\n}\n```\n\nThe real validation logic is in this part of the code:\n\n```go\n...\nadmissionResponse := \u0026admissionv1.AdmissionResponse{}\nadmissionResponse.Allowed = true\n\nfor _, container := range pod.Spec.Containers {\n    if !strings.HasPrefix(container.Image, \"docker.io\") {\n        admissionResponse.Allowed = false\n        admissionResponse.Result = \u0026metav1.Status{\n            Message: \"only container from docker.io are allowed\",\n        }\n        break\n    }\n}\n...\n```\nWe loop through the containers in the pod and check if the image name starts with `docker.io`. If not, we set the\n`AdmissionResponse` allowed to false and write a meaningful message to present to the user.\n\n### Create the Kubernetes manifests\n\nTo deploy our admission controller, we need to create a `Deployment` and a `Service` resource. In the folder \n[deploy](k8s-diy-validating-webhook/deploy) you will find the `Deployment` and `Service` manifests. We will take look at\nthe `Certificate` and `ValidatingWebhookConfiguration` manifest later in this article. Just ignore them for now.\n\nAs you can see, we mount in the Deployment the TLS certificates from a secret and use the flags `--tls-cert` and `--tls-key`\nso our binary knows where to find the certificates.\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  labels:\n    app: k8s-diy-validating-webhook\n  name: k8s-diy-validating-webhook\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: k8s-diy-validating-webhook\n  template:\n    metadata:\n      labels:\n        app: k8s-diy-validating-webhook\n    spec:\n      containers:\n        - image: ghcr.io/dirien/k8s-diy-validating-webhook:latest\n          name: k8s-diy-validating-webhook\n          imagePullPolicy: Always\n          args:\n            - --port=8443\n            - --tls-cert=/etc/webhook/certs/tls.crt\n            - --tls-key=/etc/webhook/certs/tls.key\n          ports:\n            - containerPort: 8443\n              name: webhook\n              protocol: TCP\n          volumeMounts:\n            - mountPath: /etc/webhook/certs\n              name: certs\n      volumes:\n        - name: certs\n          secret:\n            secretName: k8s-diy-validating-webhook-certs\n```\n\nThe `Service` resource is just taking care, that the port `443` is mapped to the port `8443` in the `Deployment`. As \nthe API server is calling the webhook via the default https port.\n\n\u003e I deploy everything into the `default` namespace. Don't do this in production.\n\n\n### Create the TLS certificates via `cert-manager`\n\n\u003cgrafik\u003e\n\nAs we don't want to generate the TLS certificates manually, we will use the `cert-manager` to generate them. [cert-manager](https://cert-manager.io/) \nis an awesome open source project to manage X.509 certificates within your cluster. As it is packed with a lot of features,\nwe will not go into detail here.\n\nThe `ca-root-issuer` is SelfSigned issuers which is useful for bootstrapping a PKI locally. That's absolutely what we want.\n\n```yaml\nkind: ClusterIssuer\napiVersion: cert-manager.io/v1\nmetadata:\n  name: ca-root-issuer\nspec:\n  selfSigned: {}\n```\n\nNow we can create our `CA` certificate via following yaml. Worth to point out is, that we refer to the `ca-root-issuer`\n`ClusterIssuer` in our `spec` section.\n\n```yaml\nkind: Certificate\napiVersion: cert-manager.io/v1\nmetadata:\n  name: ca-root\nspec:\n  secretName: ca-root\n  isCA: true\n  commonName: ca-root\n  privateKey:\n    algorithm: RSA\n    size: 4096\n  issuerRef:\n    kind: ClusterIssuer\n    name: ca-root-issuer\n```\n\nNow that we crated the `CA` certificate, we can create an additional `ClusterIssuer` use with the internal `CA` and issue \ncertificates for the webhooks.\n\n```yaml\nkind: ClusterIssuer\napiVersion: cert-manager.io/v1\nmetadata:\n  name: ca-root\nspec:\n  ca:\n    secretName: ca-root\n```\n\n### Create the ValidatingWebhookConfiguration\n\nNow we can finish the last bits, before we deploy the webhook. As the Kubernetes API server is calling the webhook via\nhttps, we need to create the TLS certificates using our internal `CA` certificate.\n\nFollowing resource will create the certificate using the `ca-root` cluster issuer from above and store the result in the \nKubernetes secret called `k8s-diy-validating-webhook-certs`.\n\n\u003e Important note: The `dnsNames` filed should be set to the service name of the webhook. Reminder: In this article\n\u003e I deployed the webhook into the `default` namespace. Don't do this in production and change the dnsNames to the appropriate\n\u003e values in your cluster.\n\n```yaml\nkind: Certificate\napiVersion: cert-manager.io/v1\nmetadata:\n  name: k8s-diy-validating-webhook\nspec:\n  issuerRef:\n    name: ca-root\n    kind: ClusterIssuer\n  secretName: k8s-diy-validating-webhook-certs\n  duration: 2160h\n  renewBefore: 360h\n  dnsNames:\n    - k8s-diy-validating-webhook.default.svc\n  isCA: false\n  privateKey:\n    algorithm: RSA\n    size: 4096\n  usages:\n    - client auth\n    - server auth\n```\n\nAnd of course, we need to create the `ValidatingWebhookConfiguration` resource. Notable parts of the `ValidatingWebhookConfiguration`\nare the client config, where we point to the service name of our admission controller and the endpoint.\n\nThe next part is the `rules` section. Here we define, on which resource and operation the admission controller should be called.\nHere we check if the resource is a `Pod` and the operation is `CREATE`. As scope, we decided that only namespaced resources will be\nchecked.\n\nThe `cert-manager.io/inject-ca-from: default/k8s-diy-validating-webhook` annotation is used to inject the certificate from\nthe step above.\n\n```yaml\nkind: ValidatingWebhookConfiguration\napiVersion: admissionregistration.k8s.io/v1\nmetadata:\n  name: docker-io-required\n  annotations:\n    cert-manager.io/inject-ca-from: default/k8s-diy-validating-webhook\nwebhooks:\n  - name: docker-io-required.ediri.io\n    clientConfig:\n      service:\n        namespace: default\n        name: k8s-diy-validating-webhook\n        path: /validate\n    rules:\n      - apiGroups:\n          - \"\"\n        apiVersions:\n          - \"v1\"\n        resources:\n          - \"pods\"\n        operations:\n          - \"CREATE\"\n        scope: Namespaced\n    sideEffects: None\n    admissionReviewVersions:\n      - \"v1\"\n```\n\n### Deploy the validating admission controller\n\nNow we can finally deploy the admission controller into our `minikube` cluster. Let's start the cluster with following\ncommand:\n\n```shell\nminikube start\n😄  minikube v1.26.0 on Darwin 10.15.7\n✨  Automatically selected the docker driver. Other choices: hyperkit, virtualbox, ssh, qemu2 (experimental)\n📌  Using Docker Desktop driver with root privileges\n👍  Starting control plane node minikube in cluster minikube\n🚜  Pulling base image ...\n🔥  Creating docker container (CPUs=2, Memory=5391MB) ...\n🐳  Preparing Kubernetes v1.24.1 on Docker 20.10.17 ...\n    ▪ Generating certificates and keys ...\n    ▪ Booting up control plane ...\n    ▪ Configuring RBAC rules ...\n🔎  Verifying Kubernetes components...\n    ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5\n🌟  Enabled addons: storage-provisioner, default-storageclass\n🏄  Done! kubectl is now configured to use \"minikube\" cluster and \"default\" namespace by default\n```\n\nQuickly check that everything is working by running the following command:\n\n```shell\nkubectl get nodes\nNAME       STATUS   ROLES           AGE   VERSION\nminikube   Ready    control-plane   28s   v1.24.1\n```\n\nNow we can navigate into the root folder of your project and call the `all` target in our parent Makefile.\n\n```shell\nmake all\n```\n\nThis will deploy the `cert-manager`, the manifest to create the local `CA`, call `ko`to build our container image\nand then deploy the `k8s-diy-validating-webhook` into the `default` namespace.\n\nAfter everything is deployed we can test that our admission controller is working by creating a `Pod` in the `default` \nwith not setting the registry to `docker.io`.\n\n```shell\nkubectl run nginx --image=nginx \nError from server: admission webhook \"docker-io-required.ediri.io\" denied the request: only container from docker.io are allowed\n```\n\nAnd as expected, the admission controller will deny the request. If we set the registry to `docker.io` then everything \nworks as planned in our admission controller.\n\n```shell\nkubectl run nginx --image=docker.io/nginx           \npod/nginx created\n```\n\nBut what happens, if we want actively change or amend values in our `Pod` resource? For this we can create a mutating\nadmission controller. Why? Because with this we can ensure, that specific best practices in terms of security are enforced.\n\nThis rules can be written by the platform team, who provide the service to the user. It could be the security team, or the SRE\nteam.\n\n## Write a mutating admission controller\n\nMost of the steps form above are similar for the mutating admission controller. I will go only in detail on the parts that \ndiffer from the validating admission controller.\n\n### Coding the mutating admission controller\n\nWe create a new route called `mutate` in our `runMutatingWebhookServer` function. \n\n```go\n...\nhttp.HandleFunc(\"/mutate\", mutate)\n...\n```\n\nWe still need to decode the json request to create our `AdmissionReview` object, so there is no change in this part.\n\nThe actual change, is where we create the mutation of the `Pod` resource.\n\n```go\n...\nvar patch string\npatchType := admissionv1.PatchTypeJSONPatch\n\nfor i := 0; i \u003c len(pod.Spec.Containers); i++ {\n    if pod.Spec.Containers[i].Resources.Limits == nil {\n        patch = fmt.Sprintf(`{\"op\": \"add\", \"path\": \"/spec/containers/%d/resources/limits\", \"value\": {\"cpu\": \"100m\", \"memory\": \"100Mi\"}}, %s`, i, patch)\n        patch = strings.TrimSpace(patch)\n    }\n}\n\nif len(patch) \u003e 0 {\n    patch = strings.TrimRight(patch, \",\")\n    patch = fmt.Sprintf(`[%s]`, patch)\n}\n\nadmissionResponse.Allowed = true\nif patch != \"\" {\n    admissionResponse.PatchType = \u0026patchType\n    admissionResponse.Patch = []byte(patch)\n}\n...\n```\n\nThe code loops through the `Containers` of the `Pod` resource and if no `Limits` are set, we add some default values.\nThe AdmissionResponse is currently only support as patch type `JSONPatch`.\n\nSo I create the `JSONPatch` object with the `add` operation and the path to the `Limits` field.\n\n### Create the MutatingWebhookConfiguration\n\nSimilar to the validating admission controller, we need to create a `MutatingWebhookConfiguration` and the certificate:\n\n```yaml\nkind: Certificate\napiVersion: cert-manager.io/v1\nmetadata:\n  name: k8s-diy-mutating-webhook\nspec:\n  issuerRef:\n    name: ca-root\n    kind: ClusterIssuer\n  secretName: k8s-diy-mutating-webhook-certs\n  duration: 2160h\n  renewBefore: 360h\n  dnsNames:\n    - k8s-diy-mutating-webhook.default.svc\n  isCA: false\n  privateKey:\n    algorithm: RSA\n    size: 4096\n  usages:\n    - client auth\n    - server auth\n```\n\nThe certificate, will be issued from our intenal `CA` that we created and also used in the validating admission controller.\nAgain the certificates will be stored in a `Secret` resource.\n\nThe `MutatingWebhookConfiguration` is nearly similar to the `ValidatingWebhookConfiguration` but just with different values \nreflecting to use the mutating admission controller. The service to use is the mutating webhook service and the `/mutate`\nendpoint.\n\nThe `cert-manager.io/inject-ca-from` annotation is used to inject the `CA` to the kubernetes api server.\n\n```yaml\nkind: MutatingWebhookConfiguration\napiVersion: admissionregistration.k8s.io/v1\nmetadata:\n  name: set-resource-limits\n  annotations:\n    cert-manager.io/inject-ca-from: default/k8s-diy-mutating-webhook\nwebhooks:\n  - name: set-resource-limits.ediri.io\n    clientConfig:\n      service:\n        namespace: default\n        name: k8s-diy-mutating-webhook\n        path: /mutate\n    rules:\n      - apiGroups:\n          - \"\"\n        apiVersions:\n          - \"v1\"\n        resources:\n          - \"pods\"\n        operations:\n          - \"CREATE\"\n        scope: Namespaced\n    sideEffects: None\n    admissionReviewVersions:\n      - \"v1\"\n```\n\n### Deploy the mutating admission controller\n\nSimilar to the validating admission controller, we need to deploy the mutating admission controller. Just navigate to the\nroot folder of your project and call the `all` target in our parent Makefile.\n\n```shell\nmake all\n```\n\nAfter everything is deployed we can test that our admission controller is working by creating a `Pod` in the `default`\nand check if the missing `Limits` are set.\n\n```shell\nkubectl run nginx --image=docker.io/nginx \npod/nginx created\n```\n\n\n```shell\n kubectl get pods nginx -o=jsonpath='{.spec.containers.*.resources}'\n{\"limits\":{\"cpu\":\"100m\",\"memory\":\"100Mi\"},\"requests\":{\"cpu\":\"100m\",\"memory\":\"100Mi\"}}% \n```\n\n## Wrap up - Next steps\n\nCongratulations, you created your first two admission controllers. One for validating and one for mutating. Now you can\nthink about to create additional admission controllers depending on your needs. You could also merge both admission controllers\ninto one to avoid duplicate code and just add additional rules to reflect your needs.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdirien%2Fkubernetes-diy-policy-engine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdirien%2Fkubernetes-diy-policy-engine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdirien%2Fkubernetes-diy-policy-engine/lists"}