preface
Kubernetes provides three security access control measures for API access: authentication, authorization and permission control. Authentication solves the problem of who the user is, authorization solves the problem of what the user can do, and permission control plays a role in resource management. Through reasonable authority management, the security and reliability of the system can be guaranteed.
This article mainly talks about ValidatingAdmissionWebhook and MutatingAdmissionWebhook in advertising.
AdmissionWebhook
We know that k8s has scalability in all aspects, such as implementing a variety of network models through cni, implementing a variety of storage engines through csi, implementing a variety of container runtime through cri, and so on. The Admission Web hook is another extensible means. In addition to the compiled permission plug-in, you can develop your own permission plug-in as an extension and configure it as webhook at runtime.
Permission webhooks are HTTP callbacks that receive permission requests and do something about them. You can define two types of Admission webhook, ValidatingAdmissionWebhook and MutatingAdmissionWebhook.
If MutatingAdmission is enabled, when 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 uniformly make some functional enhancements. When business development creates a new deployment, we will perform some injection operations, such as sensitive information aksk or some optimized init scripts.
Similar to this, the only thing is that ValidatingAdmissionWebhook is whether to allow the creation of resources according to your custom logic. For example, in the actual production k8s cluster, we need to set request and limit for the deployment created in consideration of stability.
How to implement your own AdmissionWebhook Server
prerequisite
- The k8s version needs at least v1.9
- Ensure that MutatingAdmissionWebhook and ValidatingAdmissionWebhook admission controllers are enabled
- Are you sure you have enabled http://admissionregistration.k8s.io/v1beta1
Write an admission webhook server
There is an official offer demo . You can study it in detail. The core idea is:
webhook processes the AdmissionReview request sent by apiservers and sends its decision back 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("", "") }
Dynamically configure admission webhooks
You can ValidatingWebhookConfiguration or MutatingWebhookConfiguration Dynamically configure which resources are limited by the portal 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, let's summarize the advantages of webhook Admission:
- webhook can dynamically expand the ability of advertisement to meet the needs of customized customers
- You do not need to restart the API Server. You can hot load webhook admission by creating a webhook configuration