/* Copyright 2019 The Kubernetes 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 utils import ( "crypto/tls" "crypto/x509" "encoding/json" "fmt" "io/ioutil" "net/http" "net/http/httptest" "testing" "k8s.io/api/admission/v1beta1" ) // NewAdmissionWebhookServer sets up a webhook server with TLS enabled, returns URL and Close function // for the server func NewAdmissionWebhookServer(handler http.Handler) (string, func(), error) { // set up webhook server roots := x509.NewCertPool() if !roots.AppendCertsFromPEM(LocalhostCert) { return "", nil, fmt.Errorf("Failed to append Cert from PEM") } cert, err := tls.X509KeyPair(LocalhostCert, LocalhostKey) if err != nil { return "", nil, fmt.Errorf("Failed to build cert with error: %+v", err) } webhookServer := httptest.NewUnstartedServer(handler) webhookServer.TLS = &tls.Config{ RootCAs: roots, Certificates: []tls.Certificate{cert}, } webhookServer.StartTLS() return webhookServer.URL, webhookServer.Close, nil } // AdmissionWebhookHandler creates a HandlerFunc that decodes/encodes AdmissionReview and performs // given admit function func AdmissionWebhookHandler(t *testing.T, admit func(*v1beta1.AdmissionReview) error) http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() data, err := ioutil.ReadAll(r.Body) if err != nil { t.Error(err) return } if contentType := r.Header.Get("Content-Type"); contentType != "application/json" { t.Errorf("contentType=%s, expect application/json", contentType) return } review := v1beta1.AdmissionReview{} if err := json.Unmarshal(data, &review); err != nil { t.Errorf("Fail to deserialize object: %s with error: %v", string(data), err) http.Error(w, err.Error(), 400) return } if err := admit(&review); err != nil { t.Errorf("%v", err) http.Error(w, err.Error(), 400) return } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(review); err != nil { t.Errorf("Marshal of response failed with error: %v", err) } }) } // LocalhostCert was generated from crypto/tls/generate_cert.go with the following command: // go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h var LocalhostCert = []byte(`-----BEGIN CERTIFICATE----- MIIBjzCCATmgAwIBAgIRAKpi2WmTcFrVjxrl5n5YDUEwDQYJKoZIhvcNAQELBQAw EjEQMA4GA1UEChMHQWNtZSBDbzAgFw03MDAxMDEwMDAwMDBaGA8yMDg0MDEyOTE2 MDAwMFowEjEQMA4GA1UEChMHQWNtZSBDbzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgC QQC9fEbRszP3t14Gr4oahV7zFObBI4TfA5i7YnlMXeLinb7MnvT4bkfOJzE6zktn 59zP7UiHs3l4YOuqrjiwM413AgMBAAGjaDBmMA4GA1UdDwEB/wQEAwICpDATBgNV HSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MC4GA1UdEQQnMCWCC2V4 YW1wbGUuY29thwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUA A0EAUsVE6KMnza/ZbodLlyeMzdo7EM/5nb5ywyOxgIOCf0OOLHsPS9ueGLQX9HEG //yjTXuhNcUugExIjM/AIwAZPQ== -----END CERTIFICATE-----`) // LocalhostKey is the private key for LocalhostCert. var LocalhostKey = []byte(`-----BEGIN RSA PRIVATE KEY----- MIIBOwIBAAJBAL18RtGzM/e3XgavihqFXvMU5sEjhN8DmLtieUxd4uKdvsye9Phu R84nMTrOS2fn3M/tSIezeXhg66quOLAzjXcCAwEAAQJBAKcRxH9wuglYLBdI/0OT BLzfWPZCEw1vZmMR2FF1Fm8nkNOVDPleeVGTWoOEcYYlQbpTmkGSxJ6ya+hqRi6x goECIQDx3+X49fwpL6B5qpJIJMyZBSCuMhH4B7JevhGGFENi3wIhAMiNJN5Q3UkL IuSvv03kaPR5XVQ99/UeEetUgGvBcABpAiBJSBzVITIVCGkGc7d+RCf49KTCIklv bGWObufAR8Ni4QIgWpILjW8dkGg8GOUZ0zaNA6Nvt6TIv2UWGJ4v5PoV98kCIQDx rIiZs5QbKdycsv9gQJzwQAogC8o04X3Zz3dsoX+h4A== -----END RSA PRIVATE KEY-----`)