summaryrefslogtreecommitdiffhomepage
path: root/webhook
diff options
context:
space:
mode:
Diffstat (limited to 'webhook')
-rw-r--r--webhook/main.go24
-rw-r--r--webhook/pkg/cli/cli.go115
-rw-r--r--webhook/pkg/cli/cli_state_autogen.go3
-rw-r--r--webhook/pkg/injector/certs.go97
-rw-r--r--webhook/pkg/injector/injector_state_autogen.go3
-rw-r--r--webhook/pkg/injector/webhook.go211
6 files changed, 453 insertions, 0 deletions
diff --git a/webhook/main.go b/webhook/main.go
new file mode 100644
index 000000000..220016543
--- /dev/null
+++ b/webhook/main.go
@@ -0,0 +1,24 @@
+// Copyright 2020 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Binary main serves a mutating Kubernetes webhook.
+package main
+
+import (
+ "gvisor.dev/gvisor/webhook/pkg/cli"
+)
+
+func main() {
+ cli.Main()
+}
diff --git a/webhook/pkg/cli/cli.go b/webhook/pkg/cli/cli.go
new file mode 100644
index 000000000..a07d341a2
--- /dev/null
+++ b/webhook/pkg/cli/cli.go
@@ -0,0 +1,115 @@
+// Copyright 2020 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package cli provides a CLI interface for a mutating Kubernetes webhook.
+package cli
+
+import (
+ "flag"
+ "fmt"
+ "net"
+ "net/http"
+ "os"
+ "strconv"
+ "strings"
+
+ "gvisor.dev/gvisor/pkg/log"
+ "gvisor.dev/gvisor/webhook/pkg/injector"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ k8snet "k8s.io/apimachinery/pkg/util/net"
+ "k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/rest"
+)
+
+var (
+ address = flag.String("address", "", "The ip address the admission webhook serves on. If unspecified, a public address is selected automatically.")
+ port = flag.Int("port", 0, "The port the admission webhook serves on.")
+ podLabels = flag.String("pod-namespace-labels", "", "A comma-separated namespace label selector, the admission webhook will only take effect on pods in selected namespaces, e.g. `label1,label2`.")
+)
+
+// Main runs the webhook.
+func Main() {
+ flag.Parse()
+
+ if err := run(); err != nil {
+ log.Warningf("%v", err)
+ os.Exit(1)
+ }
+}
+
+func run() error {
+ log.Infof("Starting %s\n", injector.Name)
+
+ // Create client config.
+ cfg, err := rest.InClusterConfig()
+ if err != nil {
+ return fmt.Errorf("create in cluster config: %w", err)
+ }
+
+ // Create clientset.
+ clientset, err := kubernetes.NewForConfig(cfg)
+ if err != nil {
+ return fmt.Errorf("create kubernetes client: %w", err)
+ }
+
+ if err := injector.CreateConfiguration(clientset, parsePodLabels()); err != nil {
+ return fmt.Errorf("create webhook configuration: %w", err)
+ }
+
+ if err := startWebhookHTTPS(clientset); err != nil {
+ return fmt.Errorf("start webhook https server: %w", err)
+ }
+
+ return nil
+}
+
+func parsePodLabels() *metav1.LabelSelector {
+ rv := &metav1.LabelSelector{}
+ for _, s := range strings.Split(*podLabels, ",") {
+ req := metav1.LabelSelectorRequirement{
+ Key: strings.TrimSpace(s),
+ Operator: "Exists",
+ }
+ rv.MatchExpressions = append(rv.MatchExpressions, req)
+ }
+ return rv
+}
+
+func startWebhookHTTPS(clientset kubernetes.Interface) error {
+ log.Infof("Starting HTTPS handler")
+ defer log.Infof("Stopping HTTPS handler")
+
+ if *address == "" {
+ ip, err := k8snet.ChooseHostInterface()
+ if err != nil {
+ return fmt.Errorf("select ip address: %w", err)
+ }
+ *address = ip.String()
+ }
+ mux := http.NewServeMux()
+ mux.Handle("/", http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ injector.Admit(w, r)
+ }))
+ server := &http.Server{
+ // Listen on all addresses.
+ Addr: net.JoinHostPort(*address, strconv.Itoa(*port)),
+ TLSConfig: injector.GetTLSConfig(),
+ Handler: mux,
+ }
+ if err := server.ListenAndServeTLS("", ""); err != http.ErrServerClosed {
+ return fmt.Errorf("start HTTPS handler: %w", err)
+ }
+ return nil
+}
diff --git a/webhook/pkg/cli/cli_state_autogen.go b/webhook/pkg/cli/cli_state_autogen.go
new file mode 100644
index 000000000..e81991e0b
--- /dev/null
+++ b/webhook/pkg/cli/cli_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package cli
diff --git a/webhook/pkg/injector/certs.go b/webhook/pkg/injector/certs.go
new file mode 100644
index 000000000..d237d5354
--- /dev/null
+++ b/webhook/pkg/injector/certs.go
@@ -0,0 +1,97 @@
+package injector
+
+// This file was generated using openssl by the gencerts.sh script.
+
+var caKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAzSQsvx5GdE42kwGrZO1WgEGTsR42S+9/BHi0cyElEpgyGwHt
+smJdmU1eDed73+Hy23Yy+Pz/WgaroinE65WrV3CeNKCYpvz4EqXF5R4VfGuXaBu7
+79+B98yH6L2UK18MNsTUcrQEvT1lJYHH6s/Zoc0kJFR9j956MPbjc3evKpREEOEO
+NYM0SJaUSJgoxoGDxB1ym035m9kDtv5sfogW+jevwVncYPcGcBBl4W9ccdRYG+UW
+AR9ZqlW2EaluzxkEMunvVH+gDlH0ZBkpOnIyEIGo998C/xT5bXnb6BmcCu+f++gM
+h6lxpkj5FxBYClMfDIZ92lGJWOy3yGg8J3LxZwIDAQABAoIBAB2NRYjuspWUotYA
+mpE6g4iMadtND+NWiAS74rrnHnEUTbuIRpa5BuTLuW3lV/oDkbm9yFAIGjz80wLU
+Y5LQh9Nb3N6V+AeuT4pYKS79a3j2AuSzghpnJ1DsPPPxQ4QP+DF3n2c6uagNTSHf
+FU6lTKO7aqZ1KXVtRksBdfivWCOY4CAd76j/CExzNWxmWhJg2ds3aRsGeZrQxeWI
++OgHqo40qRNAtEqs1x1lLa3oLGM4KCVDAKeACMdFHhYfPLsdc3WciEnIS8eZinkW
+q9f+CyrwFJzUTc++NuNSDcQ+hmXIZlUv7rNMNVCOlEdFyxqMr3wa2ZkphRNaxVtf
+K9c++bkCgYEA9EujEEwJSDuDi6U2ceBVCoH8w2PopQqC28T5p+c9KQarsyNdFkqK
+bn0CzQCKJR5tjl7Ai/yv/nddSYDoDszYYifo5MiUPlGBrcRKqo0K7MN7NuYWWyVQ
+Ody9cLdYkjoc2oYkFgYo32EbQSUf7F/aotPBnXhBFMMioBx4sR5xzT0CgYEA1vhM
+quKmw3kJHOCPm9FZ1P04IX9fWQiwnz8Vz9Edd7P0WQewzsgOWEZ0Cd4bP/cOu7Vp
+JPF2XyZWV+yBoT1jFOBnyRuQHzmzO6AS6OYh9V004UYQtkZkxLrgILjy3BcV9gka
++wA3ek5mnKS9FIib1+D1h73EQO7XftfgRWrsq3MCgYASOlqOar3+j8I+9zLayFxQ
+DmbnxVqkheZBs67VImHj38WL9kWJ1kICAH3nAfVM07pk9xjy2QXgvNNPGrk18X7r
+xAKSn4zAIaDFcHIJy8BW7jcRX5Wnc19LEfdoo6WOM5vXik7C/e6qzDoWYEjDgFt1
+7srxjvl8LRs2SymOPbFMGQKBgFxxpM6r71kKOMABVeCFE+ODDVtiYgdwtDuXLnMT
+E2ABtCeBJiiWYYzWp1oC+Kb2QJC6P8ASUnwyiVkALPLA6lX09szGHKFA9/HBMcCU
+DrBsZ6wkrFUmSnlLf8yynEXHa7tFSSP9gN4Izxm3wlQNNy+L3yqDkdz2mRdEEH7p
+r2M/AoGAUsuARMasHYJJESeftpMKXrLb7bDmMC7xgCEpavUpjHRfF1r2aaHlpzGt
+H60/NFiSmSagMu+hZnplWIlrVMLwhq8+w+WRf00C3ZtiD2Ute/Gc+UZOVeO+4dqX
+nyi/78KzitotjV0rTmaT6GKvP1QFuMuVEQ98UWgPRQFjb7gwvaE=
+-----END RSA PRIVATE KEY-----`)
+
+var caCert = []byte(`-----BEGIN CERTIFICATE-----
+MIICqzCCAZMCFCdriGGzKjFvuwFvDkgT70JT+SuoMA0GCSqGSIb3DQEBCwUAMBEx
+DzANBgNVBAMMBmUyZV9jYTAgFw0yMDEwMzAxOTA1NDFaGA8yMjk0MDgxNTE5MDU0
+MVowETEPMA0GA1UEAwwGZTJlX2NhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAzSQsvx5GdE42kwGrZO1WgEGTsR42S+9/BHi0cyElEpgyGwHtsmJdmU1e
+Ded73+Hy23Yy+Pz/WgaroinE65WrV3CeNKCYpvz4EqXF5R4VfGuXaBu779+B98yH
+6L2UK18MNsTUcrQEvT1lJYHH6s/Zoc0kJFR9j956MPbjc3evKpREEOEONYM0SJaU
+SJgoxoGDxB1ym035m9kDtv5sfogW+jevwVncYPcGcBBl4W9ccdRYG+UWAR9ZqlW2
+EaluzxkEMunvVH+gDlH0ZBkpOnIyEIGo998C/xT5bXnb6BmcCu+f++gMh6lxpkj5
+FxBYClMfDIZ92lGJWOy3yGg8J3LxZwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBf
+SSBuUjYn/YrffCPsAmQgk9kIZgGg2ByE3A4veIS+rm8yiBhchpRWgRdJoDAqixKJ
+6J9Tw9vHZZlfE8lNDrgGCKoIBS1zxZDqY2PVsvE2X+L+q3+US+GuvCBLNdI8Etcx
+ElJ10F3UZtOKwCoAPvq7+jM/jGZs7Tp2qQPamS1l0ToghxLYagLNeXC3Id70CPVF
+rUS5Kk9HNiLUTmoRnePH9fE0kQl7DUTPictNMSCsThm9B9KBZGDFURUmLeSnmwzC
+0DwW4HtPfNShzGswxjhXd/WSvS4PwGevUJtIeEHjpFlU6gN+YmYhr3CxgqQJhC2F
+uA1eJQ0tw1wePtiULOdW
+-----END CERTIFICATE-----`)
+
+var serverKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAyyHmOyiRpsoGBSKgA6THeRDlqxJj3hNpkpQQZAw98+ZRbpJ+
+AigPJ3ju9/c28KwhxKb7bmSAk0JqlK/VCkO98ZqaUadYvkrjg5yncKqKs48Rx8ld
+KOJbTeZPCgwxuzpJguFpTU7gzeY2Ws623/j9VWwCjKOt27ggN1JXw0cgoRDS9Jgj
+9TluE1UNfPLty/F32HyWb3NCgjSRW//3Sj/vCAVnbwJop639+lXskjljA/Zcx6ju
+JvVF/mhLl58H22NTWz28+qdCxeCypCDLIXOSDuQIXTvmSU5MkHMAxxdZEagAP7ZW
+mk9RtCAAmFsSdY1Gs929kSsqNSw380kVoJFM7wIDAQABAoIBAGnK2fFti43mIYSD
+J/s90nWHC1YxWbnLOmyQmIjCp3FjPn3r5ZyR6HKSVULnicGGpH+ax9ASn7QSHxgZ
+C0cxSLeP4VctZNaWgi+FYt8Wsu4ZURVQFii42vyTOg7tkukDzk1PwFv2/LW/dAeP
+KUc0khvvCNTeWinYl064n+SgIqo+/w8kFNRf1FS1JbTbXcxy+dvRBbGhKZ+wcpEO
+qSBCFAbbA+M8e1eyu51TAvz3yxhil2csFaRJaLloXmsCB82aqditdo2mJolD8aIf
+pkz/HLxEDRDtte231rC7UrbeFP68wyfI688PudndAPnW8qm9SSonBp4yA6BjOEgA
+h/xewkECgYEA72o76NqLHaAemcZRQUh0m9lJtMdC6beaaAFyP6rc9g1T4pRbiXfB
+X4QVDcmH2qG8xcYTuPMXZJYMBwlGVKy7XQ9BCbGcKD/y818M7odQAgLUoxr9gtFO
+5a0RnaCpxEpW63RAjXghCX1KjD7Yt1P1hhHvmtsrqj2IGwZVxun7omcCgYEA2TQ7
+vWVZUM5YXX9cgFLCkJU+NekPWeUjP/5KcDKrF9+cDCwpiZ9c8tMlt4M4TCuFKha1
+H3qd2bTLks3D/jfJt7B3WdQX8VX/i81kroP1G54Ci4hs9X0dVwsWPQ/r076xdfbm
+tpAby2W79lLKHXGTh9CFRpVKwleIkSA/D352PDkCgYBIQzNj/BrDTWIPHgnGf50a
+sUIK+53Zt142iEE4sFTTO4CXQhpC6s+GCfLk33BO8ERvGXM4fr8P0C4/LXB5/Ezt
+ML57s40jpPGqvYTEtjjS8pHFzU65Xn3G2y8W+bhkE+AaX1Ngn+Kw341RuWJmK0RP
+PDiq7/5E+x+KsKXRTSxzfwKBgF6GXc1B0wnYkxo1eCMcYTIc0RMTFywvDRUnbGvB
+kTX1iWq+uWD8Kq4+d7aSc6iqc+xqL27ApPt+s+1ygO4chzvan0ZHiBfuLiVAQKW2
+JbBcJo1k1M5NIbykxYvTZvUikwZcafFfa8krwA4l33HK4MKFW8ro470J7RQDbY4n
+ofr5AoGBAKCpQC0Lue7PeH5aTBKFO4G3CpSutvjdH1A3kJ8NPsO1pfC0Ye4ep45n
+4X4NM1xLjS4h5YltrL2dTCBQcwZTHmOmmNAt5d349XclrYYwnBlOi8FV9QgsPglY
+hwbWUeiHC7gUdYXfkcXWKb7C84uXNJVy2u0YP0HQh2PA84XFzeET
+-----END RSA PRIVATE KEY-----`)
+
+var serverCert = []byte(`-----BEGIN CERTIFICATE-----
+MIIDDzCCAfegAwIBAgIUSwGRpMB/h++K/U2elCgWnjYl89MwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZTJlX2NhMCAXDTIwMTAzMDE5MDU0MVoYDzIyOTQwODE1
+MTkwNTQxWjA1MTMwMQYDVQQDDCpndmlzb3ItaW5qZWN0aW9uLWFkbWlzc2lvbi13
+ZWJob29rLmUyZS5zdmMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDL
+IeY7KJGmygYFIqADpMd5EOWrEmPeE2mSlBBkDD3z5lFukn4CKA8neO739zbwrCHE
+pvtuZICTQmqUr9UKQ73xmppRp1i+SuODnKdwqoqzjxHHyV0o4ltN5k8KDDG7OkmC
+4WlNTuDN5jZazrbf+P1VbAKMo63buCA3UlfDRyChENL0mCP1OW4TVQ188u3L8XfY
+fJZvc0KCNJFb//dKP+8IBWdvAminrf36VeySOWMD9lzHqO4m9UX+aEuXnwfbY1Nb
+Pbz6p0LF4LKkIMshc5IO5AhdO+ZJTkyQcwDHF1kRqAA/tlaaT1G0IACYWxJ1jUaz
+3b2RKyo1LDfzSRWgkUzvAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXg
+MB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOC
+AQEAlvnL1PjLGvv0IgMlWJVPj4c+g3jgg0j6fiK6+7oKSf2Y6wk/7DFRKL4yd23b
+BTOzBkAJ0i5J0BQxOSEX0I8qX5b7FxCxwkHVZue3h13//zeFbPqaywwk9uVEkhbR
+BKevOaSHjqcC7ci3W0ksl+icBEEmMYvgYqBIB5cZt/Dueam84a1voIZaa32rIOQe
+Sivy4NvBGryJPNHs2VmjNdA6g3Zre+tsymwlBLxN03RQ0VWZf+eE3yjuBnKBRyua
+rSf8GvwoJrWLl0Q71p8JGG8cPTjHmTwaR8D63yN2QTmINAfXS15zG/PuHsFQ9rpW
+JDNmoyI0GR9zyL1M1ZtIZpRuNQ==
+-----END CERTIFICATE-----`)
diff --git a/webhook/pkg/injector/injector_state_autogen.go b/webhook/pkg/injector/injector_state_autogen.go
new file mode 100644
index 000000000..2c994b7c9
--- /dev/null
+++ b/webhook/pkg/injector/injector_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package injector
diff --git a/webhook/pkg/injector/webhook.go b/webhook/pkg/injector/webhook.go
new file mode 100644
index 000000000..614b5add7
--- /dev/null
+++ b/webhook/pkg/injector/webhook.go
@@ -0,0 +1,211 @@
+// Copyright 2020 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package injector handles mutating webhook operations.
+package injector
+
+import (
+ "crypto/tls"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "os"
+
+ "github.com/mattbaird/jsonpatch"
+ "gvisor.dev/gvisor/pkg/log"
+ admv1beta1 "k8s.io/api/admission/v1beta1"
+ admregv1beta1 "k8s.io/api/admissionregistration/v1beta1"
+ v1 "k8s.io/api/core/v1"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ kubeclientset "k8s.io/client-go/kubernetes"
+)
+
+const (
+ // Name is the name of the admission webhook service. The admission
+ // webhook must be exposed in the following service; this is mainly for
+ // the server certificate.
+ Name = "gvisor-injection-admission-webhook"
+
+ // serviceNamespace is the namespace of the admission webhook service.
+ serviceNamespace = "e2e"
+
+ fullName = Name + "." + serviceNamespace + ".svc"
+)
+
+// CreateConfiguration creates MutatingWebhookConfiguration and registers the
+// webhook admission controller with the kube-apiserver. The webhook will only
+// take effect on pods in the namespaces selected by `podNsSelector`. If `podNsSelector`
+// is empty, the webhook will take effect on all pods.
+func CreateConfiguration(clientset kubeclientset.Interface, selector *metav1.LabelSelector) error {
+ fail := admregv1beta1.Fail
+
+ config := &admregv1beta1.MutatingWebhookConfiguration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: Name,
+ },
+ Webhooks: []admregv1beta1.MutatingWebhook{
+ {
+ Name: fullName,
+ ClientConfig: admregv1beta1.WebhookClientConfig{
+ Service: &admregv1beta1.ServiceReference{
+ Name: Name,
+ Namespace: serviceNamespace,
+ },
+ CABundle: caCert,
+ },
+ Rules: []admregv1beta1.RuleWithOperations{
+ {
+ Operations: []admregv1beta1.OperationType{
+ admregv1beta1.Create,
+ },
+ Rule: admregv1beta1.Rule{
+ APIGroups: []string{"*"},
+ APIVersions: []string{"*"},
+ Resources: []string{"pods"},
+ },
+ },
+ },
+ FailurePolicy: &fail,
+ NamespaceSelector: selector,
+ },
+ },
+ }
+ log.Infof("Creating MutatingWebhookConfiguration %q", config.Name)
+ if _, err := clientset.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(config); err != nil {
+ if !apierrors.IsAlreadyExists(err) {
+ return fmt.Errorf("failed to create MutatingWebhookConfiguration %q: %s", config.Name, err)
+ }
+ log.Infof("MutatingWebhookConfiguration %q already exists; use the existing one", config.Name)
+ }
+ return nil
+}
+
+// GetTLSConfig retrieves the CA cert that signed the cert used by the webhook.
+func GetTLSConfig() *tls.Config {
+ serverCert, err := tls.X509KeyPair(serverCert, serverKey)
+ if err != nil {
+ log.Warningf("Failed to generate X509 key pair: %v", err)
+ os.Exit(1)
+ }
+ return &tls.Config{
+ Certificates: []tls.Certificate{serverCert},
+ }
+}
+
+// Admit performs admission checks and mutations on Pods.
+func Admit(writer http.ResponseWriter, req *http.Request) {
+ review := &admv1beta1.AdmissionReview{}
+ if err := json.NewDecoder(req.Body).Decode(review); err != nil {
+ log.Infof("Failed with error (%v) to decode Admit request: %+v", err, *req)
+ writer.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ log.Debugf("admitPod: %+v", review)
+ var err error
+ review.Response, err = admitPod(review.Request)
+ if err != nil {
+ log.Warningf("admitPod failed: %v", err)
+ review.Response = &admv1beta1.AdmissionResponse{
+ Result: &metav1.Status{
+ Reason: metav1.StatusReasonInvalid,
+ Message: err.Error(),
+ },
+ }
+ sendResponse(writer, review)
+ return
+ }
+
+ log.Debugf("Processed admission review: %+v", review)
+ sendResponse(writer, review)
+}
+
+func sendResponse(writer http.ResponseWriter, response interface{}) {
+ b, err := json.Marshal(response)
+ if err != nil {
+ log.Warningf("Failed with error (%v) to marshal response: %+v", err, response)
+ writer.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+
+ writer.WriteHeader(http.StatusOK)
+ writer.Write(b)
+}
+
+func admitPod(req *admv1beta1.AdmissionRequest) (*admv1beta1.AdmissionResponse, error) {
+ // Verify that the request is indeed a Pod.
+ resource := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
+ if req.Resource != resource {
+ return nil, fmt.Errorf("unexpected resource %+v in pod admission", req.Resource)
+ }
+
+ // Decode the request into a Pod.
+ pod := &v1.Pod{}
+ if err := json.Unmarshal(req.Object.Raw, pod); err != nil {
+ return nil, fmt.Errorf("failed to decode pod object %s/%s", req.Namespace, req.Name)
+ }
+
+ // Copy first to change it.
+ podCopy := pod.DeepCopy()
+ updatePod(podCopy)
+ patch, err := createPatch(req.Object.Raw, podCopy)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create patch for pod %s/%s (generatedName: %s)", pod.Namespace, pod.Name, pod.GenerateName)
+ }
+
+ log.Debugf("Patched pod %s/%s (generateName: %s): %+v", pod.Namespace, pod.Name, pod.GenerateName, podCopy)
+ patchType := admv1beta1.PatchTypeJSONPatch
+ return &admv1beta1.AdmissionResponse{
+ Allowed: true,
+ Patch: patch,
+ PatchType: &patchType,
+ }, nil
+}
+
+func updatePod(pod *v1.Pod) {
+ gvisor := "gvisor"
+ pod.Spec.RuntimeClassName = &gvisor
+
+ // We don't run SELinux test for gvisor.
+ // If SELinuxOptions are specified, this is usually for volume test to pass
+ // on SELinux. This can be safely ignored.
+ if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.SELinuxOptions != nil {
+ pod.Spec.SecurityContext.SELinuxOptions = nil
+ }
+ for i := range pod.Spec.Containers {
+ c := &pod.Spec.Containers[i]
+ if c.SecurityContext != nil && c.SecurityContext.SELinuxOptions != nil {
+ c.SecurityContext.SELinuxOptions = nil
+ }
+ }
+ for i := range pod.Spec.InitContainers {
+ c := &pod.Spec.InitContainers[i]
+ if c.SecurityContext != nil && c.SecurityContext.SELinuxOptions != nil {
+ c.SecurityContext.SELinuxOptions = nil
+ }
+ }
+}
+
+func createPatch(old []byte, newObj interface{}) ([]byte, error) {
+ new, err := json.Marshal(newObj)
+ if err != nil {
+ return nil, err
+ }
+ patch, err := jsonpatch.CreatePatch(old, new)
+ if err != nil {
+ return nil, err
+ }
+ return json.Marshal(patch)
+}