1. Admission Webhooks ๆฏไปไน
่ฏทๆฑ้พ่ทฏ๏ผ
็จๆท่ฏทๆฑ โ Mutating ๅ ณๅก โ Validating ๅ ณๅก โ etcd
Webhook ๆ็ๆฏไธญ้ๆฆๆช่ฏทๆฑๅๅฎๅถๅๅค็๏ผๅ ๅซไธค็ง็ฑปๅ๏ผ
Mutate ไฟฎๆน
ๆช่ก่ฏทๆฑไนๅๅไฟฎๆน๏ผๆนๅฎไนๅๆๅๅ้พ่ทฏใ
Validate ๆ ก้ช
ๅฏน่ฏทๆฑ็ๆฐๆฎๅๆ ก้ช๏ผไธ็ฌฆๅ่งๅฎ็็ดๆฅๆๆใ
๐ก Admission ็ฟป่ฏไธบ"ๅๅ ฅ"๏ผ"ๅ ฅ"ๆ็ๆฏๅ ฅๅฐ etcd ๆฐๆฎๅบไธญใ
2. Webhook ็ๆง่กๆถๆบ
Admission Webhook ๆฌ่ดจๆฏ API Server ็ไธไธช webhook ่ฐ็จใๆป็ปๅบไธค็ง webhook ็่งฆๅๆถๆบ๏ผ
- ๆปไฝๆฅ่ฏด๏ผAdmission Webhooks ๆบๅถๆฏๅจ API Server ๆฅๆถๅฐ่ฏทๆฑใๆง่กๆๆๆฃๆฅๅๅๅฐ่ฏทๆฑๆไน ๅๅฐ etcd ไนๅ่งฆๅใ
- ่ฏฆ็ปๅฐ็๏ผMutating Webhook ๅจ Validating Webhook ไนๅ่งฆๅ๏ผๅ ๆญคๅฏไปฅๅ ๅฏน่ตๆบ่ฟ่กไฟฎๆน๏ผ็ถๅๅ่ฟ่ก้ช่ฏใ
3. Admission Webhook ็่ฟ่กๆต็จ
Pod ๅๅปบๆต็จไธญ Webhook ๆง่กๆถๆบ๏ผ
4. Mutating Webhook ่ฟๅๆ ผๅผ
Mutating Webhook ็่ฟๅๆ ผๅผๆฏไธไธช AdmissionReview ๅฏน่ฑก๏ผไธ่ฏทๆฑไฝฟ็จ็ธๅ็็ฑปๅ๏ผๅชๆฏๅกซๅ
ไบ response ้จๅใ
ๅฎๆด็ปๆ
JSON{
"kind": "AdmissionReview",
"apiVersion": "admission.k8s.io/v1",
"response": {
"uid": "่ฏทๆฑไธญ็ uid๏ผๅฟ
้กปๅๆ ท่ฟๅ",
"allowed": true,
"patchType": "JSONPatch",
"patch": "Base64 ็ผ็ ็ JSON Patch",
"status": {
"code": 200,
"message": "ๅฏ้็็ถๆไฟกๆฏ"
}
}
}
ๅๅญๆฎต่ฏดๆ
| ๅญๆฎต | ๆฏๅฆๅฟ ้ | ่ฏดๆ |
|---|---|---|
uid |
ๅฟ ้ | ไป่ฏทๆฑไธญๅๆ ทๅคๅถ๏ผAPI Server ้ ๅฎๅ ณ่่ฏทๆฑๅๅๅบ |
allowed |
ๅฟ ้ | true ๆพ่ก๏ผfalse ๆ็ป |
patchType |
ๆกไปถๅฟ ้ | allowed: true ไธ่ฆไฟฎๆน่ตๆบๆถๅฟ
้กปๆๅฎ๏ผ็ฎๅๅชๆฏๆ JSONPatch |
patch |
ๆกไปถๅฟ ้ | Base64 ็ผ็ ็ JSON Patch ๆฐ็ป๏ผไธ patchType ้
ๅฏนไฝฟ็จ |
status |
ๅฏ้ | ไป
ๅจ allowed: false ๆถๆๆไน๏ผ่ฏดๆๆ็ปๅๅ |
ไธ็งๅ ธๅๅๅบ
โ ๆพ่กๅนถไฟฎๆน๏ผMutate๏ผ
JSON{
"kind": "AdmissionReview",
"apiVersion": "admission.k8s.io/v1",
"response": {
"uid": "abc-123",
"allowed": true,
"patchType": "JSONPatch",
"patch": "W3sib3AiOiJhZGQiLCJwYXRoIjoiL21ldGFkYXRhL2xhYmVscy9lbnZpcm9ubWVudCIsInZhbHVlIjoicHJvZHVjdGlvbiJ9XQ=="
}
}
โก ็ดๆฅๆ็ป
JSON{
"kind": "AdmissionReview",
"apiVersion": "admission.k8s.io/v1",
"response": {
"uid": "abc-123",
"allowed": false,
"status": {
"code": 403,
"message": "Pod ไธๅ
่ฎธไฝฟ็จ latest ๆ ็ญพ"
}
}
}
โข ็ดๆฅๆพ่ก๏ผไธไฟฎๆน๏ผ
JSON{
"kind": "AdmissionReview",
"apiVersion": "admission.k8s.io/v1",
"response": {
"uid": "abc-123",
"allowed": true
}
}
๐ก ๅ
ณไบ patch ๅญๆฎต๏ผpatch ๅญๆฎต็ๅผๆฏ Base64 ็ผ็ ๅ็ JSON Patch๏ผ่งฃ็ ๅๅฐฑๆฏๆ ๅ็ RFC 6902 ๆ ผๅผใ
JSON Patch (RFC 6902)[
{"op": "add", "path": "/metadata/labels/environment", "value": "production"}
]
5. ไปฃ็ ็คบไพ
webhook.py
Pythonfrom flask import Flask, request, jsonify
import json
import ssl
import base64
app = Flask(__name__)
def create_patch(metadata):
"""
ๅๅปบ JSON Patch ไปฅๆทปๅ 'mutate' ๆณจ้ใ
ๅฆๆ metadata.annotations ไธๅญๅจ๏ผๅ้ฆๅ
ๅๅปบ่ฏฅ่ทฏๅพใ
"""
if 'labels' in metadata:
dic = metadata['labels']
else:
dic = {}
patch = [
{'op': 'add', 'path': '/metadata/labels', 'value': dic},
{'op': 'add', 'path': '/metadata/labels/environment', 'value': 'production'}
]
patch_json = json.dumps(patch)
patch_base64 = base64.b64encode(patch_json.encode('utf-8')).decode('utf-8')
return patch_base64
@app.route('/mutate', methods=['POST'])
def mutate():
"""
ๅค็ Mutating Webhook ็่ฏทๆฑ๏ผๅฏน Pod ๅฏน่ฑกๅบ็จ JSON Patchใ
"""
admission_review = request.get_json()
if 'request' not in admission_review or 'object' not in admission_review['request']:
return jsonify({
'kind': 'AdmissionReview',
'apiVersion': 'admission.k8s.io/v1',
'response': {
'allowed': False,
'status': {'message': 'Invalid AdmissionReview format'}
}
})
req = admission_review['request']
metadata = req['object']['metadata']
patch_json = create_patch(metadata)
admission_response = {
'kind': 'AdmissionReview',
'apiVersion': 'admission.k8s.io/v1',
'response': {
'uid': req['uid'],
'allowed': True,
'patchType': 'JSONPatch',
'patch': patch_json
}
}
return jsonify(admission_response)
if __name__ == '__main__':
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain('/certs/tls.crt', '/certs/tls.key')
app.run(host='0.0.0.0', port=443, ssl_context=context)
6. Validating Webhook ไปฃ็ ็คบไพ
ๅ Mutating Webhook ไธๅ๏ผValidating Webhook ๅชๅๆ ก้ชใไธไฟฎๆนๅฏน่ฑก๏ผๅๅบไธญไธ้่ฆ patch / patchType๏ผๅช้่ฆ่ฟๅ allowed: true/falseใ
ไธ้ขๅฎ็ฐไธไธชๅธธ่ง็ๅฎๅ
จ็ญ็ฅ๏ผ็ฆๆญข Pod ไฝฟ็จ :latest ้ๅๆ ็ญพ + ็ฆๆญข็นๆๅฎนๅจ + ็ฆๆญข hostPath ๆ่ฝฝใ
validate_webhook.py
Pythonfrom flask import Flask, request, jsonify
import ssl
app = Flask(__name__)
# ========== ๆ ก้ช่งๅ ==========
ALLOWED_REGISTRIES = [] # ็็ฉบ=ไธ้ๅถ้ๅไปๅบ๏ผๅกซๅ
ฅ ["company.registry.io"] ๅๅชๅ
่ฎธ่ฏฅไปๅบ
FORBIDDEN_TAGS = [":latest", ":LATEST", ""] # ็ฆๆญข็้ๅๆ ็ญพ
FORBIDDEN_HOST_PATHS = ["/", "/etc", "/var", "/proc", "/sys", "/dev"] # ็ฆๆญข็ hostPath ๅ็ผ
def validate_pod(pod):
"""
ๅฏน Pod ๅฏน่ฑกๆง่กๆ ก้ช๏ผ่ฟๅ (allowed, reason)ใ
allowed=True ่กจ็คบ้่ฟ๏ผallowed=False ่กจ็คบๆ็ป๏ผreason ไธบๆ็ปๅๅ ใ
"""
containers = pod.get('spec', {}).get('containers', [])
init_containers = pod.get('spec', {}).get('initContainers', [])
all_containers = containers + init_containers
# ---- 1. ๆฃๆฅ้ๅๆ ็ญพ ----
for container in all_containers:
image = container.get('image', '')
# ๆฃๆฅๆฏๅฆไฝฟ็จไบ็ฆๆญข็ๆ ็ญพ
for tag in FORBIDDEN_TAGS:
if tag and image.endswith(tag):
return False, f"ๅฎนๅจ '{container['name']}' ไฝฟ็จไบ็ฆๆญข็้ๅๆ ็ญพ: {image}๏ผไธๅ
่ฎธ :latest ๆๆ ๆ ็ญพ๏ผ"
# ๆฃๆฅ้ๅไปๅบ็ฝๅๅ๏ผๅฆๆ้
็ฝฎไบ๏ผ
if ALLOWED_REGISTRIES:
allowed = any(image.startswith(registry) for registry in ALLOWED_REGISTRIES)
if not allowed:
return False, f"ๅฎนๅจ '{container['name']}' ็้ๅ '{image}' ไธๅจๅ
่ฎธ็ไปๅบๅ่กจไธญ"
# ---- 2. ๆฃๆฅ็นๆๅฎนๅจ ----
for container in all_containers:
security_context = container.get('securityContext', {})
if security_context.get('privileged', False):
return False, f"ๅฎนๅจ '{container['name']}' ไฝฟ็จไบ็นๆๆจกๅผ๏ผprivileged: true๏ผ๏ผ่ฟๆฏ่ขซ็ฆๆญข็"
if security_context.get('runAsUser', -1) == 0:
return False, f"ๅฎนๅจ '{container['name']}' ไปฅ root ็จๆท่ฟ่ก๏ผrunAsUser: 0๏ผ๏ผ่ฟๆฏ่ขซ็ฆๆญข็"
# ---- 3. ๆฃๆฅ hostPath ๆ่ฝฝ ----
volumes = pod.get('spec', {}).get('volumes', [])
for volume in volumes:
host_path = volume.get('hostPath', {})
if host_path:
path = host_path.get('path', '')
for forbidden in FORBIDDEN_HOST_PATHS:
if path == forbidden or path.startswith(forbidden + '/'):
return False, f"Pod ไฝฟ็จไบๅฑ้ฉ็ hostPath ๆ่ฝฝ: {path}๏ผ็ฆๆญขๆ่ฝฝๅฎฟไธปๆบๆๆ็ฎๅฝ๏ผ"
# ---- 4. ๆฃๆฅ hostNetwork / hostPID / hostIPC ----
pod_spec = pod.get('spec', {})
if pod_spec.get('hostNetwork', False):
return False, "Pod ไฝฟ็จไบ hostNetwork: true๏ผ่ฟๆฏ่ขซ็ฆๆญข็"
if pod_spec.get('hostPID', False):
return False, "Pod ไฝฟ็จไบ hostPID: true๏ผ่ฟๆฏ่ขซ็ฆๆญข็"
if pod_spec.get('hostIPC', False):
return False, "Pod ไฝฟ็จไบ hostIPC: true๏ผ่ฟๆฏ่ขซ็ฆๆญข็"
return True, ""
# ========== HTTP Handler ==========
@app.route('/validate', methods=['POST'])
def validate():
"""
ๅค็ Validating Webhook ็่ฏทๆฑ๏ผๅฏน Pod ๅฏน่ฑกๆง่กๅฎๅ
จ็ญ็ฅๆ ก้ชใ
"""
admission_review = request.get_json()
# ้ช่ฏ่ฏทๆฑๆ ผๅผ
if 'request' not in admission_review or 'object' not in admission_review['request']:
return jsonify({
'kind': 'AdmissionReview',
'apiVersion': 'admission.k8s.io/v1',
'response': {
'allowed': False,
'status': {'message': 'Invalid AdmissionReview format'}
}
})
req = admission_review['request']
pod = req['object']
# ๆง่กๆ ก้ช
allowed, reason = validate_pod(pod)
if allowed:
# ๆพ่ก๏ผไธ้่ฆ patch๏ผๅช่ฟๅ allowed: true
admission_response = {
'kind': 'AdmissionReview',
'apiVersion': 'admission.k8s.io/v1',
'response': {
'uid': req['uid'],
'allowed': True
}
}
else:
# ๆ็ป๏ผ่ฟๅ allowed: false + ๆ็ปๅๅ
admission_response = {
'kind': 'AdmissionReview',
'apiVersion': 'admission.k8s.io/v1',
'response': {
'uid': req['uid'],
'allowed': False,
'status': {
'code': 403,
'message': reason
}
}
}
print(f"[VALIDATE] allowed={allowed}, reason={reason or 'N/A'}")
return jsonify(admission_response)
if __name__ == '__main__':
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain('/certs/tls.crt', '/certs/tls.key')
app.run(host='0.0.0.0', port=443, ssl_context=context)
Mutating vs Validating ไปฃ็ ๅฏนๆฏ
Mutating
- ้่ฆ
patch+patchTypeๅญๆฎต - ่ฟๅ Base64 ็ผ็ ็ JSON Patch
- ๅฏไปฅไฟฎๆนๅฏน่ฑก็ไปปๆๅญๆฎต
- ๅจ Validating ไนๅๆง่ก
Validating
- ๅช้่ฆ
allowed: true/false - ไธ้่ฆ
patchๅpatchType - ๅช่ฝๅคๆญๆพ่ก/ๆ็ป๏ผไธ่ฝไฟฎๆนๅฏน่ฑก
- ๅจ Mutating ไนๅๆง่ก
ๆ ก้ช่งๅ่ฏดๆ
| ่งๅ | ๆฃๆฅๅ ๅฎน | ๆ็ป็คบไพ |
|---|---|---|
| ้ๅๆ ็ญพ | ็ฆๆญข :latest ๅๆ ๆ ็ญพ้ๅ |
nginx:latest โ ๆ็ป |
| ้ๅไปๅบ | ๅฏ้ ็ฝฎไปๅบ็ฝๅๅ๏ผ็็ฉบๅไธ้๏ผ | docker.io/nginx โ ๆ็ป๏ผ่ฅ็ฝๅๅไธๅซ docker.io๏ผ |
| ็นๆๅฎนๅจ | ็ฆๆญข privileged: true ๅ runAsUser: 0 |
่ฎพ็ฝฎไบ securityContext.privileged: true โ ๆ็ป |
| hostPath | ็ฆๆญขๆ่ฝฝๅฎฟไธปๆบๆๆ็ฎๅฝ | hostPath: /var/run/docker.sock โ ๆ็ป |
| host ็ฝ็ป | ็ฆๆญข hostNetwork/hostPID/hostIPC |
hostNetwork: true โ ๆ็ป |
ValidatingWebhookConfiguration
ๅ MutatingWebhookConfiguration ็ปๆๅ ไนไธๆ ท๏ผๅชๆฏ kind ไธๅใpath ๆๅ /validate๏ผ
YAMLapiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: example-validating-webhook
webhooks:
- name: validate.example.webhook.com
clientConfig:
service:
name: webhook-service
namespace: default
path: "/validate" # ๆๅ /validate ่้ /mutate
caBundle: "..." # ๅไธไธช CA ่ฏไนฆ
rules:
- operations: ["CREATE", "UPDATE"] # ๅๅปบๅๆดๆฐ้ฝๆ ก้ช
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
admissionReviewVersions: ["v1"]
sideEffects: None
failurePolicy: Fail # webhook ไธๅฏ็จๆถๆ็ป่ฏทๆฑ๏ผๅฎๅ
จไผๅ
๏ผ
timeoutSeconds: 5
๐ก failurePolicy ็้ๆฉ๏ผ
Fail โ webhook ไธๅฏ็จๆถๆ็ป่ฏทๆฑ๏ผๅฎๅ
จไผๅ
๏ผๆจ่็จไบๅฎๅ
จ็ญ็ฅ๏ผ
Ignore โ webhook ไธๅฏ็จๆถ่ทณ่ฟๆ ก้ช๏ผๅฏ็จๆงไผๅ
๏ผๆจ่็จไบ้ๅ
ณ้ฎ็ญ็ฅ๏ผ
ๅฏนไบๅฎๅ
จ็ฑปๆ ก้ช๏ผ็ฆๆญข็นๆๅฎนๅจ็ญ๏ผ๏ผๅบ่ฏฅ็จ Fail๏ผ้ฟๅ
webhook ๆ
้ๆถไธๅฎๅ
จ็ Pod ๆๆ้่ฟใ
ไธค็ง Webhook ้จ็ฝฒๅจไธ่ตท
ๅฎ้ ้กน็ฎไธญ๏ผMutating ๅ Validating ้ๅธธ้จ็ฝฒๅจๅไธไธช Service ้๏ผๅ ฑ็จ่ฏไนฆๅ Pod๏ผๅชๆฏ่ทฏๅพไธๅ๏ผ
Python โ ๅๅนถ็ๆฌfrom flask import Flask, request, jsonify
import json, ssl, base64
app = Flask(__name__)
# ... create_patch() ๅ validate_pod() ็ไปฃ็ ๅไธ ...
@app.route('/mutate', methods=['POST'])
def mutate():
# ... Mutating ้ป่พ ...
@app.route('/validate', methods=['POST'])
def validate():
# ... Validating ้ป่พ ...
if __name__ == '__main__':
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain('/certs/tls.crt', '/certs/tls.key')
app.run(host='0.0.0.0', port=443, ssl_context=context)
ๅฏนๅบ็ K8s ่ตๆบๅ ณ็ณป๏ผ
7. ๅถไฝ้ๅ
Dockerfile
DockerfileFROM python:3.9-slim
WORKDIR /app
COPY webhook.py .
RUN pip install Flask -i https://pypi.tuna.tsinghua.edu.cn/simple
CMD ["python", "webhook.py"]
ๆๅปบ & ๆจ้
Shelldocker tag webhook:latest uhub.service.ucloud.cn/mute-webhook/mute:latest
docker push uhub.service.ucloud.cn/mute-webhook/mute:latest
8. ็ๆ่ฏไนฆ
Shell โ gen-webhook-cert.sh#!/bin/bash
set -e
SERVICE=webhook-service
NAMESPACE=default
SECRET=webhook-certs
DIR=certs
rm -rf ${DIR}
mkdir -p ${DIR} && cd ${DIR}
echo "===== 1. ็ๆ CA ====="
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key \
-subj "/CN=${SERVICE}.${NAMESPACE}.svc" \
-days 36500 -out ca.crt
echo "===== 2. ็ๆ Webhook ๆๅก่ฏไนฆ ====="
cat > server.cnf << EOF
[req]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = v3_req
distinguished_name = dn
[dn]
CN = ${SERVICE}.${NAMESPACE}.svc
[v3_req]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${SERVICE}
DNS.2 = ${SERVICE}.${NAMESPACE}
DNS.3 = ${SERVICE}.${NAMESPACE}.svc
DNS.4 = ${SERVICE}.${NAMESPACE}.svc.cluster.local
EOF
openssl genrsa -out webhook.key 2048
openssl req -new -key webhook.key -out webhook.csr -config server.cnf
openssl x509 -req -in webhook.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out webhook.crt -days 36500 \
-extensions v3_req -extfile server.cnf
echo "===== 3. ๅๅปบ K8s Secret ====="
kubectl create secret tls ${SECRET} \
--cert=webhook.crt \
--key=webhook.key \
--namespace=${NAMESPACE} \
--dry-run=client -o yaml | kubectl apply -f -
echo ""
echo "===== 4. caBundle๏ผ็จไบ WebhookConfiguration๏ผ====="
cat ca.crt | base64 | tr -d '\n'
echo ""
echo "===== ๅฎๆ๏ผ ====="
ls -la ca.crt webhook.crt webhook.key
ๆง่กๆนๅผ๏ผ
chmod +x gen-webhook-cert.sh
./gen-webhook-cert.sh
่พๅบ้็ caBundle ๅผ็ดๆฅ็ฒ่ดดๅฐ MutatingWebhookConfiguration ็ caBundle ๅญๆฎตๅณๅฏใๅฆๆ้่ฆๆนๆๅกๅๆๅฝๅ็ฉบ้ด๏ผๆน่ๆฌๅผๅคด็ SERVICE ๅ NAMESPACE ๅ้ๅฐฑ่กใ
9. MutatingWebhookConfiguration
ๅฎๅฐฑๆฏๆดไธชๆบๅถ็"ๅ
ณๅก่งๅ"ใๅ
ๆ webhook ็จๅบ่ฟ่ก็ๆฏไธๅค็๏ผK8s API Server ๆ นๆฌไธ็ฅ้ๅฎ็ๅญๅจ๏ผไนไธ็ฅ้ไปไนๆถๅ่ฏฅๆ่ฏทๆฑ่ฝฌๅ็ปๅฎใMutatingWebhookConfiguration ๅฐฑๆฏๅ่ฏ API Server ็้ฃๅผ "้็ฅๅ"ใ
YAMLapiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: example-mutating-webhook
webhooks:
- name: example.webhook.com # webhook ็ๅฏไธๆ ่ฏๅ
clientConfig: # ๅ่ฏ API Server ๆไนๆพๅฐไฝ ็ webhook
service:
name: webhook-service # Service ๅ
namespace: default # ๅฝๅ็ฉบ้ด
path: "/mutate" # ่ฏทๆฑ่ทฏๅพ
caBundle: "..." # CA ่ฏไนฆ็ Base64
rules: # ไปไนๆ
ๅตไธ่งฆๅ่ฟไธช webhook
- operations: ["CREATE"] # ๅๅปบ่ตๆบๆถ่งฆๅ
apiGroups: [""] # ๆ ธๅฟ API ็ป
apiVersions: ["v1"] # API ็ๆฌ
resources: ["pods"] # ๅชๅฏน Pod ็ๆ
admissionReviewVersions: ["v1"] # ๆฏๆ็ AdmissionReview ็ๆฌ
sideEffects: None # ๅฃฐๆ webhook ๆฒกๆๅฏไฝ็จ
ๅ ณ้ฎ็น้ไธช่งฃ้
clientConfig
API Server ๆไนๆพไฝ ็ webhookใ่ฟ้็จ Service ๆนๅผ๏ผ้็พคๅ ้จ่ฎฟ้ฎ๏ผ๏ผไนๅฏไปฅ็จ URL ๆนๅผ๏ผๅค้จๅฐๅ๏ผใ
caBundle
ไธบไปไน้่ฆ๏ผๅ ไธบ API Server ็จ HTTPS ่ฐ็จ webhook๏ผๅฎ้่ฆ้ช่ฏ webhook ่ฏไนฆ็ๅๆณๆงใcaBundle ๅฐฑๆฏ็ญพๅ webhook ่ฏไนฆ็ CA ่ฏไนฆ๏ผAPI Server ๆฟๅฎๆฅ้ช่ฏ webhook ็ TLS ่ฏไนฆๆฏๅฆๅฏไฟกใ่ฟๅฐฑๆฏไนๅ็จ gen-webhook-cert.sh ็ๆ ca.crt ๅนถ Base64 ็ผ็ ็ๅๅ ใ
rules
ๆฆๆช่งๅใ่ฟ้้ ็ฝฎ็ๆฏ๏ผๅชๆๅๅปบ Pod ๆถๆ่งฆๅ๏ผไธๅฝฑๅๅ ถไป่ตๆบๆๅ ถไปๆไฝ๏ผๆดๆฐใๅ ้ค๏ผใ
sideEffects: None
ๅ่ฏ K8s "ๆ่ฟไธช webhook ไธไผไบง็ๅฏไฝ็จ"๏ผๆฏๅฆไธไผไฟฎๆนๅค้จ็ณป็ปใ่ฟๆฏ K8s v1 ็้่ฆ่ฆๆฑใ
ๆดไฝๆต็จ
ๆไปฅ่ๆฌ้ๅ
็จ base64 -w 0 ca.crt ๆ CA ่ฏไนฆ็ผ็ ๏ผๅๅกซๅ
ฅ caBundle๏ผๅฐฑๆฏๆ"้ช่ฏๅญๆฎ"ไบค็ป API Server๏ผ่ฎฉๅฎ่ฝๅฎๅ
จๅฐ่ฟๆฅไฝ ็ webhookใ