# kubernetes-webhook-demo **Repository Path**: littlefeet/kubernetes-webhook-demo ## Basic Information - **Project Name**: kubernetes-webhook-demo - **Description**: kubernetes-webhook-demo - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-07-30 - **Last Updated**: 2022-08-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README + 创建两个钩子,`/mutate` 与 `/validate` + `/mutate` 将在创建`deployment`资源时,基于版本,给资源加上 `webhook.example.com/allow: true`的`annotations(注释)` + `/validate` 将对 `/mutate` 增加了 `allow:true` 的注释批准通行,否则拒绝 ```sh # 获得集群的ca证书base64 cat /etc/kubernetes/pki/ca.crt | base64 | tr -d '\n' # 获得当前上下文中的CA base64 信息 kubectl config view --raw --flatten -o json | jq -r '.clusters[] | .cluster."certificate-authority-data"' ``` ```yaml # webhook会对资源进行修改,需要一个sa apiVersion: v1 kind: ServiceAccount metadata: name: admission-webhook-example-sa labels: app: admission-webhook-example --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: admission-webhook-example-cr labels: app: admission-webhook-example rules: - apiGroups: - qikqiak.com resources: - "*" verbs: - "*" - apiGroups: - "" resources: - pods - events verbs: - "*" - apiGroups: - apps resources: - deployments - daemonsets - replicasets - statefulsets verbs: - "*" - apiGroups: - autoscaling resources: - '*' verbs: - '*' --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: admission-webhook-example-crb labels: app: admission-webhook-example subjects: - kind: ServiceAccount name: admission-webhook-example-sa namespace: default roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: admission-webhook-example-cr ``` ```yaml apiVersion: v1 kind: Service metadata: name: admission-webhook-example-svc labels: app: admission-webhook-example spec: ports: - port: 443 targetPort: 443 selector: app: admission-webhook-example ``` ```sh # rbac文件由个ServiceAccount, 会创建一个secret, [root@webhook ~/webhood]# kubectl get secrets NAME TYPE DATA AGE admission-webhook-example-sa-token-s4jbv kubernetes.io/service-account-token 3 3m49s ``` + K8S集群默认是HTTPS通信的,所以APiserver调用webhook的过程也是HTTPS的 + k8s集群1.22版本以后此脚本需要修改,具体参考 [官网](https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/certificate-signing-requests/) ```sh # 证书签发脚本, 因为K8S集群默认是HTTPS通信的,所以APiserver调用webhook的过程也是HTTPS的,所以需要进行证书认证, 通过 CertificateSigningRequest 签发, --secret一定要指定自己的(如上面创建的sa) #!/bin/bash set -e usage() { cat <> ${tmpdir}/csr.conf [req] req_extensions = v3_req distinguished_name = req_distinguished_name [req_distinguished_name] [ v3_req ] basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = ${service} DNS.2 = ${service}.${namespace} DNS.3 = ${service}.${namespace}.svc EOF openssl genrsa -out ${tmpdir}/server-key.pem 2048 openssl req -new -key ${tmpdir}/server-key.pem -subj "/CN=${service}.${namespace}.svc" -out ${tmpdir}/server.csr -config ${tmpdir}/csr.conf # 清理之前为我们的服务创建的任何 CSR。 如果不存在则忽略错误 kubectl delete csr ${csrName} 2>/dev/null || true # 创建服务器证书/密钥 CSR 并发送到 k8s API cat <&2 exit 1 fi # 写入到证书中 echo ${serverCert} | openssl base64 -d -A -out ${tmpdir}/server-cert.pem # create the secret with CA cert and server cert/key kubectl create secret generic ${secret} \ --from-file=key.pem=${tmpdir}/server-key.pem \ --from-file=cert.pem=${tmpdir}/server-cert.pem \ --dry-run -o yaml | kubectl -n ${namespace} apply -f - ``` ```sh # 查看生成的证书 [root@webhook ~/webhood]# kubectl get csr NAME AGE SIGNERNAME REQUESTOR CONDITION admission-webhook-example-svc.default 5s kubernetes.io/legacy-unknown kubernetes-admin Approved,Issued # 将证书放在特定位置,这也是我们启动 deployment必须的 用于和api-server通信 mkdir -p /etc/webhook/certs kubectl get secret ${secret} -o json | jq -r '.data."key.pem"' | base64 -d > /etc/webhook/certs/key.pem kubectl get secret ${secret} -o json | jq -r '.data."cert.pem"' | base64 -d > /etc/webhook/certs/cert.pem ``` + MutatingWebhookConfiguration ```yaml apiVersion: admissionregistration.k8s.io/v1beta1 kind: MutatingWebhookConfiguration metadata: name: mutating-webhook-example-cfg labels: app: admission-webhook-example webhooks: - name: mutating-example.qikqiak.com clientConfig: service: name: admission-webhook-example-svc namespace: default path: "/mutate" # 和代码中路径一致 caBundle: ${CA_BUNDLE} # ca base64信息 rules: - operations: [ "CREATE" ] apiGroups: ["apps", ""] apiVersions: ["v1"] resources: ["deployments","services"] namespaceSelector: matchLabels: # namespace具有该标签才作用于资源对象 admission-webhook-example: enabled ``` + ValidatingWebhookConfiguration ```yaml apiVersion: admissionregistration.k8s.io/v1beta1 kind: ValidatingWebhookConfiguration metadata: name: validation-webhook-example-cfg labels: app: admission-webhook-example webhooks: - name: required-labels.qikqiak.com clientConfig: service: name: admission-webhook-example-svc namespace: default path: "/validate" # 和代码中路径一致 caBundle: ${CA_BUNDLE} # ca base64信息 rules: - operations: [ "CREATE" ] apiGroups: ["apps", ""] apiVersions: ["v1"] resources: ["deployments","services"] namespaceSelector: matchLabels: # namespace具有该标签才作用于资源对象 admission-webhook-example: enabled ``` ```sh # 给要使用的 namespace 打标签 kubectl label namespace default admission-webhook-example=enabled ``` ### 2. Webhook代码 [code](https://gitee.com/littlefeet/kubernetes-webhook-demo) ```go go build -o admission-webhook-example ``` ### 3. webhook-deployment ```Dockerfile FROM alpine:latest WORKDIR / ADD ./cert.pem /etc/webhook/certs/cert.pem ADD ./key.pem /etc/webhook/certs/key.pem ADD ./admission-webhook-example /admission-webhook-example RUN chmod +x /admission-webhook-example ENTRYPOINT ["./admission-webhook-example"] ``` ```sh # 编译好的go程序admission-webhook-example也放在这 cd /etc/webhook/certs cp /etc/webhook/certs/* . # build docker build -t admission-webhook:v1 . ``` ```yaml # deployment apiVersion: apps/v1 kind: Deployment metadata: name: admission-webhook-example-deployment labels: app: admission-webhook-example spec: replicas: 1 selector: matchLabels: app: admission-webhook-example template: metadata: labels: app: admission-webhook-example spec: serviceAccount: admission-webhook-example-sa containers: - name: admission-webhook-example image: admission-webhook:v1 imagePullPolicy: IfNotPresent # 证书位置改为自己的 args: - -tlsCertFile=/etc/webhook/certs/cert.pem - -tlsKeyFile=/etc/webhook/certs/key.pem - -alsologtostderr - -v=4 - 2>&1 volumeMounts: - name: webhook-certs mountPath: /etc/webhook/certs readOnly: true volumes: - name: webhook-certs secret: # 名字改为创建的sa的secret名字 secretName: admission-webhook-example-sa-token-s4jbv ``` ### 4. 测试 ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: reject annotations: admission-webhook-example.qikqiak.com/mutate: "false" spec: selector: matchLabels: app: reject template: metadata: labels: app: reject spec: containers: - name: reject image: tutum/curl command: ["/bin/sleep","infinity"] imagePullPolicy: IfNotPresent ``` ```sh # yaml中的注释 admission-webhook-example.qikqiak.com/mutate: "false", value 为 false,准入控制就拒绝, 去掉 annotations 就可以创建 [root@webhook ~/webhood/debug]# kubectl apply -f jujue.yaml Error from server (required labels are not set): error when creating "jujue.yaml": admission webhook "required-labels.qikqiak.com" denied the request: required labels are not set ```