From 5cf5bf9fb10937019d39ec6895549d40825f87bf Mon Sep 17 00:00:00 2001 From: Alex Kalenyuk Date: Wed, 11 May 2022 17:30:55 +0300 Subject: [PATCH] Allow TLS config to be entirely configured on webhook server Some operators might want to respect cluster-wide TLS ciphers for example, which means that these will eventually have to be passed down to the webhook server. Signed-off-by: Alex Kalenyuk --- pkg/webhook/server.go | 12 ++++++++++++ pkg/webhook/server_test.go | 40 +++++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/pkg/webhook/server.go b/pkg/webhook/server.go index 5bdc847e17..66cdcec2e6 100644 --- a/pkg/webhook/server.go +++ b/pkg/webhook/server.go @@ -41,6 +41,9 @@ import ( // DefaultPort is the default port that the webhook server serves. var DefaultPort = 9443 +// ExportedTLSConfig is used in unit tests to ensure propagation of tls related configurables to server. +var ExportedTLSConfig *tls.Config + // Server is an admission webhook server that can serve traffic and // generates related k8s resources for deploying. // @@ -76,6 +79,9 @@ type Server struct { // "", "1.0", "1.1", "1.2" and "1.3" only ("" is equivalent to "1.0" for backwards compatibility) TLSMinVersion string + // TLSOpts is used to allow configuring the TLS config used for the server + TLSOpts []func(*tls.Config) + // WebhookMux is the multiplexer that handles different webhooks. WebhookMux *http.ServeMux @@ -254,6 +260,12 @@ func (s *Server) Start(ctx context.Context) error { cfg.ClientAuth = tls.RequireAndVerifyClientCert } + // fallback TLS config ready, will now mutate if passer wants full control over it + for _, op := range s.TLSOpts { + op(cfg) + } + ExportedTLSConfig = cfg + listener, err := tls.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)), cfg) if err != nil { return err diff --git a/pkg/webhook/server_test.go b/pkg/webhook/server_test.go index 2ed986e405..73db57aba4 100644 --- a/pkg/webhook/server_test.go +++ b/pkg/webhook/server_test.go @@ -18,6 +18,7 @@ package webhook_test import ( "context" + "crypto/tls" "fmt" "io" "net" @@ -186,7 +187,7 @@ var _ = Describe("Webhook Server", func() { }) }) - It("should serve be able to serve in unmanaged mode", func() { + It("should be able to serve in unmanaged mode", func() { server = &webhook.Server{ Host: servingOpts.LocalServingHost, Port: servingOpts.LocalServingPort, @@ -207,6 +208,43 @@ var _ = Describe("Webhook Server", func() { ctxCancel() Eventually(doneCh, "4s").Should(BeClosed()) }) + + It("should respect passed in TLS configurations", func() { + tlsCfgFunc := func(cfg *tls.Config) { + cfg.CipherSuites = []uint16{ + tls.TLS_AES_128_GCM_SHA256, + tls.TLS_AES_256_GCM_SHA384, + } + } + server = &webhook.Server{ + Host: servingOpts.LocalServingHost, + Port: servingOpts.LocalServingPort, + CertDir: servingOpts.LocalServingCertDir, + TLSMinVersion: "1.2", + TLSOpts: []func(*tls.Config){ + tlsCfgFunc, + }, + } + server.Register("/somepath", &testHandler{}) + doneCh := genericStartServer(func(ctx context.Context) { + Expect(server.StartStandalone(ctx, scheme.Scheme)) + }) + + Eventually(func() ([]byte, error) { + resp, err := client.Get(fmt.Sprintf("https://%s/somepath", testHostPort)) + Expect(err).NotTo(HaveOccurred()) + defer resp.Body.Close() + return io.ReadAll(resp.Body) + }).Should(Equal([]byte("gadzooks!"))) + Expect(webhook.ExportedTLSConfig.MinVersion).To(Equal(uint16(tls.VersionTLS12))) + Expect(webhook.ExportedTLSConfig.CipherSuites).To(ContainElements( + tls.TLS_AES_128_GCM_SHA256, + tls.TLS_AES_256_GCM_SHA384, + )) + + ctxCancel() + Eventually(doneCh, "4s").Should(BeClosed()) + }) }) type testHandler struct {