Go使用k8s准入控制器来拒绝Latest镜像标签的yaml创建
文章目录准入控制器注册准入控制器 webhook创建准入控制器服务器部署和测试结语参考链接准入控制器简而言之,Kubernetes准入控制器是管理和强制执行集群使用方式的插件。它们可以被认为是拦截(经过身份验证的)API 请求并可能更改请求对象或完全拒绝请求的看门人。准入控制过程有两个阶段:首先执行变更阶段,然后是验证阶段。Kubernetes 准入控制器阶段:准入控制器是一个软件,它在对象(如
准入控制器
简而言之,Kubernetes准入控制器是管理和强制执行集群使用方式的插件。它们可以被认为是拦截(经过身份验证的)API 请求并可能更改请求对象或完全拒绝请求的看门人。准入控制过程有两个阶段:首先执行变更阶段,然后是验证阶段。
Kubernetes 准入控制器阶段:
准入控制器是一个软件,它在对象(如 Pod
、Deployment
、Service
等 k8s
资源) etcd
数据库中持久化之前,但在请求被认证和授权之后,拦截对 Kubernetes API
服务器的请求. 使用准入控制器,我们可以验证或“改变”传入请求的资源。例如,想象以下用例:
- 应用一个简单的安全验证,即集群中的所有容器都不能使用该
latest
标签。 - 可能想要设置一些默认值,例如
annotations
或labels
在您部署的每个资源中。
Kubernetes
中有两种类型的准入控制器。他们正在验证准入控制器和变异准入控制器。变异准入控制器首先被调用并且可以“修改”对象。在所有对象修改完成后,在传入对象被 API
服务器验证后,验证准入控制器被调用。可以拒绝执行自定义策略的请求。
注意:我将“修改”和“变异”这两个词放在引号中,因为我们并没有真正修改资源本身。我们使用 JSON Patch
格式告诉 Kubernetes
它应该对对象“K8s
资源”进行哪些修改。
变异准入控制器可以充当变异或验证控制器。它可以同时对请求执行两种操作。但是,请记住,变异准入控制器首先执行。为确保您将验证对象的最后状态,应该使用验证准入控制器。
注册准入控制器 webhook
创建了这个存储库,其中包含此示例的所有代码以及 Go GitHub
中准入控制器的简单样板。
https://github.com/douglasmakey/admissioncontroller
可以测试此示例的集群必须运行 Kubernetes 1.9.0 或更高版本。确保启用 admissionregistration.k8s.io/v1beta1 API
。可以使用以下命令进行验证:
[root@VM-2-29-centos 101]# kubectl api-versions |grep admissionreg
admissionregistration.k8s.io/v1
admissionregistration.k8s.io/v1beta1
应该检查MutatingAdmissionWebhook并ValidatingAdmissionWebhook在您的集群中激活检查kube-apiserver.
--enable-admission-plugins=..,MutatingAdmissionWebhook,ValidatingAdmissionWebhook.."
为了实现我们的准入控制器,Kubernetes API
服务器需要知道何时何地将传入的请求发送到我们的准入控制器。必须在 Kubernetes
中创建一个ValidatingWebhookConfiguration
或MutatingWebhookConfiguration
对象,这取决于我们想要什么。例如,以下配置是注册一个ValidatingWebhookConfiguration
以应用一些验证来创建一个Pod
.
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
name: pod-validation
webhooks:
- name: pod-validation.default.svc
clientConfig:
service:
name: admission-server
namespace: default
path: "/validate/pods"
caBundle: "${CA_BUNDLE}"
rules:
- operations: ["CREATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
这里的主要部分是:
clientConfig
:准入控制器服务器的配置。service.name
:service
准入控制器服务器的名称。service.namespace
: 准入控制器服务器在哪个命名空间中。service.path
:准入控制器将接收此webhook
请求的路径。rules
: 包含你要拦截的操作、资源、资源的api
版本和组operations
:您希望为这些资源拦截的操作。例如,CREATE
,DELETE
,UPDATED
resources
: 你想在这个webhook
上拦截的资源。例如,Pods
、Deployment
、Service
等。apiGroups
以及apiVersions
这些资源。
Kubernetes API
服务器向给定的服务和 URL
路径发出 HTTPS POST
请求。由于必须通过 HTTPS
提供 webhook
,因此我们需要为服务器提供适当的证书。这些证书可以是自签名的,但需要 Kubernetes
在与 webhook
服务器通信时指示相应的 CA
证书。为此,您可以caBundle
在配置中看到 。
在 GitHub
存储库中,您将找到demo/deploy.sh
将创建自签名证书并demo/webhooks.yaml
为此示例创建 webhook
的脚本。要在生产中正确管理您的证书,您可以使用类似https://cert-manager.io
的东西。
创建准入控制器服务器
注意:这里的所有代码示例都经过简化,以便于阅读。如需完整实施,请访问存储库。
https://github.com/douglasmakey/admissioncontroller
开始创建一个简单的 HTTPS。它应该具有我们为 admission webhook 定义的路径的端点。
// http/server.go
func NewServer(port string) *http.Server {
// Instances hooks
podsValidation := pods.NewValidationHook()
// Routers
ah := newAdmissionHandler()
mux := http.NewServeMux()
mux.Handle("/validate/pods", ah.Serve(podsValidation))
return &http.Server{
Addr: fmt.Sprintf(":%s", port),
Handler: mux,
}
}
// cmd/main.go
func main() {
// flags
// ...
server := http.NewServer(port)
if err := server.ListenAndServeTLS(tlscert, tlskey); err != nil{
log.Errorf("Failed to listen and serve: %v", err)
}
}
然后必须创建admissionHandler
以接收来自我们的 webhook
的所有请求。这些请求在请求正文中带有 JSON
编码的AdmissionReview(
请求字段已填写)。响应应该是填写了响应字段的 JSON AdmissionReview
。
// http/handlers.go
type admissionHandler struct {...}
// Serve returns a http.HandlerFunc for an admission webhook
func (h *admissionHandler) Serve(hook admissioncontroller.Hook) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// HTTP validations
// ...
body, err := io.ReadAll(r.Body)
if err != nil {...}
var review admission.AdmissionReview
if _, _, err := h.decoder.Decode(body, nil, &review); err != nil {...}
result, err := hook.Execute(review.Request)
if err != nil {...}
admissionResponse := v1beta1.AdmissionReview{
Response: &v1beta1.AdmissionResponse{
UID: review.Request.UID,
Allowed: result.Allowed,
Result: &meta.Status{Message: result.Msg},
},
}
//...
res, err := json.Marshal(admissionResponse)
if err != nil {...}
w.WriteHeader(http.StatusOK)
w.Write(res)
}
}
正如上面的代码中注意到的那样,处理程序接收一个Hook
结构并调用其Execute
方法来处理请求。在Hook
结构中,可以AdmitFunc
为每个允许的操作注册一个。
// AdmitFunc defines how to process an admission request
type AdmitFunc func(request *admission.AdmissionRequest) (*Result, error)
// Hook represents the set of functions for each operation in an admission webhook.
type Hook struct {
Create AdmitFunc
Delete AdmitFunc
Update AdmitFunc
Connect AdmitFunc
}
// Execute evaluates the request and try to execute the function for operation specified in the request.
func (h *Hook) Execute(r *admission.AdmissionRequest) (*Result, error) {
switch r.Operation {
case admission.Create:
return wrapperExecution(h.Create, r)
.....
}
return &Result{Msg: fmt.Sprintf("Invalid operation: %s", r.Operation)}, nil
}
现在,只关注我们的业务逻辑。需要为注册的 webhook
编写代码。将为我们在资源中ValidatingWebhook
注册的CREATE
操作实现逻辑。我们想要验证 pod
的容器都不能使用latest
其镜像中的标签。
// pods/pods.go
// NewValidationHook creates a new instance of pods validation hook
func NewValidationHook() admissioncontroller.Hook {
return admissioncontroller.Hook{
Create: validateCreate(),
}
}
// validateImages validates that none of the containers use the `latest` tag.
func validateImages() admissioncontroller.AdmitFunc {
return func(r *v1beta1.AdmissionRequest) (*admissioncontroller.Result, error) {
pod, err := parsePod(r.Object.Raw)
if err != nil {
return &admissioncontroller.Result{Msg: err.Error()}, nil
}
for _, c := range pod.Spec.Containers {
if strings.HasSuffix(c.Image, ":latest") {
return &admissioncontroller.Result{Msg: "You cannot use the tag 'latest' in a container."}, nil
}
}
return &admissioncontroller.Result{Allowed: true}, nil
}
}
当然,这是一个非常基本的示例,可以根据用例实现更复杂的验证。例如,在存储库上,我们有另一个有趣的例子。使用MutattingWebhookJSON
补丁,我们将一个容器作为 sidecar
注入到我们的 pod
中。
注意:Istio使用类似的方法来注入其 sidecar容器。
func mutateCreate() admissioncontroller.AdmitFunc {
return func(r *v1beta1.AdmissionRequest) (*admissioncontroller.Result, error) {
var operations []admissioncontroller.PatchOperation
pod, err := parsePod(r.Object.Raw)
if err != nil {
return &admissioncontroller.Result{Msg: err.Error()}, nil
}
// Very simple logic to inject a new "sidecar" container.
if pod.Namespace == "special" {
var containers []v1.Container
containers = append(containers, pod.Spec.Containers...)
sideC := v1.Container{
Name: "test-sidecar",
Image: "busybox:stable",
Command: []string{"sh", "-c", "while true; do echo 'I am a container injected by mutating webhook'; sleep 2; done"},
}
containers = append(containers, sideC)
operations = append(operations, admissioncontroller.ReplacePatchOperation("/spec/containers", containers))
}
return &admissioncontroller.Result{
Allowed: true,
PatchOps: operations,
}, nil
}
}
部署和测试
Rundemo/deploy.sh
将为服务器和 webhook
创建一个自签名 CA
、证书和私有 CA
。它还将创建以下资源:
Secret TLS
- 准入服务器的部署使用
demo/deployment.yaml
- 我们的
admission service
- 所有的
Admission webhooks
注意:demo/deploy.sh
仅用于开发/测试环境。它不是用于生产的。您可以看到所有创建的资源:
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
admission-server ClusterIP 10.43.120.27 <none> 443/TCP 1h
kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
admission-server 1/1 0 1 1h
kubectl get secret
NAME TYPE DATA AGE
admission-tls kubernetes.io/tls 2 1h
kubectl get mutatingwebhookconfigurations
NAME WEBHOOKS AGE
pod-mutation 1 1h
kubectl get validatingwebhookconfigurations
NAME WEBHOOKS AGE
deployment-validation 1 1h
pod-validation 1 1h
现在,我们可以测试我们的 webhook
。如果我们尝试Pod
使用以下清单创建一个将失败。
# demo/pods/01_fail_pod_creation_test.yaml
apiVersion: v1
kind: Pod
metadata:
name: webserver
spec:
containers:
- name: webserver
image: nginx:latest
ports:
- containerPort: 80
注意:您可以在内部使用不同的清单demo/pods
并demo/deployments
测试验证和突变。
kubectl create -f pods/01_fail_pod_creation_test.yaml
Error from server: error when creating "pods/01_fail_pod_creation_test.yaml": admission webhook "pod-validation.default.svc" denied the request: You cannot use the tag 'latest' in a container.
结语
如上所见,准入控制器是一个强大的功能,它允许我们为 k8s 资源实现自定义规则和默认值。这是您可以扩展 k8s 行为的一种方式。我希望你喜欢这篇文章,任何反馈都会得到很好的接受。
参考链接
https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers/#why-do-i-need-admission-controllers
https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers
https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/
https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/
http://jsonpatch.com
https://douglasmakey.medium.com/implementing-a-simple-k8s-admission-controller-in-go-87ae84408cb2
更多推荐
所有评论(0)