ImagePolicyWebhook用于限制节点调用某个镜像
- 环境查看
系统环境
# cat /etc/redhat-release
Rocky Linux release 9.3 (Blue Onyx)
# uname -a
Linux Rocky9K8SMaster003021 5.14.0-362.18.1.el9_3.0.1.x86_64 #1 SMP PREEMPT_DYNAMIC Sun Feb 11 13:49:23 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
软件环境
# kubectl version
Client Version: v1.30.2
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.25.16
WARNING: version difference between client (1.30) and server (1.25) exceeds the supported minor version skew of +/-1
- 配置
本次已限制nginx镜像为例
安装go
# yum -y install go
编写go语言用于限制nginx
# cat webhook.go
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
)
// AdmissionReview 是 Webhook 接收到的请求的结构
type AdmissionReview struct {
Request struct {
Kind string `json:"kind"`
Object struct {
Spec struct {
Containers []struct {
Name string `json:"name"`
Image string `json:"image"`
} `json:"containers"`
} `json:"spec"`
} `json:"object"`
} `json:"request"`
}
type AdmissionResponse struct {
UID string `json:"uid"`
Allowed bool `json:"allowed"`
Result struct {
Message string `json:"message"`
} `json:"status"`
}
func validate(w http.ResponseWriter, r *http.Request) {
var admissionReview AdmissionReview
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&admissionReview); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// 默认 AdmissionResponse
response := AdmissionResponse{
UID: admissionReview.Request.Kind,
Allowed: true,
}
// 检查容器镜像
for _, container := range admissionReview.Request.Object.Spec.Containers {
if strings.Contains(container.Image, "nginx") {
// 如果镜像是 nginx,拒绝请求
response.Allowed = false
response.Result.Message = fmt.Sprintf("Forbidden image: %s", container.Image)
break
}
}
// 将结果返回给 Kubernetes API Server
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(&response); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func main() {
http.HandleFunc("/validate", validate)
port := "443" // Webhook 通常监听 HTTPS 端口
log.Printf("Starting Webhook server on port %s", port)
log.Fatal(http.ListenAndServeTLS(":"+port, "/etc/webhook/cert/cert.pem", "/etc/webhook/cert/key.pem", nil))
}
初始化go
# go mod init image-policy-webhook
初始之后会在当前目录生成以下文件
# cat go.mod
module image-policy-webhook
go 1.22.7
自建证书
# openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=webhook.default.svc"
在当前目录生成key.pem和cert.pem证书
编写Dockerfile文件
# cat Dockerfile
# 基础镜像使用 golang
FROM golang:1.22-alpine AS builder
# 设置工作目录
WORKDIR /app
# 将本地代码复制到容器中
COPY . .
RUN mkdir -p /etc/webhook/cert
ADD key.pem /etc/webhook/cert/
ADD cert.pem /etc/webhook/cert/
# 编译 Go 程序
RUN go build -o webhook .
# 使用更小的基础镜像
FROM alpine:latest
RUN mkdir -p /etc/webhook/cert
ADD key.pem /etc/webhook/cert/
ADD cert.pem /etc/webhook/cert/
# 安装所需的 CA 证书等依赖
RUN apk add --no-cache ca-certificates
# 复制编译好的程序
COPY --from=builder /app/webhook /webhook
# 配置容器启动命令
CMD ["/webhook"]
打包镜像推送至私有仓库
# docker build -t you-harbor-url/image-policy-webhook .
# docker push you-harbor-url/image-policy-webhook .
部署deployment
# cat webhook-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: image-policy-webhook
spec:
replicas: 1
selector:
matchLabels:
app: image-policy-webhook
template:
metadata:
labels:
app: image-policy-webhook
spec:
containers:
- name: image-policy-webhook
image: your-harbor-url/image-policy-webhook:latest
ports:
- containerPort: 443
---
apiVersion: v1
kind: Service
metadata:
name: image-policy-webhook
spec:
ports:
- port: 443
targetPort: 443
selector:
app: image-policy-webhook
部署
# kubectl apply -f webhook-deployment.yaml
部署ValidatingWebhookConfiguration
# cat image-policy-webhook-configuration.yaml
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: image-policy-webhook
webhooks:
- name: imagepolicy.webhook.example.com
clientConfig:
service:
name: image-policy-webhook
namespace: default
path: "/validate/images"
caBundle:
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
admissionReviewVersions: ["v1"]
sideEffects: None
部署
# kubectl apply -f image-policy-webhook-configuration.yaml
部署完毕之后使用以下Pod测试
# cat nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: nginx
image: nginx:latest
部署拒绝提示如下
# kubectl apply -f nginx-pod.yaml
Error from server (InternalError): error when creating "nginx-pod.yaml": Internal error occurred: failed calling webhook "imagepolicy.webhook.example.com": failed to call webhook: Post "https://image-policy-webhook.default.svc:443/validate/images?timeout=10s": dial tcp 10.43.55.46:443: connect: connection refused
注意:
标签:ImagePolicyWebhook,image,webhook,cert,nginx,json,集群,policy,K8S From: https://www.cnblogs.com/minseo/p/18561167k8s集群版本需要1.19以上,在1.13版本测试无法部署
部署限制执行已经调度的nginx镜像不受影响