nginx-ingress安装admission webhook增加对ingress资源校验能力
一、背景
nginx-ingress作为南北向流量入口被广泛使用,某些情况下由于疏忽未检查ingress资源的合法性,可能导致ingress资源创建后,ingress-nginx容器中nginx一直reload,导致服务入口异常。
二、方案介绍
在nginx-ingress中引入admission webhook,在配置到达ingress-nginx之前,对其的合法性进行校验。
Admission webhook是K8S对请求资源对象的准入控制,Admission webhook有两种控制机制MutatingAdmissionWebhook和 ValidatingAdmissionWebhook,这两个控制器将发送准入请求的 HTTP 回调服务并接收一个准入响应。准入控制器是在对象持久化之前用于对 Kubernetes API Server 的请求进行拦截的代码段,在请求经过身份验证和授权之后放行通过。准入控制器可能正在 validating、 mutating或者都在执行,Mutating 控制器可以修改他们的处理的资源对象,Validating 控制器校验资源合法性,如果任何一个阶段中的任何控制器拒绝了请求,则会立即拒绝整个请求,并将错误返回给最终的用户。
-
MutatingAdmissionWebhook:对请求的对象进行修改,sidecar就是通过此机制实现对请求对象进行修改,如istio 就是通过这种webhook将envoy sidecar容器自动注入pod中的
-
ValidatingAdmissionWebhook:对请求资源对象的合法性进行检查,避免错误的对象被写入导致发生异常,例如ingress就是应用的这种webhook对请求资源进行合法性检查。
三、启用nginx-ingress admission webhook
3.1 helm部署nginx-ingress时开启webhook
使用helm直接开启webhook能力
- 在集群中安装helm3工具
# 先在集群中安装helm3部署工具(可忽略)
wget https://get.helm.sh/helm-v3.10.2-linux-amd64.tar.gz
tar -xzvf helm-v3.10.2-linux-amd64.tar.gz
mv linux-amd64/helm /usr/local/bin/helm
# 验证helm正确安装,查看helm版本
helm version
version.BuildInfo{Version:"v3.10.2", GitCommit:"50f003e5ee8704ec937a756c646870227d7c8b58", GitTreeState:"clean", GoVersion:"go1.18.8"}
-
下载ingress-nginx chart包
wget https://github.com/kubernetes/ingress-nginx/releases/download/helm-chart-4.3.0/ingress-nginx-4.3.0.tgz
tar -xf ./ingress-nginx-4.3.0.tgz
cd ./ingress-nginx
- 创建配置文件,开启admissionWebhook配置
cat > config.yaml << EOF
controller:
name: controller #自定义名称
# controller镜像地址设定
image:
# name指定了nginx-ingress-controller工作负载的名称
name: custom
# repository指定了镜像地址,请根据集群所在region进行替换
repository: swr.cn-east-3.myhuaweicloud.com/hwofficial/nginx-ingress
registry: ""
image: ""
# 镜像版本
tag: "v1.2.1"
digest: ""
# 设定ingress class和controller api,ingressClass和ingressClassResource.name的值要相同
ingressClass: ingress-custom
ingressClassResource:
name: ingress-custom
controllerValue: k8s.io/ingress-custom
# 设定controller要使用的svc类型。本例中使用的是LoadBalancer类型
service:
# 注解中写入ELB ID
# 本例中ELB ID已经省略中间部分,以实际使用的ELB ID为准
type: LoadBalancer
annotations:
kubernetes.io/elb.class: performance
kubernetes.io/elb.id: 3660...a481
kubernetes.io/elb.health-check-flag: "on"
kubernetes.io/elb.health-check-option: '{}'
externalTrafficPolicy: Local
resources:
requests:
cpu: 200m
memory: 200Mi
config:
keep-alive-requests: "100"
admissionWebhooks: # 开启admissionWebhook配置,helm安装后,自动安装
enabled: true
patch:
enabled: true
image:
registry: registry.k8s.io
image: ingress-nginx/kube-webhook-certgen
tag: v1.1.1
digest: ""
# 关闭defaultBackend的安装
defaultBackend:
enabled: false
EOF
3.2 在部署nginx-ingress后手动通过yaml部署webhook方式
某些场景在一开始安装ingress-nginx时,没有开启webhook能力,需要手动后面补装,主要步骤如下:
- 创建ValidatingWebhookConfiguration资源,确保kube-apiserver收到ingress资源创建时可以回调到对应webhook服务
- 创建job生产证书,webhook服务必须是https,并且kube-apiserver调用webhoo服务是单向认证(客户端验证服务端证书)
- 创建相应的权限
- 为nginx-ingress的validating-webhook服务暴露service
- 修改nginx-ingress启动参数,增加validating-webhook能力,加载服务端证书和暴露相关服务端口
- 创建相关资源,对应上述1-4步内容
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: admission-webhook
name: ingress-nginx-admission
webhooks:
- name: validate.nginx.ingress.kubernetes.io
matchPolicy: Equivalent
rules:
- apiGroups:
- networking.k8s.io
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- ingresses
failurePolicy: Fail #若admission调用失败(访问异常或处理异常),Fail拒绝请求,Ignore则继续处理请求但跳过 Webhook
sideEffects: None
admissionReviewVersions:
- v1
clientConfig:
service:
namespace: kube-system
name: ingress-nginx-controller-admission
path: /networking/v1/ingresses
---
# Source: ingress-nginx/templates/controller-service-webhook.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: controller
name: ingress-nginx-controller-admission
namespace: kube-system
spec:
type: ClusterIP
ports:
- name: https-webhook
port: 443
targetPort: webhook
appProtocol: https
selector:
app: nginx-ingress
component: controller
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: ingress-nginx-admission
namespace: kube-system
annotations:
helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: admission-webhook
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: ingress-nginx-admission
annotations:
helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: admission-webhook
rules:
- apiGroups:
- admissionregistration.k8s.io
resources:
- validatingwebhookconfigurations
verbs:
- get
- update
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ingress-nginx-admission
annotations:
helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: admission-webhook
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: ingress-nginx-admission
subjects:
- kind: ServiceAccount
name: ingress-nginx-admission
namespace: kube-system
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: ingress-nginx-admission
namespace: kube-system
annotations:
helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: admission-webhook
rules:
- apiGroups:
- ''
resources:
- secrets
verbs:
- get
- create
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ingress-nginx-admission
namespace: kube-system
annotations:
helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: admission-webhook
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: ingress-nginx-admission
subjects:
- kind: ServiceAccount
name: ingress-nginx-admission
namespace: kube-system
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: ingress-nginx-admission-create
namespace: kube-system
annotations:
helm.sh/hook: pre-install,pre-upgrade
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: admission-webhook
spec:
template:
metadata:
name: ingress-nginx-admission-create
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: admission-webhook
spec:
containers:
- name: create
image: swr.cn-north-4.myhuaweicloud.com/hjmtest/kube-webhook-certgen:v1.1.1 #测试镜像(需自行更换)
imagePullPolicy: IfNotPresent
args:
- create
- --host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc
- --namespace=$(POD_NAMESPACE)
- --secret-name=ingress-nginx-admission
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
securityContext:
allowPrivilegeEscalation: false
restartPolicy: OnFailure
serviceAccountName: ingress-nginx-admission
imagePullSecrets:
- name: default-secret
nodeSelector:
kubernetes.io/os: linux
securityContext:
runAsNonRoot: true
runAsUser: 2000
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: ingress-nginx-admission-patch
namespace: kube-system
annotations:
helm.sh/hook: post-install,post-upgrade
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: admission-webhook
spec:
template:
metadata:
name: ingress-nginx-admission-patch
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: admission-webhook
spec:
containers:
- name: patch
image: swr.cn-north-4.myhuaweicloud.com/hjmtest/kube-webhook-certgen:v1.1.1 #测试镜像(需自行更换)
imagePullPolicy: IfNotPresent
args:
- patch
- --webhook-name=ingress-nginx-admission
- --namespace=$(POD_NAMESPACE)
- --patch-mutating=false
- --secret-name=ingress-nginx-admission
- --patch-failure-policy=Fail
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
securityContext:
allowPrivilegeEscalation: false
restartPolicy: OnFailure
serviceAccountName: ingress-nginx-admission
imagePullSecrets:
- name: default-secret
nodeSelector:
kubernetes.io/os: linux
securityContext:
runAsNonRoot: true
runAsUser: 2000
fsGroup: 2000
- 修改ingress-nginx 启动参数,对应第5步内容
# 修改ingress-nginx controller启动参数,新增validating-webhook、validating-webhook-certificate、validating-webhook-key
- args:
- /nginx-ingress-controller
- --default-backend-service=kube-system/cceaddon-nginx-ingress-default-backend
- --election-id=ingress-controller-leader
- --ingress-class=nginx
- --configmap=kube-system/cceaddon-nginx-ingress-controller
- --publish-service=$(POD_NAMESPACE)/cceaddon-nginx-ingress-controller
- --validating-webhook=:8443
- --validating-webhook-certificate=/usr/local/certificates/cert
- --validating-webhook-key=/usr/local/certificates/key
# 修改ingress-nginx controller port端口
name: nginx-ingress-controller
ports:
- containerPort: 80
name: http
protocol: TCP
- containerPort: 443
name: https
protocol: TCP
- containerPort: 8443
name: webhook
protocol: TCP
- containerPort: 10254
name: prometheus
protocol: TCP
# 挂载secrets作为服务端证书
volumeMounts:
- mountPath: /etc/localtime
name: localtime
readOnly: true
- mountPath: /usr/local/certificates/
name: webhook-cert
readOnly: true
…
volumes:
- name: webhook-cert
secret:
secretName: ingress-nginx-admission
defaultMode: 420
- name: localtime
hostPath:
path: /etc/localtime
type: ''
四、多nginx-ingress场景下,通过admissionwebhook作用域限制单个nginx-ingress webhook作用范围
admission webhook作用域默认是全局的,如果一个集群中有多个nginx-ingress组件(通过ingressClass区域),由于有多个ValidatingAdmissionWebhook,当某个ingress资源创建时,kube-apiserver会调用多个nginx-ingress webhok能力进行校验,当某个nginx-ingress出现故障(可能与该ingress资源并不关联),将会阻塞所有的ingress的写入。
多nginx-ingress场景下建议通过admissionwebhook 自带的namespaceSelector控制每个nginx-ingress的作用域,使其作用到指定的命名空间,只教验作用域命名空间下的ingresses资源。
admissionwebhook:
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name #需要教验的namespace含有的标签key
operator: In
values: ["${namespace}"] # namespace含有的标签volue(默认命名空间本身)
rules:
- operations: ["CREATE","UPDATE"]
apiGroups: ["*"]
apiVersions: ["*"]
resources: ["ingresses"] #限制作用资源
scope: "*" #由于限制了作用资源ingresses,无需配置
objectSelector: {}
订阅本文作者或关注容器魔方
获取更多云原生技术资讯
本文评论区回复“云原生”,即可添加小助手微信k8s2222
领取应季云原生资料一份
- 点赞
- 收藏
- 关注作者
评论(0)