31

k8s与Admission--webhook admission

 4 years ago
source link: https://www.tuicool.com/articles/UjANZjU
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

Kubernetes 对 API 访问提供了三种安全访问控制措施:认证、授权和 Admission Control。认证解决用户是谁的问题,授权解决用户能做什么的问题,Admission Control 则是资源管理方面的作用。通过合理的权限管理,能够保证系统的安全可靠。

bVbvopD?w=600&h=294

本文主要讲讲Admission中ValidatingAdmissionWebhook和MutatingAdmissionWebhook。

AdmissionWebhook

我们知道k8s在各个方面都具备可扩展性,比如通过cni实现多种网络模型,通过csi实现多种存储引擎,通过cri实现多种容器运行时等等。而AdmissionWebhook就是另外一种可扩展的手段。 除了已编译的Admission插件外,可以开发自己的Admission插件作为扩展,并在运行时配置为webhook。

Admission webhooks是HTTP回调,它接收Admission请求并对它们做一些事情。可以定义两种类型的Admission webhook,ValidatingAdmissionWebhook和MutatingAdmissionWebhook。

如果启用了MutatingAdmission,当开始创建一种k8s资源对象的时候,创建请求会发到你所编写的controller中,然后我们就可以做一系列的操作。比如我们的场景中,我们会统一做一些功能性增强,当业务开发创建了新的deployment,我们会执行一些注入的操作,比如敏感信息aksk,或是一些优化的init脚本。

而与此类似,只不过ValidatingAdmissionWebhook 是按照你自定义的逻辑是否允许资源的创建。比如,我们在实际生产k8s集群中,处于稳定性考虑,我们要求创建的deployment 必须设置request和limit。

如何实现自己的 AdmissionWebhook Server

前提条件

  • k8s版本需至少v1.9
  • 确保启用了 MutatingAdmissionWebhook and ValidatingAdmissionWebhook admission controllers
  • 确定 启用了admissionregistration.k8s.io/v1beta1

写一个 admission webhook server

官方提供了有个 demo 。大家可以详细研究,核心思想就是:

webhook处理apiservers发送的AdmissionReview请求,并将其决定作为AdmissionReview对象发送回去。

package main

import (
    "encoding/json"
    "flag"
    "fmt"
    "io/ioutil"
    "net/http"

    "k8s.io/api/admission/v1beta1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/klog"
    // TODO: try this library to see if it generates correct json patch
    // https://github.com/mattbaird/jsonpatch
)

// toAdmissionResponse is a helper function to create an AdmissionResponse
// with an embedded error
func toAdmissionResponse(err error) *v1beta1.AdmissionResponse {
    return &v1beta1.AdmissionResponse{
        Result: &metav1.Status{
            Message: err.Error(),
        },
    }
}

// admitFunc is the type we use for all of our validators and mutators
type admitFunc func(v1beta1.AdmissionReview) *v1beta1.AdmissionResponse

// serve handles the http portion of a request prior to handing to an admit
// function
func serve(w http.ResponseWriter, r *http.Request, admit admitFunc) {
    var body []byte
    if r.Body != nil {
        if data, err := ioutil.ReadAll(r.Body); err == nil {
            body = data
        }
    }

    // verify the content type is accurate
    contentType := r.Header.Get("Content-Type")
    if contentType != "application/json" {
        klog.Errorf("contentType=%s, expect application/json", contentType)
        return
    }

    klog.V(2).Info(fmt.Sprintf("handling request: %s", body))

    // The AdmissionReview that was sent to the webhook
    requestedAdmissionReview := v1beta1.AdmissionReview{}

    // The AdmissionReview that will be returned
    responseAdmissionReview := v1beta1.AdmissionReview{}

    deserializer := codecs.UniversalDeserializer()
    if _, _, err := deserializer.Decode(body, nil, &requestedAdmissionReview); err != nil {
        klog.Error(err)
        responseAdmissionReview.Response = toAdmissionResponse(err)
    } else {
        // pass to admitFunc
        responseAdmissionReview.Response = admit(requestedAdmissionReview)
    }

    // Return the same UID
    responseAdmissionReview.Response.UID = requestedAdmissionReview.Request.UID

    klog.V(2).Info(fmt.Sprintf("sending response: %v", responseAdmissionReview.Response))

    respBytes, err := json.Marshal(responseAdmissionReview)
    if err != nil {
        klog.Error(err)
    }
    if _, err := w.Write(respBytes); err != nil {
        klog.Error(err)
    }
}

func serveAlwaysDeny(w http.ResponseWriter, r *http.Request) {
    serve(w, r, alwaysDeny)
}

func serveAddLabel(w http.ResponseWriter, r *http.Request) {
    serve(w, r, addLabel)
}

func servePods(w http.ResponseWriter, r *http.Request) {
    serve(w, r, admitPods)
}

func serveAttachingPods(w http.ResponseWriter, r *http.Request) {
    serve(w, r, denySpecificAttachment)
}

func serveMutatePods(w http.ResponseWriter, r *http.Request) {
    serve(w, r, mutatePods)
}

func serveConfigmaps(w http.ResponseWriter, r *http.Request) {
    serve(w, r, admitConfigMaps)
}

func serveMutateConfigmaps(w http.ResponseWriter, r *http.Request) {
    serve(w, r, mutateConfigmaps)
}

func serveCustomResource(w http.ResponseWriter, r *http.Request) {
    serve(w, r, admitCustomResource)
}

func serveMutateCustomResource(w http.ResponseWriter, r *http.Request) {
    serve(w, r, mutateCustomResource)
}

func serveCRD(w http.ResponseWriter, r *http.Request) {
    serve(w, r, admitCRD)
}

func main() {
    var config Config
    config.addFlags()
    flag.Parse()

    http.HandleFunc("/always-deny", serveAlwaysDeny)
    http.HandleFunc("/add-label", serveAddLabel)
    http.HandleFunc("/pods", servePods)
    http.HandleFunc("/pods/attach", serveAttachingPods)
    http.HandleFunc("/mutating-pods", serveMutatePods)
    http.HandleFunc("/configmaps", serveConfigmaps)
    http.HandleFunc("/mutating-configmaps", serveMutateConfigmaps)
    http.HandleFunc("/custom-resource", serveCustomResource)
    http.HandleFunc("/mutating-custom-resource", serveMutateCustomResource)
    http.HandleFunc("/crd", serveCRD)
    server := &http.Server{
        Addr:      ":443",
        TLSConfig: configTLS(config),
    }
    server.ListenAndServeTLS("", "")
}

动态配置admission webhooks

您可以通过 ValidatingWebhookConfigurationMutatingWebhookConfiguration 动态配置哪些资源受入口webhooks的限制。

具体示例如下:

apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
  name: <name of this configuration object>
webhooks:
- name: <webhook name, e.g., pod-policy.example.io>
  rules:
  - apiGroups:
    - ""
    apiVersions:
    - v1
    operations:
    - CREATE
    resources:
    - pods
    scope: "Namespaced"
  clientConfig:
    service:
      namespace: <namespace of the front-end service>
      name: <name of the front-end service>
    caBundle: <pem encoded ca cert that signs the server cert used by the webhook>
  admissionReviewVersions:
  - v1beta1
  timeoutSeconds: 1

总结

最后我们来总结下 webhook Admission 的优势:

  • webhook 可动态扩展 Admission 能力,满足自定义客户的需求
  • 不需要重启 API Server,可通过创建 webhook configuration 热加载 webhook admission

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK