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, 准入控制器, 力导向图, 动态注入, 子域名突变, 安全合规, 安全防御评估, 教程, 文本排版, 日志审计, 环境变量注入, 网络代理, 请求拦截, 轻量级应用