This document describes steps necessary to install the CEL Admission Webhook Shim on your local cluister
🚧 WORK IN PROGRESS 🚧
This method uses a local kind
cluster and is recommended only for testing/development.
Run the following script to build the image from source, and generate a minimum working webhook configuration:
cd $REPO_PATH
# Build container image to run the controller
docker build -t kubernetes-x/cel-shim-webhook:v1 ./
# Make container image available to Pods in the Kind cluster
# NOTE: If your kind cluster is named, this will also need the --name argument with the name of your cluster
kind load docker-image kubernetes-x/cel-shim-webhook:v1
# Generate deployment, serviceaccount, certificates, etc into ./_output/manifests
./hack/gen-manifests.sh
# Apply required manifests for webhook
kubectl apply -f ./_output/manifests --server-side=true
This script creates a self-signed CA and a server certificate, and outputs manifests containing webhook configurations and TLS secrets.
After running the final kubectl apply
the CEL Admission Webhook should
be active on your kind cluster and ready to enforce policies.
🚧 WORK IN PROGRESS 🚧
Requirements:
- Kubernetes 1.16+
- kubectl configured to cluster
The cel-admission-webhook
requires CRDs to properly function. Lets begin by making sure those CRDs are available in our cluster.
kubectl apply -f <https://github.com/URL/to/unified/crds/yaml/in/repo> --server-side=true
customresourcedefinition.apiextensions.k8s.io/validatingadmissionpolicies.admissionregistration.x-k8s.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/validatingadmissionpolicybindings.admissionregistration.x-k8s.io serverside-applied
kubectl apply -f ./artifacts/crds --server-side=true
customresourcedefinition.apiextensions.k8s.io/validatingadmissionpolicies.admissionregistration.x-k8s.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/validatingadmissionpolicybindings.admissionregistration.x-k8s.io serverside-applied
For organizational purposes, we will apply all objects to the celshim
namespace. Feel free to change this to suit your needs.
kubectl create namespace celshim
namespace/celshim created
To authenticate the webhook to the cluster, we require a TLS server certificate and CA trusted by the cluster.
There are a number of ways to generate certificates, or sign a certificate with a pre-existing CA. This guide includes a way to create a self-signed cert specific to the webhook.
If you have your own PKI, refer to your own processes for generating a certificate for the webhook. The only requirement is that the SANs in the certificate match the URL used to reach the webhook service. For our service named cel-shim-webhook
in celshim
namespace that will be cel-shim-webhook.celshim.svc
You can quickly get a self-signed CA and server certificate through
the bundled gentls
command:
go run k8s.io/cel-admission-webhook/cmd/gentls --host=cel-shim-webhook.celshim.svc
Change the host argument if your webhook service will be named differently.
The output of this script are three files:
ca.pem
: Self-Signed Certificate Authority. Set this aside for later.server.pem
: Server Certificate signed byca.pem
. This should be placed in a TLS Secret.server-key.pem
. Private key forserver.pem
. This should be placed in a TLS Secret.
Run the following command after you have a key and a certificate for the webhook server. This will create a secret in the celshim
namespace
to be injected into the shim deployment.
kubectl create secret tls cel-shim-webhook --cert=server.pem --key=server-key.pem --namespace celshim
secret/cel-shim-webhook created
ServiceAccount and RBAC configuration is often very cluster specific. Below is an example of a ServiceAccount, ClusterRole, and ClusterRoleBinding that can be used with the cel shim webhook.
If you already have a service account for the webhook to use, feel free to skip this step and use substitute it into the Deployment configuration.
kubectl create serviceaccount cel-webhook -n celshim
serviceaccount/cel-webhook created
Equivalent YAML
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: cel-webhook
namespace: celshim
kubectl create clusterrole cel-webhook --verb="*" --resource="*.*"
clusterrole.rbac.authorization.k8s.io/cel-webhook created
Equivalent YAML
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cel-webhook
rules:
- verbs: ["*"]
apiGroups: ["*"]
resources: ['*']
kubectl create clusterrolebinding cel-webhook --clusterrole=cel-webhook --serviceaccount=celshim:cel-webhook
clusterrolebinding.rbac.authorization.k8s.io/cel-webhook created
Equivalent YAML
---
kind: ClusterRoleBinding
metadata:
name: cel-webhook
namespace: celshim
apiVersion: rbac.authorization.k8s.io/v1
subjects:
- kind: ServiceAccount
name: cel-webhook
namespace: celshim
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cel-webhook
namespace: celshim
This project does not yet have any releases in public registry, but that is the long term plan.
Alternatively, you may also choose to build the container image from source yourself:
git clone git@github.com:kubernetes/cel-admission-webhook.git
cd $REPO_PATH
docker build -t kubernetes-x/cel-shim-webhook:v1 ./
To make the container image available to run in Kubernetes, it must be published to a container registry trusted and accessible by your cluster.
Images can be loaded into a kind cluster with a single command.
kind load docker-image kubernetes-x/cel-shim-webhook:v1
NOTE:
kind
assumes your cluster is namedkind
by default. If your cluster is named differently, you must also supply the--name
argument.
TODO
TODO
Apply the following deployment configuration.
kubectl apply --server-side=true -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: cel-shim-webhook
namespace: celshim
labels:
app: cel-shim-webhook
spec:
selector:
matchLabels:
app: cel-shim-webhook
template:
metadata:
labels:
app: cel-shim-webhook
spec:
serviceAccountName: cel-webhook
containers:
- name: cel-shim-webhook
image: kubernetes-x/cel-shim-webhook:v1
args:
- -cert=/etc/tls/tls.crt
- -key=/etc/tls/tls.key
- -addr=:443
volumeMounts:
- mountPath: "/etc/tls"
name: tls
readOnly: true
volumes:
- name: tls
secret:
# kubectl create secret tls cel-shim-webhook --cert=server.pem --key=server-key.pem
secretName: cel-shim-webhook
EOF
deployment.apps/cel-shim-webhook serverside-applied
NOTE: If you named your service account or TLS secret differently, this configuration must be edited to reflect that.
Now that the controller is standing up, expose the deployment to the apiserver with a service:
kubectl apply --server-side=true -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: cel-shim-webhook
namespace: celshim
labels:
app: cel-shim-webhook
spec:
selector:
app: cel-shim-webhook
ports:
- port: 443
protocol: TCP
EOF
service/cel-shim-webhook serverside-applied
This service matches all webhook Pods with label app: cel-shim-webhook
.
Requests to this service on port 443
will be directed to a running webhook container.
With deployment running and service properly pointing to it, it is time to do the final step of configuring kubernetes to use the webhook. ValidatingWebhookConfiguration informs Kubernetes that you'd like to use a webhook, on which resources, and how to connect to the webhook.
Find the CA pem file used to sign the wbehook server certificate. Encode it into base64 with the following command:
export CA_BUNDLE=$(cat ca.pem | base64 -w0) && echo $CA_BUNDLE
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJMVENCNEtBREFnRUNBaFIzV3huK3JScmdueC9BeWNOK1hraURsWDBKRkRBRkJnTXJaWEF3RlRFVE1CRUcKQTFVRUF4TUtVMlZzWmxOcFoyNWxaREFlRncweU16QTFNREV4T1RReE5UZGFGdzB5TkRBME16QXhPVFEyTlRkYQpNQlV4RXpBUkJnTlZCQU1UQ2xObGJHWlRhV2R1WldRd0tqQUZCZ01yWlhBRElRQkljS2RCTktJYm52ZThQRWw2CnpvRm5Ud0wveWc0WjlwdTZjZlR2dGFUdzBhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnS2tNQThHQTFVZEV3RUIKL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkVMcEh2ZXFIbkJDVWxKSDR1WFk3RG5lZXZrR01BVUdBeXRsY0FOQgpBSjVNOVd3L1BEcW40SXVQVjViK05NR05ocXZlK2tyNUYzV0s2RTZZSE80WXRwTFhlL2dTOUlNUlZnTW14Sm5LCks2U3IzTGVMQmxXVzFFN29kbmY4QXd3PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
kubectl apply --server-side=true -f -<<EOF
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: "cel-shim.example.com"
namespace: "celshim"
webhooks:
- name: "cel-shim.example.com"
rules:
- apiGroups: ["*"]
apiVersions: ["*"]
operations: ["*"]
resources: ["*"]
scope: "*"
clientConfig:
service:
namespace: celshim
name: cel-shim-webhook
path: /validate
port: 443
caBundle: |
$CA_BUNDLE
admissionReviewVersions: ["v1"]
sideEffects: None
timeoutSeconds: 2
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: NotIn
values: ["kube-system","kube-node-lease","kube-public","celshim"]
objectSelector:
matchExpressions:
- key: app
operator: NotIn
values: ["cel-shim-webhook"]
EOF
validatingwebhookconfiguration.admissionregistration.k8s.io/cel-shim.example.com serverside-applied
NOTE: We use $CA_BUNDLE environment variable in the deployment. This inserts the base64-encoded CA certificate into the resource to be created.
This configuration ignores anything in celshim
namespace and
some common internal Kubernetes objects. If you use a different namespace for
your deployment, you should also add it to the ignore list.
Create an admission policy to match all operations on ConfigMaps whose names
do not end with k8s
:
kubectl apply --server-side=true -f - <<EOF
apiVersion: admissionregistration.x-k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: k8s-policy
spec:
matchConstraints:
resourceRules:
- resourceNames: [ ]
operations: [ "*" ]
apiGroups: [ "" ]
apiVersions: [ "v1" ]
resources: [ "configmaps" ]
paramKind:
apiVersion: v1
kind: ConfigMap
failurePolicy: Fail
validations:
- expression: object.metadata.name.endsWith('k8s')
EOF
Create a binding to begin enforcing the policy:
kubectl apply --server-side -f - <<EOF
apiVersion: admissionregistration.x-k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: k8s-policy-binding
spec:
policyName: k8s-policy
validationActions:
- Deny
EOF
kubectl apply --server-side=true -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: my-config
data:
key: value
EOF
The request is invalid: admission webhook "cel-shim.example.com" denied the request: configmaps "my-config" is forbidden: ValidatingAdmissionPolicy 'k8s-policy' with binding 'k8s-policy-binding' denied request: failed expression: object.metadata.name.endsWith('k8s')
kubectl apply --server-side=true -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: my-config-k8s
data:
key: value
EOF
configmap/my-config-k8s serverside-applied