├── .gitignore ├── image ├── .gitignore └── Dockerfile ├── go.mod ├── examples ├── pod-with-defaults.yaml ├── pod-with-override.yaml └── pod-with-conflict.yaml ├── Makefile ├── deployment ├── deployment.yaml.template └── generate-keys.sh ├── deploy.sh ├── README.md ├── cmd └── webhook-server │ ├── main.go │ └── admission_controller.go ├── LICENSE └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | -------------------------------------------------------------------------------- /image/.gitignore: -------------------------------------------------------------------------------- 1 | /webhook-server 2 | -------------------------------------------------------------------------------- /image/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | 3 | COPY ./webhook-server / 4 | ENTRYPOINT ["/webhook-server"] 5 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/stackrox/admission-controller-webhook-demo 2 | 3 | go 1.16 4 | 5 | require ( 6 | k8s.io/api v0.22.0 7 | k8s.io/apimachinery v0.22.0 8 | ) 9 | -------------------------------------------------------------------------------- /examples/pod-with-defaults.yaml: -------------------------------------------------------------------------------- 1 | # A pod with no securityContext specified. 2 | # Without the webhook, it would run as user root (0). The webhook mutates it 3 | # to run as the non-root user with uid 1234. 4 | apiVersion: v1 5 | kind: Pod 6 | metadata: 7 | name: pod-with-defaults 8 | labels: 9 | app: pod-with-defaults 10 | spec: 11 | restartPolicy: OnFailure 12 | containers: 13 | - name: busybox 14 | image: busybox 15 | command: ["sh", "-c", "echo I am running as user $(id -u)"] 16 | -------------------------------------------------------------------------------- /examples/pod-with-override.yaml: -------------------------------------------------------------------------------- 1 | # A pod with a securityContext explicitly allowing it to run as root. 2 | # The effect of deploying this with and without the webhook is the same. The 3 | # explicit setting however prevents the webhook from applying more secure 4 | # defaults. 5 | apiVersion: v1 6 | kind: Pod 7 | metadata: 8 | name: pod-with-override 9 | labels: 10 | app: pod-with-override 11 | spec: 12 | restartPolicy: OnFailure 13 | securityContext: 14 | runAsNonRoot: false 15 | containers: 16 | - name: busybox 17 | image: busybox 18 | command: ["sh", "-c", "echo I am running as user $(id -u)"] 19 | -------------------------------------------------------------------------------- /examples/pod-with-conflict.yaml: -------------------------------------------------------------------------------- 1 | # A pod with a conflicting securityContext setting: it has to run as a non-root 2 | # user, but we explicitly request a user id of 0 (root). 3 | # Without the webhook, the pod could be created, but would be unable to launch 4 | # due to an unenforceable security context leading to it being stuck in a 5 | # `CreateContainerConfigError` status. With the webhook, the creation of 6 | # the pod is outright rejected. 7 | apiVersion: v1 8 | kind: Pod 9 | metadata: 10 | name: pod-with-conflict 11 | labels: 12 | app: pod-with-conflict 13 | spec: 14 | restartPolicy: OnFailure 15 | securityContext: 16 | runAsNonRoot: true 17 | runAsUser: 0 18 | containers: 19 | - name: busybox 20 | image: busybox 21 | command: ["sh", "-c", "echo I am running as user $(id -u)"] 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019 StackRox Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Makefile for building the Admission Controller webhook demo server + docker image. 16 | 17 | .DEFAULT_GOAL := docker-image 18 | 19 | IMAGE ?= stackrox/admission-controller-webhook-demo:latest 20 | 21 | image/webhook-server: $(shell find . -name '*.go') 22 | CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o $@ ./cmd/webhook-server 23 | 24 | .PHONY: docker-image 25 | docker-image: image/webhook-server 26 | docker build -t $(IMAGE) image/ 27 | 28 | .PHONY: push-image 29 | push-image: docker-image 30 | docker push $(IMAGE) 31 | -------------------------------------------------------------------------------- /deployment/deployment.yaml.template: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: webhook-server 5 | namespace: webhook-demo 6 | labels: 7 | app: webhook-server 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: webhook-server 13 | template: 14 | metadata: 15 | labels: 16 | app: webhook-server 17 | spec: 18 | securityContext: 19 | runAsNonRoot: true 20 | runAsUser: 1234 21 | containers: 22 | - name: server 23 | image: stackrox/admission-controller-webhook-demo:latest 24 | imagePullPolicy: Always 25 | ports: 26 | - containerPort: 8443 27 | name: webhook-api 28 | volumeMounts: 29 | - name: webhook-tls-certs 30 | mountPath: /run/secrets/tls 31 | readOnly: true 32 | volumes: 33 | - name: webhook-tls-certs 34 | secret: 35 | secretName: webhook-server-tls 36 | --- 37 | apiVersion: v1 38 | kind: Service 39 | metadata: 40 | name: webhook-server 41 | namespace: webhook-demo 42 | spec: 43 | selector: 44 | app: webhook-server 45 | ports: 46 | - port: 443 47 | targetPort: webhook-api 48 | --- 49 | apiVersion: admissionregistration.k8s.io/v1beta1 50 | kind: MutatingWebhookConfiguration 51 | metadata: 52 | name: demo-webhook 53 | webhooks: 54 | - name: webhook-server.webhook-demo.svc 55 | sideEffects: None 56 | admissionReviewVersions: ["v1", "v1beta1"] 57 | clientConfig: 58 | service: 59 | name: webhook-server 60 | namespace: webhook-demo 61 | path: "/mutate" 62 | caBundle: ${CA_PEM_B64} 63 | rules: 64 | - operations: [ "CREATE" ] 65 | apiGroups: [""] 66 | apiVersions: ["v1"] 67 | resources: ["pods"] 68 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright (c) 2019 StackRox Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # deploy.sh 18 | # 19 | # Sets up the environment for the admission controller webhook demo in the active cluster. 20 | 21 | set -euo pipefail 22 | 23 | basedir="$(dirname "$0")/deployment" 24 | keydir="$(mktemp -d)" 25 | 26 | # Generate keys into a temporary directory. 27 | echo "Generating TLS keys ..." 28 | "${basedir}/generate-keys.sh" "$keydir" 29 | 30 | # Create the `webhook-demo` namespace. This cannot be part of the YAML file as we first need to create the TLS secret, 31 | # which would fail otherwise. 32 | echo "Creating Kubernetes objects ..." 33 | kubectl create namespace webhook-demo 34 | 35 | # Create the TLS secret for the generated keys. 36 | kubectl -n webhook-demo create secret tls webhook-server-tls \ 37 | --cert "${keydir}/webhook-server-tls.crt" \ 38 | --key "${keydir}/webhook-server-tls.key" 39 | 40 | # Read the PEM-encoded CA certificate, base64 encode it, and replace the `${CA_PEM_B64}` placeholder in the YAML 41 | # template with it. Then, create the Kubernetes resources. 42 | ca_pem_b64="$(openssl base64 -A <"${keydir}/ca.crt")" 43 | sed -e 's@${CA_PEM_B64}@'"$ca_pem_b64"'@g' <"${basedir}/deployment.yaml.template" \ 44 | | kubectl create -f - 45 | 46 | # Delete the key directory to prevent abuse (DO NOT USE THESE KEYS ANYWHERE ELSE). 47 | rm -rf "$keydir" 48 | 49 | echo "The webhook server has been deployed and configured!" 50 | -------------------------------------------------------------------------------- /deployment/generate-keys.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright (c) 2019 StackRox Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # generate-keys.sh 18 | # 19 | # Generate a (self-signed) CA certificate and a certificate and private key to be used by the webhook demo server. 20 | # The certificate will be issued for the Common Name (CN) of `webhook-server.webhook-demo.svc`, which is the 21 | # cluster-internal DNS name for the service. 22 | # 23 | # NOTE: THIS SCRIPT EXISTS FOR DEMO PURPOSES ONLY. DO NOT USE IT FOR YOUR PRODUCTION WORKLOADS. 24 | 25 | : ${1?'missing key directory'} 26 | 27 | key_dir="$1" 28 | 29 | chmod 0700 "$key_dir" 30 | cd "$key_dir" 31 | 32 | cat >server.conf <