diff --git a/.gitignore b/.gitignore index 600d2d3..d2f09b6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.vscode \ No newline at end of file +.vscode +k8s/ingress.yaml \ No newline at end of file diff --git a/cmd/webhook/httpServer.go b/cmd/webhook/httpServer.go index 17fb378..b07a6c8 100644 --- a/cmd/webhook/httpServer.go +++ b/cmd/webhook/httpServer.go @@ -11,7 +11,6 @@ import ( "encoding/json" "net/http" - "mutating-webhook/internal/certificate" "mutating-webhook/internal/config" "mutating-webhook/internal/operations" @@ -37,16 +36,9 @@ func strictTransport(w http.ResponseWriter) { w.Header().Add("Strict-Transport-Security", "max-age=63072000") } -func httpServer() { - var serverCertificate tls.Certificate - if config.DefaultConfig().WebServerCertificate == "" || cfg.WebServerKey == "" { - log.Printf("[INFO] No webserver certificate configured, automatically generating self signed certificate.") - serverCertificate = certificate.CreateServerCert() - } else { - log.Fatal("[FATAL] Code to support external webserver certificate is not complete yet. ./cmd/webhook/httpServer.go:36") - // read certificate from files - // check for errors - } +func httpServer(cfg *config.Config) { + serverCertificate, _ := tls.X509KeyPair(append([]byte(cfg.CertCert), []byte(cfg.CACert)...), []byte(cfg.CertPrivateKey)) + path := http.NewServeMux() connection := &http.Server{ @@ -73,7 +65,7 @@ func httpServer() { ah := &admissionHandler{ decoder: serializer.NewCodecFactory(runtime.NewScheme()).UniversalDeserializer(), - config: &cfg, + config: cfg, } // pod admission diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go index 207e054..02c4b7b 100644 --- a/cmd/webhook/main.go +++ b/cmd/webhook/main.go @@ -29,7 +29,7 @@ func main() { // initialize application configuration cfg = config.Init() - go httpServer() + go httpServer(&cfg) forever() } diff --git a/config.yaml b/config.yaml index 042c025..9b00332 100644 --- a/config.yaml +++ b/config.yaml @@ -1,6 +1,6 @@ --- allow-admin-nomutate: false -allow-admin-nomutate-toggle: 2d77b689-dc14-40a5-8971-34c62999335c +allow-admin-nomutate-toggle: 7b068a99-c02b-410a-bd59-3514bac85e7a dockerhub-registry: registry.hub.docker.com mutate-ignored-images: - goharbor/chartmuseum-photon @@ -12,4 +12,4 @@ mutate-ignored-images: - goharbor/harbor-portal - goharbor/redis-photon - goharbor/registry-photon -- goharbor/trivy-adapter-photon \ No newline at end of file +- goharbor/trivy-adapter-photon diff --git a/internal/certificate/create-ca.go b/internal/certificate/create-ca.go index 96b6941..40255b4 100644 --- a/internal/certificate/create-ca.go +++ b/internal/certificate/create-ca.go @@ -2,18 +2,19 @@ package certificate import ( "bytes" + "fmt" + "log" "strconv" "time" "crypto/rand" - "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "math/big" ) -func CreateCA() ([]byte, []byte, []byte, error) { +func CreateCA(privateKey string) (string, error) { serial, _ := strconv.ParseInt(time.Now().Format("20060102150405"), 10, 64) ca := &x509.Certificate{ SerialNumber: big.NewInt(serial), @@ -37,14 +38,18 @@ func CreateCA() ([]byte, []byte, []byte, error) { SignatureAlgorithm: x509.SHA384WithRSA, } - keyPair, err := rsa.GenerateKey(rand.Reader, 4096) + pemKey, _ := pem.Decode([]byte(privateKey)) + if pemKey == nil || pemKey.Type != "RSA PRIVATE KEY" { + return "", fmt.Errorf("failed to decode PEM block containing private key") + } + keyPair, err := x509.ParsePKCS1PrivateKey(pemKey.Bytes) if err != nil { - return []byte(""), []byte(""), []byte(""), err + return "", err } certBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &keyPair.PublicKey, keyPair) if err != nil { - return []byte(""), []byte(""), []byte(""), err + return "", err } c := new(bytes.Buffer) @@ -53,17 +58,6 @@ func CreateCA() ([]byte, []byte, []byte, error) { Bytes: certBytes, }) - k := new(bytes.Buffer) - pem.Encode(k, &pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(keyPair), - }) - - p := new(bytes.Buffer) - pem.Encode(p, &pem.Block{ - Type: "PUBLIC KEY", - Bytes: x509.MarshalPKCS1PublicKey(&keyPair.PublicKey), - }) - - return c.Bytes(), k.Bytes(), p.Bytes(), nil + log.Printf("[DEBUG] Generated Certificate Authority Certificate:\n%s", c) + return c.String(), nil } diff --git a/internal/certificate/create-cert.go b/internal/certificate/create-cert.go deleted file mode 100644 index 14f6462..0000000 --- a/internal/certificate/create-cert.go +++ /dev/null @@ -1,72 +0,0 @@ -package certificate - -import ( - "bytes" - "strconv" - "time" - - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "math/big" -) - -func CreateCert() ([]byte, []byte, []byte, error) { - serial, _ := strconv.ParseInt(time.Now().Format("20060102150405"), 10, 64) - ca := &x509.Certificate{ - SerialNumber: big.NewInt(serial + 1), - Subject: pkix.Name{ - Organization: []string{"Kubernetes Mutating Webserver"}, - Country: []string{"K8S"}, - Province: []string{"Cluster Service"}, - Locality: []string{"Cluster Local"}, - //StreetAddress: []string{""}, - //PostalCode: []string{""}, - }, - NotBefore: time.Now(), - NotAfter: time.Now().AddDate(1, 6, 0), - ExtKeyUsage: []x509.ExtKeyUsage{ - x509.ExtKeyUsageClientAuth, - x509.ExtKeyUsageServerAuth, - }, - DNSNames: []string{ - "svc.cluster.local", - "*.svc.cluster.local", - }, - SubjectKeyId: []byte{1, 2, 3, 4, 6}, - KeyUsage: x509.KeyUsageDigitalSignature, - SignatureAlgorithm: x509.SHA384WithRSA, - } - - keyPair, err := rsa.GenerateKey(rand.Reader, 4096) - if err != nil { - return []byte(""), []byte(""), []byte(""), err - } - - certBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &keyPair.PublicKey, keyPair) - if err != nil { - return []byte(""), []byte(""), []byte(""), err - } - - c := new(bytes.Buffer) - pem.Encode(c, &pem.Block{ - Type: "CERTIFICATE", - Bytes: certBytes, - }) - - k := new(bytes.Buffer) - pem.Encode(k, &pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(keyPair), - }) - - p := new(bytes.Buffer) - pem.Encode(p, &pem.Block{ - Type: "PUBLIC KEY", - Bytes: x509.MarshalPKCS1PublicKey(&keyPair.PublicKey), - }) - - return c.Bytes(), k.Bytes(), p.Bytes(), nil -} diff --git a/internal/certificate/create-csr.go b/internal/certificate/create-csr.go new file mode 100644 index 0000000..63a74ea --- /dev/null +++ b/internal/certificate/create-csr.go @@ -0,0 +1,53 @@ +package certificate + +import ( + "bytes" + "fmt" + "log" + + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" +) + +func CreateCSR(privateKey string) (string, error) { + csr := x509.CertificateRequest{ + Subject: pkix.Name{ + Organization: []string{"Kubernetes Mutating Webserver"}, + Country: []string{"K8S"}, + Province: []string{"Cluster Service"}, + Locality: []string{"Cluster Local"}, + //StreetAddress: []string{""}, + //PostalCode: []string{""}, + }, + DNSNames: []string{ + "svc.cluster.local", + "*.svc.cluster.local", + }, + SignatureAlgorithm: x509.SHA384WithRSA, + } + + pemKey, _ := pem.Decode([]byte(privateKey)) + if pemKey == nil || pemKey.Type != "RSA PRIVATE KEY" { + return "", fmt.Errorf("failed to decode PEM block containing private key") + } + keyPair, err := x509.ParsePKCS1PrivateKey(pemKey.Bytes) + if err != nil { + return "", err + } + + csrData, err := x509.CreateCertificateRequest(rand.Reader, &csr, keyPair) + if err != nil { + return "", err + } + + c := new(bytes.Buffer) + pem.Encode(c, &pem.Block{ + Type: "CERTIFICATE REQUEST", + Bytes: csrData, + }) + + log.Printf("[TRACE] Generated Host CSR:\n%s", c.String()) + return c.String(), nil +} diff --git a/internal/certificate/create-key.go b/internal/certificate/create-key.go new file mode 100644 index 0000000..658a174 --- /dev/null +++ b/internal/certificate/create-key.go @@ -0,0 +1,15 @@ +package certificate + +import ( + "crypto/rand" + "crypto/rsa" +) + +func CreateRSAKeyPair(bytes int) (*rsa.PrivateKey, error) { + keyPair, err := rsa.GenerateKey(rand.Reader, bytes) + if err != nil { + return &rsa.PrivateKey{}, err + } + + return keyPair, nil +} diff --git a/internal/certificate/create-server-cert.go b/internal/certificate/create-server-cert.go deleted file mode 100644 index d533b3c..0000000 --- a/internal/certificate/create-server-cert.go +++ /dev/null @@ -1,43 +0,0 @@ -package certificate - -import ( - "bytes" - "log" - - "crypto/rand" - "crypto/tls" - "crypto/x509" - "encoding/pem" -) - -func CreateServerCert() tls.Certificate { - caCertPem, caPrivKeyPem, _, _ := CreateCA() - certCertPem, certPrivKeyPem, certPublicKeyPem, _ := CreateCert() - - caCertBlob, _ := pem.Decode(caCertPem) - caCert, _ := x509.ParseCertificate(caCertBlob.Bytes) - caPrivKeyBlob, _ := pem.Decode(caPrivKeyPem) - caPrivKey, _ := x509.ParsePKCS1PrivateKey(caPrivKeyBlob.Bytes) - certCertBlob, _ := pem.Decode(certCertPem) - certCert, _ := x509.ParseCertificate(certCertBlob.Bytes) - certPublicKeyBlob, _ := pem.Decode(certPublicKeyPem) - certPublicKey, _ := x509.ParsePKCS1PublicKey(certPublicKeyBlob.Bytes) - - signedCert, err := x509.CreateCertificate(rand.Reader, certCert, caCert, certPublicKey, caPrivKey) - if err != nil { - log.Fatalf("[FATAL] CreateCertificate: %v", err) - } - - serverCertPem := new(bytes.Buffer) - pem.Encode(serverCertPem, &pem.Block{ - Type: "CERTIFICATE", - Bytes: signedCert, - }) - - serverCert, err := tls.X509KeyPair(append(serverCertPem.Bytes(), caCertPem...), certPrivKeyPem) - if err != nil { - log.Fatalf("[FATAL] x509KeyPair: %v", err) - } - - return serverCert -} diff --git a/internal/certificate/sign-cert.go b/internal/certificate/sign-cert.go new file mode 100644 index 0000000..71bb65c --- /dev/null +++ b/internal/certificate/sign-cert.go @@ -0,0 +1,74 @@ +package certificate + +import ( + "bytes" + "fmt" + "strconv" + "time" + + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" +) + +func SignCert(caCertPem, caPrivKeyPem, csrPem string) (string, error) { + caCertData, _ := pem.Decode([]byte(caCertPem)) + caCert, err := x509.ParseCertificate(caCertData.Bytes) + if err != nil { + return "", fmt.Errorf("parse cert %v", err) + } + + pemKey, _ := pem.Decode([]byte(caPrivKeyPem)) + if pemKey == nil || pemKey.Type != "RSA PRIVATE KEY" { + return "", fmt.Errorf("failed to decode PEM block containing private key") + } + keyPair, err := x509.ParsePKCS1PrivateKey(pemKey.Bytes) + if err != nil { + return "", fmt.Errorf("private key %v", err) + } + + csrData, _ := pem.Decode([]byte(csrPem)) + csr, err := x509.ParseCertificateRequest(csrData.Bytes) + if err != nil { + return "", fmt.Errorf("parse csr %v", err) + } + + serial, _ := strconv.ParseInt(time.Now().Format("20060102150405"), 10, 64) + certTemplate := x509.Certificate{ + SerialNumber: big.NewInt(serial + 1), + Issuer: caCert.Issuer, + Subject: pkix.Name{ + Organization: []string{"Kubernetes Mutating Webserver"}, + Country: csr.Subject.Country, + Province: []string{"Cluster Service"}, + Locality: []string{"Cluster Local"}, + StreetAddress: csr.Subject.StreetAddress, + PostalCode: csr.Subject.PostalCode, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(1, 6, 0), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{ + x509.ExtKeyUsageClientAuth, + x509.ExtKeyUsageServerAuth, + }, + DNSNames: csr.DNSNames, + Signature: csr.Signature, + SignatureAlgorithm: csr.SignatureAlgorithm, + PublicKey: csr.PublicKey, + PublicKeyAlgorithm: csr.PublicKeyAlgorithm, + } + + certBytes, err := x509.CreateCertificate(rand.Reader, &certTemplate, caCert, csr.PublicKey, keyPair) + if err != nil { + return "", fmt.Errorf("sign %v", err) + } + c := new(bytes.Buffer) + pem.Encode(c, &pem.Block{ + Type: "CERTIFICATE", + Bytes: certBytes, + }) + return c.String(), nil +} diff --git a/internal/config/config.go b/internal/config/config.go index 1aef179..8cdbde3 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -35,9 +35,15 @@ type Config struct { // mutation configuration AllowAdminNoMutate bool `env:"allow_admin_nomutate" default:"false"` - AllowAdminNoMutateToggle string `env:"allow_admin_nomutate_toggle" default:"2d77b689-dc14-40a5-8971-34c62999335c"` + AllowAdminNoMutateToggle string `env:"allow_admin_nomutate_toggle" default:"7b068a99-c02b-410a-bd59-3514bac85e7a"` DockerhubRegistry string `env:"dockerhub_registry" default:"registry.hub.docker.com"` MutateIgnoredImages []string `ignored:"true"` + + // certificate configuration + CACert string `env:"ca_cert"` + CAPrivateKey string `env:"ca_private_key"` + CertCert string `env:"cert_cert"` + CertPrivateKey string `env:"cert_private_key"` } // DefaultConfig initializes the config variable for use with a prepared set of defaults. diff --git a/internal/config/configFile.go b/internal/config/configFile.go index 7ca43e4..d1bfff3 100644 --- a/internal/config/configFile.go +++ b/internal/config/configFile.go @@ -9,10 +9,18 @@ import ( ) type configFileStruct struct { - AllowAdminNoMutate bool `yaml:"allow-admin-nomutate"` - AllowAdminNoMutateToggle string `yaml:"allow-admin-nomutate-toggle"` - DockerhubRegistry string `yaml:"dockerhub-registry"` - MutateIgnoredImages []string `yaml:"mutate-ignored-images"` + AllowAdminNoMutate bool `yaml:"allow-admin-nomutate"` + AllowAdminNoMutateToggle string `yaml:"allow-admin-nomutate-toggle"` + DockerhubRegistry string `yaml:"dockerhub-registry"` + MutateIgnoredImages []string `yaml:"mutate-ignored-images"` + CertificateAuthority CertStruct `yaml:"certificate-authority"` + Certificate CertStruct `yaml:"certificate"` +} + +type CertStruct struct { + Certificate string `yaml:"certificate"` + PrivateKey string `yaml:"private-key"` + PublicKey string `yaml:"public-key"` } func getConfigFileData(fileLocation string) (configFileStruct, error) { diff --git a/internal/config/initialize.go b/internal/config/initialize.go index b0e594e..cd4753f 100644 --- a/internal/config/initialize.go +++ b/internal/config/initialize.go @@ -1,8 +1,13 @@ package config import ( + "bytes" + "crypto/x509" + "encoding/pem" "flag" + "fmt" "log" + "mutating-webhook/internal/certificate" "os" "reflect" "strings" @@ -70,25 +75,108 @@ func Init() Config { } time.Now().Format(cfg.TimeFormat) - // print running config - printRunningConfig(&cfg, cfgInfo) - // read config file configFileData, err := getConfigFileData(cfg.ConfigFile) if err != nil { log.Fatalf("[FATAL] Unable to read configuration file") } - if cfg.AllowAdminNoMutate == false { - cfg.AllowAdminNoMutate = configFileData.AllowAdminNoMutate + updateValues(&cfg, configFileData) + + // Generate certificates if needed + if err := certificateInit(&cfg); err != nil { + log.Fatalf("[FATAL] Unable to initialize certificate data: %v", err) } - if cfg.AllowAdminNoMutateToggle == "2d77b689-dc14-40a5-8971-34c62999335c" { - cfg.AllowAdminNoMutateToggle = configFileData.AllowAdminNoMutateToggle - } - if cfg.DockerhubRegistry == "registry.hub.docker.com" { - cfg.DockerhubRegistry = configFileData.DockerhubRegistry - } - cfg.MutateIgnoredImages = configFileData.MutateIgnoredImages + + // print running config + printRunningConfig(&cfg, cfgInfo) log.Println("[INFO] initialization sequence complete") return cfg } + +func updateValues(cfg *Config, configFileData configFileStruct) { + if cfg.AllowAdminNoMutate == false && configFileData.AllowAdminNoMutate != false { + cfg.AllowAdminNoMutate = configFileData.AllowAdminNoMutate + } + if cfg.AllowAdminNoMutateToggle == "7b068a99-c02b-410a-bd59-3514bac85e7a" && configFileData.AllowAdminNoMutateToggle != "2d77b689-dc14-40a5-8971-34c62999335c" { + cfg.AllowAdminNoMutateToggle = configFileData.AllowAdminNoMutateToggle + } + if cfg.DockerhubRegistry == "registry.hub.docker.com" && configFileData.DockerhubRegistry != "registry.hub.docker.com" { + cfg.DockerhubRegistry = configFileData.DockerhubRegistry + } + if len(configFileData.MutateIgnoredImages) != 0 { + cfg.MutateIgnoredImages = configFileData.MutateIgnoredImages + } + if len(configFileData.CertificateAuthority.Certificate) != 0 { + cfg.CACert = configFileData.CertificateAuthority.Certificate + } + if len(configFileData.CertificateAuthority.PrivateKey) != 0 { + cfg.CAPrivateKey = configFileData.CertificateAuthority.PrivateKey + } + if len(configFileData.Certificate.Certificate) != 0 { + cfg.CertCert = configFileData.Certificate.Certificate + } + if len(configFileData.Certificate.PrivateKey) != 0 { + cfg.CertPrivateKey = configFileData.Certificate.PrivateKey + } +} + +func certificateInit(cfg *Config) error { + // certificate authority private key does not exist, generate key pair + if len(cfg.CAPrivateKey) == 0 { + log.Printf("[TRACE] No certificate authority private key detected") + keyPair, err := certificate.CreateRSAKeyPair(4096) + if err != nil { + return fmt.Errorf("Create RSA Key (%v)", err) + } + // pem encode private key + k := new(bytes.Buffer) + pem.Encode(k, &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(keyPair), + }) + cfg.CAPrivateKey = k.String() + } + + // certificate authority certificate is missing, create it + if len(cfg.CACert) == 0 { + log.Printf("[TRACE] No certificate authority certificate detected") + caCert, err := certificate.CreateCA(cfg.CAPrivateKey) + if err != nil { + return fmt.Errorf("Create CA (%v)", err) + } + cfg.CACert = caCert + } + + // certificate private key does not exist, generate key pair + if len(cfg.CertPrivateKey) == 0 { + log.Printf("[TRACE] No server private key detected") + keyPair, err := certificate.CreateRSAKeyPair(4096) + if err != nil { + return fmt.Errorf("Create RSA Key (%v)", err) + } + // pem encode private key + k := new(bytes.Buffer) + pem.Encode(k, &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(keyPair), + }) + cfg.CertPrivateKey = k.String() + } + + // certificate certificate is missing, create it + if len(cfg.CertCert) == 0 { + log.Printf("[TRACE] No server certificate detected") + csr, err := certificate.CreateCSR(cfg.CertPrivateKey) + if err != nil { + return fmt.Errorf("Create CSR (%v)", err) + } + cert, err := certificate.SignCert(cfg.CACert, cfg.CAPrivateKey, csr) + if err != nil { + return fmt.Errorf("Sign Cert (%v)", err) + } + cfg.CertCert = cert + } + + return nil +} diff --git a/k8s/admission.yaml b/k8s/admission.yaml new file mode 100644 index 0000000..68ba5aa --- /dev/null +++ b/k8s/admission.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + name: deployment-validation +webhooks: + - name: deployment-validation.default.svc + clientConfig: + service: + name: admission-server + namespace: default + path: "/validate/deployments" + caBundle: "${CA_BUNDLE}" + rules: + - operations: ["CREATE","DELETE"] + apiGroups: ["apps"] + apiVersions: ["v1"] + resources: ["deployments"] + failurePolicy: Ignore \ No newline at end of file diff --git a/k8s/configmap.yaml b/k8s/configmap.yaml new file mode 100644 index 0000000..9f0aa41 --- /dev/null +++ b/k8s/configmap.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: webhook-config +data: + config.yaml: |- + mutate-ignored-images: + - goharbor/chartmuseum-photon + - goharbor/harbor-core + - goharbor/harbor-db + - goharbor/harbor-jobservice + - goharbor/notary-server-photon + - goharbor/notary-signer-photon + - goharbor/harbor-portal + - goharbor/redis-photon + - goharbor/registry-photon + - goharbor/trivy-adapter-photon diff --git a/k8s/daemonset.yaml b/k8s/daemonset.yaml new file mode 100644 index 0000000..0dbf692 --- /dev/null +++ b/k8s/daemonset.yaml @@ -0,0 +1,55 @@ +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: mutating-webhool + labels: + app: mutating-webhook +spec: + selector: + matchLabels: + app: mutating-webhook + template: + metadata: + labels: + app: mutating-webhook + spec: + volumes: + - name: config + configMap: + name: webhook-config + defaultMode: 493 + optional: false + containers: + - name: mutating-webhook + imagePullPolicy: Always + env: + - name: DOCKERHUB_REGISTRY + value: registry.c.test-chamber-13.lan/dockerhub + - name: ALLOW_ADMIN_NOMUTATE + value: "true" + - name: CONFIG_FILE + value: /tmp/config/config.yaml + image: registry.c.test-chamber-13.lan/library/webhook:latest + volumeMounts: + - name: config + mountPath: /tmp/config + livenessProbe: + httpGet: + path: /healthcheck + port: 8443 + scheme: HTTPS + periodSeconds: 15 + initialDelaySeconds: 10 + timeoutSeconds: 2 + failureThreshold: 1 + readinessProbe: + httpGet: + path: /healthcheck + port: 8443 + scheme: HTTPS + initialDelaySeconds: 10 + timeoutSeconds: 2 + failureThreshold: 1 + terminationGracePeriodSeconds: 30 + diff --git a/k8s/service.yaml b/k8s/service.yaml new file mode 100644 index 0000000..f5b054c --- /dev/null +++ b/k8s/service.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: webhook +spec: + ports: + - name: https + protocol: TCP + port: 443 + targetPort: 8443 + selector: + app: mutating-webhook + type: ClusterIP diff --git a/k8s/webhook.yaml b/k8s/webhook.yaml new file mode 100644 index 0000000..946fed2 --- /dev/null +++ b/k8s/webhook.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + name: pod-mutation +webhooks: + - name: pod-mutation.default.svc + clientConfig: + service: + name: webhook + namespace: default + path: "/api/v1/mutate/pod" + rules: + - operations: ["CREATE", ] + apiGroups: [""] + apiVersions: ["v1"] + resources: ["pods"] + failurePolicy: Ignore \ No newline at end of file