k8s and Admission--webhook admission

Keywords: Go JSON Kubernetes network encoding

Preface

Kubernetes provides three security access control measures for API access: authentication, authorization and Admission Control. Authentication solves the problem of who the user is, and authorization solves the problem of what the user can do. Admission Control is the role of resource management. Reasonable authority management can ensure the safety and reliability of the system.

This paper mainly talks about Validating Admission Web hook and Matting Admission Web hook in Admission.

AdmissionWebhook

We know that k8s has scalability in all aspects, such as the implementation of a variety of network models through cni, a variety of storage engines through csi, a variety of container runtime through cri and so on. Admission webhook is another extensible tool. In addition to the compiled Admission plug-ins, you can develop your own Admission plug-ins as extensions and configure them as webhooks at runtime.

Admission webhooks are HTTP callbacks that receive Admission requests and do something to them. You can define two types of Admission webhooks, Validating Admission webhook and Matting Admission webhook.

If Mutating Admission is enabled, when you start creating a k8s resource object, the creation request will be sent to the controller you write, and then we can do a series of operations. For example, in our scenario, we will do some functional enhancements in a unified way. When business development creates a new deployment, we will perform some injected operations, such as sensitive information aksk, or some optimized init scripts.

Similarly, Validating Admission Web hook allows the creation of resources according to your custom logic. For example, in the actual production of k8s cluster, we are in the stability consideration, we require that the deployment created must set request and limit.

How to Realize Your Admission Webhook Server

Prerequisite

  • k8s version requires at least v1.9
  • Ensure that Mutating Admission Webhook and Validating Admission Webhook admission controllers are enabled
  • Make sure admission registration. k8s. IO / v1beta1 is enabled

Write an admission webhook server

Officially provided a demo This is the case. You can study it in detail. The core idea is:
webhook handles the AdmissionReview request sent by apiservers and sends back its decision as an AdmissionReview object.

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("", "")
}

Dynamic configuration of admission webhooks

You can go through the ValidatingWebhookConfiguration or MutatingWebhookConfiguration Dynamic configuration of which resources are restricted by the entry webhooks.

Specific examples are as follows:

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

summary

Finally, we summarize the advantages of webhook Admission:

  • Web hook can dynamically extend Admission capabilities to meet the needs of custom customers
  • Without restarting API Server, you can hot-load webhook admission by creating webhook configuration

Posted by miccomte on Mon, 22 Jul 2019 02:29:24 -0700