slackhq/simple-kubernetes-webhook

GitHub: slackhq/simple-kubernetes-webhook

由 Slack 开源的一个极简版 Kubernetes 准入 Webhook 教学项目,旨在不依赖任何框架和脚手架的前提下,展示如何使用纯 Go 语言构建完整的校验与变更准入控制器。

Stars: 205 | Forks: 103

# simple-kubernetes-webhook 这是一个简单的 [Kubernetes admission webhook](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/)。它仅用作验证和变更 admission webhook,不支持任何 controller 逻辑。它被开发为一个简单的 Go web 服务,没有使用任何框架或样板代码(如 kubebuilder)。 本项目旨在以最简单的方式说明如何构建一个功能完备的 admission webhook。网上现有的大多数示例都依赖于使用强大框架的复杂机制,却未能说明如何实现一个轻量级的 webhook 来执行一些非常必要的操作,例如出于合规性原因拒绝 pod,或注入有用的环境变量。 为了提高可读性,本项目去除了常规的生产环境要素,例如:可观测性检测、发布脚本、冗余的部署配置等。因此,它并不打算在生产环境中按原样使用。实际上,本项目是 Slack 所有 Kubernetes 生产环境中使用的某个系统的简化版分支。 ## 安装 本项目可以完全在本地运行,并包含用于部署本地 Kubernetes 集群的自动化脚本(使用 Kind)。 ### 要求 * Docker * kubectl * [Kind](https://kind.sigs.k8s.io/docs/user/quick-start/#installation) * Go >=1.16(可选) ## 使用方法 ### 创建集群 首先,我们需要创建一个 Kubernetes 集群: ``` ❯ make cluster 🔧 Creating Kubernetes cluster... kind create cluster --config dev/manifests/kind/kind.cluster.yaml Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.21.1) 🖼 ✓ Preparing nodes 📦 ✓ Writing configuration 📜 ✓ Starting control-plane 🕹️ ✓ Installing CNI 🔌 ✓ Installing StorageClass 💾 Set kubectl context to "kind-kind" You can now use your cluster with: kubectl cluster-info --context kind-kind Have a nice day! 👋 ``` 确保 Kubernetes 节点已就绪: ``` ❯ kubectl get nodes NAME STATUS ROLES AGE VERSION kind-control-plane Ready control-plane,master 3m25s v1.21.1 ``` 并确保系统 pod 正常运行: ``` ❯ kubectl -n kube-system get pods NAME READY STATUS RESTARTS AGE coredns-558bd4d5db-thwvj 1/1 Running 0 3m39s coredns-558bd4d5db-w85ks 1/1 Running 0 3m39s etcd-kind-control-plane 1/1 Running 0 3m56s kindnet-84slq 1/1 Running 0 3m40s kube-apiserver-kind-control-plane 1/1 Running 0 3m54s kube-controller-manager-kind-control-plane 1/1 Running 0 3m56s kube-proxy-4h6sj 1/1 Running 0 3m40s kube-scheduler-kind-control-plane 1/1 Running 0 3m54s ``` ### 部署 Admission Webhook 要配置集群以使用 admission webhook 并部署该 webhook,只需运行: ``` ❯ make deploy 📦 Building simple-kubernetes-webhook Docker image... docker build -t simple-kubernetes-webhook:latest . [+] Building 14.3s (13/13) FINISHED ... 📦 Pushing admission-webhook image into Kind's Docker daemon... kind load docker-image simple-kubernetes-webhook:latest Image: "simple-kubernetes-webhook:latest" with ID "sha256:46b8603bcc11a8fa1825190d3ed99c099096395b22a709e13ec6e7ae2f54014d" not yet present on node "kind-control-plane", loading... ⚙️ Applying cluster config... kubectl apply -f dev/manifests/cluster-config/ namespace/apps created mutatingwebhookconfiguration.admissionregistration.k8s.io/simple-kubernetes-webhook.acme.com created validatingwebhookconfiguration.admissionregistration.k8s.io/simple-kubernetes-webhook.acme.com created 🚀 Deploying simple-kubernetes-webhook... kubectl apply -f dev/manifests/webhook/ deployment.apps/simple-kubernetes-webhook created service/simple-kubernetes-webhook created secret/simple-kubernetes-webhook-tls created ``` 然后,确保 admission webhook pod 正在运行(在 `default` 命名空间中): ``` ❯ kubectl get pods NAME READY STATUS RESTARTS AGE simple-kubernetes-webhook-77444566b7-wzwmx 1/1 Running 0 2m21s ``` 你可以流式传输其日志: ``` ❯ make logs 🔍 Streaming simple-kubernetes-webhook logs... kubectl logs -l app=simple-kubernetes-webhook -f time="2021-09-03T04:59:10Z" level=info msg="Listening on port 443..." time="2021-09-03T05:02:21Z" level=debug msg=healthy uri=/health ``` 并从本地机器访问其健康检查 endpoint: ``` ❯ curl -k https://localhost:8443/health OK ``` ### 部署 pod 部署一个能被成功创建的有效测试 pod: ``` ❯ make pod 🚀 Deploying test pod... kubectl apply -f dev/manifests/pods/lifespan-seven.pod.yaml pod/lifespan-seven created ``` 你应该能在 admission webhook 的日志中看到该 pod 已被变更和验证。 部署一个会被拒绝的无效 pod: ``` ❯ make bad-pod 🚀 Deploying "bad" pod... kubectl apply -f dev/manifests/pods/bad-name.pod.yaml Error from server: error when creating "dev/manifests/pods/bad-name.pod.yaml": admission webhook "simple-kubernetes-webhook.acme.com" denied the request: pod name contains "offensive" ``` 你应该会在 admission webhook 的日志中看到 pod 验证失败。你可能还会看到该 pod 已被变更,因为 webhook 配置是无序的。 ## 测试 可以使用以下命令运行单元测试: ``` $ make test go test ./... ? github.com/slackhq/simple-kubernetes-webhook [no test files] ok github.com/slackhq/simple-kubernetes-webhook/pkg/admission 0.611s ok github.com/slackhq/simple-kubernetes-webhook/pkg/mutation 1.064s ok github.com/slackhq/simple-kubernetes-webhook/pkg/validation 0.749s ``` ## Admission 逻辑 一组验证和变更操作在一个可扩展的框架中实现。这些操作在部署 pod 时动态发生,不会进一步跟踪和更新资源(即没有 controller 逻辑)。 ### 验证 Webhook #### 已实现 - [名称验证](pkg/validation/name_validator.go):验证 pod 名称不包含任何冒犯性字符串 #### 如何添加新的 pod 验证 要添加新的 pod 变更,请创建文件 `pkg/validation/MUTATION_NAME.go`,然后创建一个实现 `validation.podValidator` 接口的新结构体。 ### 变更 Webhook #### 已实现 - [注入 env](pkg/mutation/inject_env.go):向 pod 注入环境变量,例如 `KUBE: true` - [最小 pod 生命周期](pkg/mutation/minimum_lifespan.go):注入一组 tolerations,用于将 pod 与特定年龄的节点匹配,注入的 tolerations 由 `acme.com/lifespan-requested` pod 标签控制。 #### 如何添加新的 pod 变更 要添加新的 pod 变更,请创建文件 `pkg/mutation/MUTATION_NAME.go`,然后创建一个实现 `mutation.podMutator` 接口的新结构体。
标签:Docker, EVTX分析, Go, Kind, Kubectl, pySHACL, Ruby工具, Slack开源, Webhook, 准入控制器, 力导向图, 动态注入, 子域名突变, 安全合规, 安全防御评估, 教程, 文本排版, 日志审计, 环境变量注入, 网络代理, 请求拦截, 轻量级应用