
We will implement a simplevalidation webhook to achieve the following:
- Input conditions:
- Applicable to all namespaces where label:
ngaddons/validation-webhooks
isenabled
- Applicable to object-types:
Deployments
andPods
Not applicable if label:
ngaddons/bypass
is set on the objectValidation logic
Fail the object creation unless all of these labels are set:
'ngaddons/ownerId', 'ngaddons/webexRoomId', 'ngaddons/appName'
Bypass the checks if
ngaddons/bypass
is set
Webhook code
importloggingimportosfromflaskimportFlask,jsonify,requestapp=Flask('webhook')app.logger.addHandler(logging.StreamHandler())app.logger.setLevel(logging.DEBUG)#Health check@app.route("/healthz",methods=['GET'])defping():returnjsonify({'message':'ok'})REQUIRED_LABELS=['ngaddons/ownerId','ngaddons/webexRoomId','ngaddons/appName']@app.route('/validate',methods=['POST'])defdeployment_webhook():r=request.get_json()req=r.get('request',{})try:ifnotreq:returnsend_response(False,'<no uid>',"Invalid request, no payload.request found")uid=req.get("uid",'')app.logger.debug(f"+ uid:{uid}")ifnotuid:returnsend_response(False,'<no uid>',"Invalid request, no payload.request.uid found")labels=req.get("object",{}).get("metadata",{}).get("labels")if'ngaddons/bypass'inlabels:returnsend_response(True,uid,"Request bypassed as 'ngaddons/bypass' is set")missing=[lforlinREQUIRED_LABELSiflnotinlabels]app.logger.debug(f"+ missing:{missing}")ifmissing:returnsend_response(False,uid,f"Missing labels:{missing}")exceptExceptionase:returnsend_response(False,uid,f"Webhook exception:{e}")#Send OKreturnsend_response(True,uid,"Request has required labels")#Function to respond back to the Admission Controllerdefsend_response(allowed,uid,message):returnjsonify({"apiVersion":"admission.k8s.io/v1","kind":"AdmissionReview","response":{"allowed":allowed,"uid":uid,"status":{"message":message}}})if__name__=="__main__":ca_crt='/etc/ssl/ca.crt'ca_key='/etc/ssl/ca.key'app.run(ssl_context=(ca_crt,ca_key),port=5000,host='0.0.0.0',debug=True)
Dockerfile
# Image: ashoka007/check-labels:0.1FROM python:3.8-slimWORKDIR /appCOPY requirements.txt /appRUNpipinstall-r requirements.txtCOPY app.py /appCMD python app.py
Webhook manifest
apiVersion:admissionregistration.k8s.io/v1kind:ValidatingWebhookConfigurationmetadata:name:ngaddons-check-labelsnamespace:check-labelswebhooks:-name:ngaddons.check-labels.webhookfailurePolicy:FailsideEffects:NoneadmissionReviewVersions:["v1","v1beta1"]namespaceSelector:matchLabels:ngaddons/validation-webhooks:enabledrules:-apiGroups:["apps",""]resources:-"deployments"-"pods"apiVersions:-"*"operations:-CREATEclientConfig:service:name:${WEBHOOK_SERVICE_NAME}# to be substitutednamespace:${WEBHOOK_NAMESPACE}# to be substitutedpath:/validate/caBundle:${CA_BUNDLE}# to be substituted
Create self-signed key-pair and ${CA_BUNDLE}
# Configuration parameters are the key and DNS match is required[ req]default_bits= 2048distinguished_name= req_distinguished_namereq_extensions= req_extprompt= no[ req_distinguished_name]countryName= INstateOrProvinceName= KARlocalityName= BGLorganizationName= ACME INCcommonName= check-labels 0.1[ req_ext]subjectAltName= @alt_names[alt_names]DNS.1=${WEBHOOK_SERVICE_NAME}.${WEBHOOK_NAMESPACE}.svc❯ openssl req-x509-newkey rsa:4096-nodes-out certs/ca.crt-keyout certs/ca.key-days 365-config conf/ext.cnf-extensions req_ext
Create a secret with the above
❯ kubectl create secret tls webhook-secret--cert=certs/ca.crt--key=certs/ca.key--namespace=${WEBHOOK_NAMESPACE}
Create namespace
❯ kubectl create namespace${WEBHOOK_NAMESPACE}
Kubernetes manifest for webhook deployment
apiVersion:apps/v1kind:Deploymentmetadata:name:webhook-depnamespace:${WEBHOOK_NAMESPACE}labels:app:webhookspec:replicas:2selector:matchLabels:app:webhooktemplate:metadata:labels:app:webhookspec:containers:-name:webhook-containerimage:ashoka007/check-labels:0.1volumeMounts:-mountPath:/etc/sslname:webhook-certsreadOnly:truevolumes:-name:webhook-certssecret:secretName:webhook-secret
Expose the deployment with service
❯ kubectl expose deployment/webhook-dep--name=${WEBHOOK_SERVICE_NAME}--namespace=${WEBHOOK_NAMESPACE}--port=443--target-port=5000
Test
❯ kubectl create namespace demo-ns# normal scenario❯ kubectl run testpod--image=nginx-n demo-nspod/testpod created# start enforcing validation❯ kubectl label namespace demo-ns-l ngaddons/validation-webhooks=enabled# validation fail❯ kubectl run testpod2--image=nginx-n demo-nsError from server: admission webhook"ngaddons.check-labels.webhook" denied the request: Missing labels:['ngaddons/ownerCec','ngaddons/webexRoomId','ngaddons/appName']# validation pass❯ kubectl run testpod3--image=nginx-n demo-ns-l=ngaddons/ownerId=ram-l=ngaddons/webexRoomId=rams-room-id-l=ngaddons/appName=rams-test-apppod/testpod3 created# validation bypass❯ kubectl run testpod4--image=nginx-n demo-ns-l=ngaddons/bypassed=1pod/testpod4 created
Delete validation webhook
❯ kubectl delete validatingwebhookconfigurations.admissionregistration.k8s.io check-labels
Top comments(1)
Subscribe
For further actions, you may consider blocking this person and/orreporting abuse