Browse Source

Add webhook tls options (#1466)

During our internal security scan, the webhook for external-secrets was
flagged because it supports protocol vulnerable to Sweet32
(https://sweet32.info/). In order to avoid the webhook from being
flagged, we need to restrict the TLS ciphers on controller runtime.

To do this I needed to update the dependency to 0.12.3 and some other
conflicting dependencies.

Signed-off-by: Joao Pedro Silva <jp.silva15@gmail.com>
João Silva 3 years ago
parent
commit
744309abfa
2 changed files with 49 additions and 2 deletions
  1. 2 0
      cmd/root.go
  2. 47 2
      cmd/webhook.go

+ 2 - 0
cmd/root.go

@@ -68,6 +68,8 @@ var (
 	certCheckInterval                     time.Duration
 	certCheckInterval                     time.Duration
 	certLookaheadInterval                 time.Duration
 	certLookaheadInterval                 time.Duration
 	enableAWSSession                      bool
 	enableAWSSession                      bool
+	tlsCiphers                            string
+	tlsMinVersion                         string
 )
 )
 
 
 const (
 const (

+ 47 - 2
cmd/webhook.go

@@ -17,9 +17,12 @@ package cmd
 
 
 import (
 import (
 	"context"
 	"context"
+	"crypto/tls"
+	"fmt"
 	"net/http"
 	"net/http"
 	"os"
 	"os"
 	"os/signal"
 	"os/signal"
+	"strings"
 	"syscall"
 	"syscall"
 	"time"
 	"time"
 
 
@@ -28,6 +31,7 @@ import (
 	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
 	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
 	ctrl "sigs.k8s.io/controller-runtime"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/log/zap"
 	"sigs.k8s.io/controller-runtime/pkg/log/zap"
+	"sigs.k8s.io/controller-runtime/pkg/webhook"
 
 
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
@@ -92,12 +96,26 @@ var webhookCmd = &cobra.Command{
 			}
 			}
 		}(c, dnsName, certCheckInterval)
 		}(c, dnsName, certCheckInterval)
 
 
+		cipherList, err := getTLSCipherSuitesIDs(tlsCiphers)
+		if err != nil {
+			ctrl.Log.Error(err, "unable to fetch tls ciphers")
+			os.Exit(1)
+		}
+		mgrTLSOptions := func(cfg *tls.Config) {
+			cfg.CipherSuites = cipherList
+		}
 		mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
 		mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
 			Scheme:                 scheme,
 			Scheme:                 scheme,
 			MetricsBindAddress:     metricsAddr,
 			MetricsBindAddress:     metricsAddr,
 			HealthProbeBindAddress: healthzAddr,
 			HealthProbeBindAddress: healthzAddr,
-			Port:                   port,
-			CertDir:                certDir,
+			WebhookServer: &webhook.Server{
+				CertDir:       certDir,
+				Port:          port,
+				TLSMinVersion: tlsMinVersion,
+				TLSOpts: []func(*tls.Config){
+					mgrTLSOptions,
+				},
+			},
 		})
 		})
 		if err != nil {
 		if err != nil {
 			setupLog.Error(err, "unable to start manager")
 			setupLog.Error(err, "unable to start manager")
@@ -167,6 +185,26 @@ func waitForCerts(c crds.CertInfo, timeout time.Duration) error {
 	}
 	}
 }
 }
 
 
+func getTLSCipherSuitesIDs(cipherListString string) ([]uint16, error) {
+	if cipherListString == "" {
+		return nil, nil
+	}
+	cipherList := strings.Split(cipherListString, ",")
+	cipherIds := map[string]uint16{}
+	for _, cs := range tls.CipherSuites() {
+		cipherIds[cs.Name] = cs.ID
+	}
+	ret := make([]uint16, 0, len(cipherList))
+	for _, c := range cipherList {
+		id, ok := cipherIds[c]
+		if !ok {
+			return ret, fmt.Errorf("cipher %s was not found", c)
+		}
+		ret = append(ret, id)
+	}
+	return ret, nil
+}
+
 func init() {
 func init() {
 	rootCmd.AddCommand(webhookCmd)
 	rootCmd.AddCommand(webhookCmd)
 	webhookCmd.Flags().StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
 	webhookCmd.Flags().StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
@@ -177,4 +215,11 @@ func init() {
 	webhookCmd.Flags().StringVar(&loglevel, "loglevel", "info", "loglevel to use, one of: debug, info, warn, error, dpanic, panic, fatal")
 	webhookCmd.Flags().StringVar(&loglevel, "loglevel", "info", "loglevel to use, one of: debug, info, warn, error, dpanic, panic, fatal")
 	webhookCmd.Flags().DurationVar(&certCheckInterval, "check-interval", 5*time.Minute, "certificate check interval")
 	webhookCmd.Flags().DurationVar(&certCheckInterval, "check-interval", 5*time.Minute, "certificate check interval")
 	webhookCmd.Flags().DurationVar(&certLookaheadInterval, "lookahead-interval", crds.LookaheadInterval, "certificate check interval")
 	webhookCmd.Flags().DurationVar(&certLookaheadInterval, "lookahead-interval", crds.LookaheadInterval, "certificate check interval")
+	// https://go.dev/blog/tls-cipher-suites explains the ciphers selection process
+	webhookCmd.Flags().StringVar(&tlsCiphers, "tls-ciphers", "", "comma separated list of tls ciphers allowed."+
+		" This does not apply to TLS 1.3 as the ciphers are selected automatically."+
+		" The order of this list does not give preference to the ciphers, the ordering is done automatically."+
+		" Full lists of available ciphers can be found at https://pkg.go.dev/crypto/tls#pkg-constants."+
+		" E.g. 'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256'")
+	webhookCmd.Flags().StringVar(&tlsMinVersion, "tls-min-version", "1.2", "minimum version of TLS supported. Defaults to 1.2")
 }
 }