|
| 1 | +--- |
| 2 | +title: "Kubernetes Webhooks made easy with OpenFaaS - Part 2" |
| 3 | +description: "In this post you'll learn how to write Kubernetes Admission webhooks using OpenFaaS functions - Part 2" |
| 4 | +date: 2020-11-2 |
| 5 | +image: /images/2020-10-27-k8s-validatingwebhook-openfaas/puzzle.jpg |
| 6 | +categories: |
| 7 | + - arkade |
| 8 | + - kubectl |
| 9 | + - faas-cli |
| 10 | + - admissionwebhooks |
| 11 | + - mutatingadmissionwebhooks |
| 12 | + - k8s extensibility |
| 13 | +author_staff_member: developer-guy |
| 14 | +dark_background: true |
| 15 | +--- |
| 16 | + |
| 17 | +In this post you'll learn how to write Kubernetes Admission webhooks using OpenFaaS functions - Part 2 |
| 18 | + |
| 19 | +<p align="center"> |
| 20 | +<img height="128" src="/images/openfaas/kubernetes.png"> |
| 21 | +</p> |
| 22 | + |
| 23 | +## Introduction to Kubernetes Mutating Admission webhooks |
| 24 | +In the previous [part](https://www.openfaas.com/blog/kubernetes-webhooks-made-easy-with-openfaas/), we talked about what Kubernetes Admission Webhooks are and how can we use Validating Admission Webhook. |
| 25 | + |
| 26 | +Today we are gonna talk about Mutating Admission Webhook and demonstrate how can we use it as OpenFaaS Function. |
| 27 | + |
| 28 | +The main difference between the two types of admission webhook are pretty self-explanatory: validating webhooks can reject a request, but they cannot modify the object they are receiving in the admission request, while mutating webhooks can modify objects by creating a patch that will be sent back in the admission response and can reject a request too. If a webhook rejects a request, an error is returned to the end-user. |
| 29 | + |
| 30 | +Also, the other difference between two types are validating webhooks are called in parallel and mutating webhooks are called in-sequence by the api-server. |
| 31 | + |
| 32 | +## The Scenario |
| 33 | +This time we will inject a file to the container filesystem using [configMap volume](https://kubernetes.io/docs/concepts/storage/volumes/#configmap) automatically by the Mutating Admission Webhook. |
| 34 | + |
| 35 | +In order to do that, we need to create a file that will inject by the Mutating Admission Webhook to the container's filesystem like below: |
| 36 | + |
| 37 | +```sh |
| 38 | +$ cat functions/fileinjector/hello-openfaas.txt |
| 39 | + _ _ _ _ ___ ___ ___ |
| 40 | + | || |___| | |___ / _ \ _ __ ___ _ _ | __|_ _ __ _/ __| |
| 41 | + | __ / -_) | / _ \ | (_) | '_ \/ -_) ' \| _/ _` / _` \__ \ |
| 42 | + |_||_\___|_|_\___/ \___/| .__/\___|_||_|_|\__,_\__,_|___/ |
| 43 | + |_| |
| 44 | +``` |
| 45 | + |
| 46 | +There is one important thing we need to say about Mutating Admission webhook is that: |
| 47 | + |
| 48 | +> In a mutating admission controller webhook, mutations are performed via JSON patches. While the JSON patch standard includes a lot of intricacies that go well beyond the scope of this discussion, the Go data structure in our example as well as its usage should give the user a good initial overview of how JSON patches work: |
| 49 | +
|
| 50 | +```golang |
| 51 | +type patchOperation struct { |
| 52 | + Op string `json:"op"` |
| 53 | + Path string `json:"path"` |
| 54 | + Value interface{} `json:"value,omitempty"` |
| 55 | +} |
| 56 | +``` |
| 57 | + |
| 58 | +For setting the field .spec.securityContext.runAsNonRoot of a pod to true, we construct the following patchOperation object: |
| 59 | + |
| 60 | +```golang |
| 61 | +patches = append(patches, patchOperation{ |
| 62 | + Op: "add", |
| 63 | + Path: "/spec/securityContext/runAsNonRoot", |
| 64 | + Value: true, |
| 65 | +}) |
| 66 | +``` |
| 67 | + |
| 68 | +Reminder, we will use the same workflow defined in previous post like below: |
| 69 | + |
| 70 | +* Kubernetes API -> Webhook (w/TLS) -> OpenFaaS Gateway (w/HTTP) -> OpenFaaS Function |
| 71 | + |
| 72 | + |
| 73 | +> Credit: [https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers/](https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers/) |
| 74 | +
|
| 75 | +### Prerequisites |
| 76 | +##### Arkade |
| 77 | + |
| 78 | +* [arkade](https://get-arkade.dev) is The OpenFaaS community built tool for Kubernetes developers, with arkade you can easily install all necessary cli tools to your host and deploy apps to the cluster. |
| 79 | + |
| 80 | +```sh |
| 81 | +$ curl -sLS https://dl.get-arkade.dev | sudo sh |
| 82 | +``` |
| 83 | + |
| 84 | +##### KinD (Kubernetes in Docker) |
| 85 | + |
| 86 | +* Kubernetes is our recommendation for teams running at scale, but in this demo we will be using [KinD](https://kind.sigs.k8s.io/docs/user/quick-start/) for the sake of simplicity. |
| 87 | + |
| 88 | +```sh |
| 89 | +$ arkade get kind |
| 90 | +``` |
| 91 | + |
| 92 | +##### kubectl |
| 93 | + |
| 94 | +* You can control your cluster using [kubectl](https://github.com/kubernetes/kubectl) CLI. |
| 95 | + |
| 96 | +```sh |
| 97 | +$ arkade get kubectl |
| 98 | +``` |
| 99 | + |
| 100 | +##### faas-cli |
| 101 | + |
| 102 | +* [faas-cli](https://github.com/openfaas/faas-cli) is an official CLI for OpenFaaS , with "faas-cli" you can build and deploy functions easily. |
| 103 | + |
| 104 | +```sh |
| 105 | +$ arkade get faas-cli |
| 106 | +``` |
| 107 | + |
| 108 | +### Setup |
| 109 | + |
| 110 | +### 1. Setup a Kubernetes Cluster with KinD |
| 111 | + |
| 112 | +You can start a Kubernetes cluster with KinD if you don't have one already |
| 113 | + |
| 114 | +```bash |
| 115 | +$ arkade get kind |
| 116 | +$ kind create cluster |
| 117 | +``` |
| 118 | + |
| 119 | +### 2. Deploy OpenFaaS to our local Kubernetes Cluster with arkade: |
| 120 | + |
| 121 | +* Install a OpenFaaS |
| 122 | + |
| 123 | +```sh |
| 124 | +$ arkade install openfaas |
| 125 | +``` |
| 126 | + |
| 127 | +Read the output from the installation and run the commands given to you. |
| 128 | + |
| 129 | +You can access them again at any time with `arkade info openfaas` |
| 130 | + |
| 131 | +### 3. Clone the project |
| 132 | + |
| 133 | +* Clone the sample from GitHub |
| 134 | + |
| 135 | +```sh |
| 136 | +$ git clone https://github.com/developer-guy/admission-webhook-example-with-openfaas |
| 137 | +$ cd admission-webhook-example-with-openfaas |
| 138 | +$ git checkout feature/mutating |
| 139 | +``` |
| 140 | + |
| 141 | +* Let's explore the structure of the project. |
| 142 | + |
| 143 | +``` |
| 144 | +deployment/ --> includes necessary manifests and scripts for the deployment of the project |
| 145 | +functions/ --> includes templates and the requiredlabel function itself |
| 146 | +Dockerfile --> includes instructions to build an image of the project |
| 147 | +build --> automated way to build and push an image of the project |
| 148 | +``` |
| 149 | + |
| 150 | +### 4. Deploy MutatingAdmissionWebhook |
| 151 | + |
| 152 | +* We will generate the TLS certificates required for the ValidatingAdmissionWebhook using the following: [Kubernetes TLS Certificates Management](https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster/) |
| 153 | + |
| 154 | +```sh |
| 155 | +$ cd deployment |
| 156 | +$ sh webhook-create-signed-cert.sh |
| 157 | +``` |
| 158 | + |
| 159 | +* Get the Certificate Authority (CA) from the local cluster |
| 160 | + |
| 161 | +```sh |
| 162 | +$ export CA_BUNDLE=$(kubectl config view --minify --flatten -o json | jq -r '.clusters[] | select(.name == "'$(kubectl config current-context)'") | .cluster."certificate-authority-data"') |
| 163 | +$ sed -e "s|\${CA_BUNDLE}|${CA_BUNDLE}|g" mutatingwebhook.yaml | kubectl apply -f - |
| 164 | +$ cd .. |
| 165 | +``` |
| 166 | + |
| 167 | +* Build the project |
| 168 | + |
| 169 | +```sh |
| 170 | +$ export DOCKER_USER="docker-hub-username" |
| 171 | +$ ./build |
| 172 | +``` |
| 173 | + |
| 174 | +Now edit `deployment.yaml` and set 'DOCKER_USER' to the above. |
| 175 | + |
| 176 | +* Deploy it project |
| 177 | + |
| 178 | +```sh |
| 179 | +$ cd deployment |
| 180 | +$ kubectl apply -f rbac.yaml,service.yaml,deployment.yaml |
| 181 | +# Label the default namespace to enable the webhook |
| 182 | +$ kubectl label namespaces default admission-webhook-example=enabled |
| 183 | +``` |
| 184 | + |
| 185 | +* This time we are using "fileinjector" as a FUNCTION_NAME environment variable in the deployment.yaml file |
| 186 | + |
| 187 | +```yaml |
| 188 | +env: |
| 189 | + - name: FUNCTION_NAME |
| 190 | + value: fileinjector |
| 191 | +``` |
| 192 | +
|
| 193 | +### 5. Build and Deploy OpenFaaS Function (Optional) |
| 194 | +
|
| 195 | +* Pull the [golang-middleware](https://github.com/openfaas-incubator/golang-http-template) template from [OpenFaaS Official Template Store](https://github.com/openfaas/store) |
| 196 | +
|
| 197 | +```sh |
| 198 | +$ faas-cli template store list # check available templates in store |
| 199 | +$ faas-cli template store describe golang-middleware # describe the specific template |
| 200 | +$ faas-cli template store pull golang-middleware |
| 201 | +``` |
| 202 | + |
| 203 | +* Create the function |
| 204 | + |
| 205 | +```sh |
| 206 | +$ export OPENFAAS_PREFIX=$DOCKER_USER |
| 207 | +$ faas-cli new fileinjector --lang go-middleware |
| 208 | +$ cd fileinjector |
| 209 | +$ go mod init fileinjector |
| 210 | +$ # fill the handler.go with the corresponding code: [functions/fileinjector/handler.go](https://github.com/developer-guy/admission-webhook-example-with-openfaas/blob/feature/mutating/functions/fileinjector/handler.go) |
| 211 | +$ go get |
| 212 | +``` |
| 213 | + |
| 214 | +* Deploy the function |
| 215 | + |
| 216 | +```sh |
| 217 | +$ cd functions |
| 218 | +$ faas-cli up -f fileinjector.yml --build-arg GO111MODULE=on # (build-push-deploy) make sure you are using your docker hub username. i.e: devopps |
| 219 | +``` |
| 220 | + |
| 221 | +* Verify the functions that are working in `openfaas-fn` namespace |
| 222 | + |
| 223 | +```sh |
| 224 | +$ kubectl get pods --namespace openfaas-fn |
| 225 | +``` |
| 226 | + |
| 227 | +### 6. Testing the whole workflow |
| 228 | +* The purpose of this PoC is that to add a simple volume which is type configMap and volumeMount which is mounted to that volume to the pod automatically by our Mutating Admission Webhook. |
| 229 | + |
| 230 | +* First, we need to create a ConfigMap to be able to mount a file to the Pod: |
| 231 | + |
| 232 | +```sh |
| 233 | +$ cat functions/fileinjector/hello-openfaas.txt |
| 234 | + |
| 235 | + _ _ _ _ ___ ___ ___ |
| 236 | + | || |___| | |___ / _ \ _ __ ___ _ _ | __|_ _ __ _/ __| |
| 237 | + | __ / -_) | / _ \ | (_) | '_ \/ -_) ' \| _/ _` / _` \__ \ |
| 238 | + |_||_\___|_|_\___/ \___/| .__/\___|_||_|_|\__,_\__,_|___/ |
| 239 | + |_| |
| 240 | +``` |
| 241 | + |
| 242 | +``` |
| 243 | +$ kubectl create hello-configmap --from-file=functions/fileinjector/hello-openfaas.txt |
| 244 | +``` |
| 245 | + |
| 246 | +* Now , you can apply the Pod manifest, once you done, you will notice that Pod includes a volume + volumeMount additional to the manifest you wrote in the beginning |
| 247 | + |
| 248 | +```sh |
| 249 | +$ kubectl apply -f busybox.yaml |
| 250 | +``` |
| 251 | + |
| 252 | +* Check the logs of the Pod, you will see "Hello OpenFaaS" text as the output of the container. |
| 253 | + |
| 254 | +```sh |
| 255 | +$ kubectl logs -f busybox |
| 256 | + |
| 257 | + _ _ _ _ ___ ___ ___ |
| 258 | + | || |___| | |___ / _ \ _ __ ___ _ _ | __|_ _ __ _/ __| |
| 259 | + | __ / -_) | / _ \ | (_) | '_ \/ -_) ' \| _/ _` / _` \__ \ |
| 260 | + |_||_\___|_|_\___/ \___/| .__/\___|_||_|_|\__,_\__,_|___/ |
| 261 | + |_| |
| 262 | +``` |
| 263 | + |
| 264 | +### Join the community |
| 265 | + |
| 266 | +Have you got questions, comments, or suggestions? Join the community on [Slack](https://slack.openfaas.io). |
| 267 | + |
| 268 | +Would you like help to set up your OpenFaaS installation, or someone to call when things don't quite go to plan? [Our Premium Subscription plan](https://www.openfaas.com/support/) gives you a say in the project roadmap, a support contact, and access to Enterprise-grade authentication with OIDC. |
| 269 | + |
| 270 | +### Acknowledgements |
| 271 | + |
| 272 | +* Special Thanks to [Alex Ellis](https://twitter.com/alexellisuk) for all guidance and for merging changes into OpenFaaS to better support this workflow. |
| 273 | +* Special Thanks to [Furkan Türkal](https://twitter.com/furkanturkaI) for all the support. |
| 274 | + |
| 275 | +### References |
| 276 | +* [https://banzaicloud.com/blog/k8s-admission-webhooks](https://banzaicloud.com/blog/k8s-admission-webhooks) |
| 277 | +* [https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers/](https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers/) |
| 278 | +* [https://medium.com/ibm-cloud/diving-into-kubernetes-mutatingadmissionwebhook-6ef3c5695f74](https://medium.com/ibm-cloud/diving-into-kubernetes-mutatingadmissionwebhook-6ef3c5695f74) |
| 279 | +* [https://blog.alexellis.io/get-started-with-openfaas-and-kind/](https://blog.alexellis.io/get-started-with-openfaas-and-kind/) |
| 280 | +* [https://github.com/morvencao/kube-mutating-webhook-tutorial](https://github.com/morvencao/kube-mutating-webhook-tutorial) |
| 281 | +* [https://github.com/developer-guy/admission-webhook-example-with-openfaas](https://github.com/developer-guy/admission-webhook-example-with-openfaas) |
0 commit comments