From 5bbdb4420254ba73b9c4df4775fe7bdacf233b17 Mon Sep 17 00:00:00 2001
From: Tuomo Tanskanen <tuomo.tanskanen@est.tech>
Date: Thu, 25 Jan 2024 13:55:55 +0200
Subject: [PATCH] feat: add TLS versions configuration

Add configuration options for TLSMinVersion and TLSMaxVersion.
This enables setting TLS 1.3 as minimum version for example for both
GRPC and Web, or enforcing TLS 1.2 only for easier debugging of
secure connections.

Signed-off-by: Tuomo Tanskanen <tuomo.tanskanen@est.tech>
---
 cmd/dex/config.go      | 20 +++++++++++++++-----
 cmd/dex/config_test.go |  8 ++++++--
 cmd/dex/serve.go       | 28 ++++++++++++++++++++++++++--
 config.yaml.dist       |  2 ++
 4 files changed, 49 insertions(+), 9 deletions(-)

diff --git a/cmd/dex/config.go b/cmd/dex/config.go
index dd91bf1e..9c5c1039 100644
--- a/cmd/dex/config.go
+++ b/cmd/dex/config.go
@@ -64,10 +64,16 @@ func (c Config) Validate() error {
 		{c.Web.HTTP == "" && c.Web.HTTPS == "", "must supply a HTTP/HTTPS  address to listen on"},
 		{c.Web.HTTPS != "" && c.Web.TLSCert == "", "no cert specified for HTTPS"},
 		{c.Web.HTTPS != "" && c.Web.TLSKey == "", "no private key specified for HTTPS"},
+		{c.Web.TLSMinVersion != "" && c.Web.TLSMinVersion != "1.2" && c.Web.TLSMinVersion != "1.3", "supported TLS versions are: 1.2, 1.3"},
+		{c.Web.TLSMaxVersion != "" && c.Web.TLSMaxVersion != "1.2" && c.Web.TLSMaxVersion != "1.3", "supported TLS versions are: 1.2, 1.3"},
+		{c.Web.TLSMaxVersion != "" && c.Web.TLSMinVersion != "" && c.Web.TLSMinVersion > c.Web.TLSMaxVersion, "TLSMinVersion greater than TLSMaxVersion"},
 		{c.GRPC.TLSCert != "" && c.GRPC.Addr == "", "no address specified for gRPC"},
 		{c.GRPC.TLSKey != "" && c.GRPC.Addr == "", "no address specified for gRPC"},
 		{(c.GRPC.TLSCert == "") != (c.GRPC.TLSKey == ""), "must specific both a gRPC TLS cert and key"},
 		{c.GRPC.TLSCert == "" && c.GRPC.TLSClientCA != "", "cannot specify gRPC TLS client CA without a gRPC TLS cert"},
+		{c.GRPC.TLSMinVersion != "" && c.GRPC.TLSMinVersion != "1.2" && c.GRPC.TLSMinVersion != "1.3", "supported TLS versions are: 1.2, 1.3"},
+		{c.GRPC.TLSMaxVersion != "" && c.GRPC.TLSMaxVersion != "1.2" && c.GRPC.TLSMaxVersion != "1.3", "supported TLS versions are: 1.2, 1.3"},
+		{c.GRPC.TLSMaxVersion != "" && c.GRPC.TLSMinVersion != "" && c.GRPC.TLSMinVersion > c.GRPC.TLSMaxVersion, "TLSMinVersion greater than TLSMaxVersion"},
 	}
 
 	var checkErrors []string
@@ -149,6 +155,8 @@ type Web struct {
 	HTTPS          string   `json:"https"`
 	TLSCert        string   `json:"tlsCert"`
 	TLSKey         string   `json:"tlsKey"`
+	TLSMinVersion  string   `json:"tlsMinVersion"`
+	TLSMaxVersion  string   `json:"tlsMaxVersion"`
 	AllowedOrigins []string `json:"allowedOrigins"`
 	AllowedHeaders []string `json:"allowedHeaders"`
 }
@@ -163,11 +171,13 @@ type Telemetry struct {
 // GRPC is the config for the gRPC API.
 type GRPC struct {
 	// The port to listen on.
-	Addr        string `json:"addr"`
-	TLSCert     string `json:"tlsCert"`
-	TLSKey      string `json:"tlsKey"`
-	TLSClientCA string `json:"tlsClientCA"`
-	Reflection  bool   `json:"reflection"`
+	Addr          string `json:"addr"`
+	TLSCert       string `json:"tlsCert"`
+	TLSKey        string `json:"tlsKey"`
+	TLSClientCA   string `json:"tlsClientCA"`
+	TLSMinVersion string `json:"tlsMinVersion"`
+	TLSMaxVersion string `json:"tlsMaxVersion"`
+	Reflection    bool   `json:"reflection"`
 }
 
 // Storage holds app's storage configuration.
diff --git a/cmd/dex/config_test.go b/cmd/dex/config_test.go
index 21037084..16803d6d 100644
--- a/cmd/dex/config_test.go
+++ b/cmd/dex/config_test.go
@@ -71,7 +71,9 @@ storage:
     connMaxLifetime: 30
     connectionTimeout: 3
 web:
-  http: 127.0.0.1:5556
+  https: 127.0.0.1:5556
+  tlsMinVersion: 1.3
+  tlsMaxVersion: 1.2
 
 frontend:
   dir: ./web
@@ -144,7 +146,9 @@ logger:
 			},
 		},
 		Web: Web{
-			HTTP: "127.0.0.1:5556",
+			HTTPS:         "127.0.0.1:5556",
+			TLSMinVersion: "1.3",
+			TLSMaxVersion: "1.2",
 		},
 		Frontend: server.WebConfig{
 			Dir: "./web",
diff --git a/cmd/dex/serve.go b/cmd/dex/serve.go
index 54767494..3494443e 100644
--- a/cmd/dex/serve.go
+++ b/cmd/dex/serve.go
@@ -145,9 +145,23 @@ func runServe(options serveOptions) error {
 		tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
 	}
 
+	allowedTLSVersions := map[string]int{
+		"1.2": tls.VersionTLS12,
+		"1.3": tls.VersionTLS13,
+	}
+
 	if c.GRPC.TLSCert != "" {
+		tlsMinVersion := tls.VersionTLS12
+		if c.GRPC.TLSMinVersion != "" {
+			tlsMinVersion = allowedTLSVersions[c.GRPC.TLSMinVersion]
+		}
+		tlsMaxVersion := 0 // default for max is whatever Go defaults to
+		if c.GRPC.TLSMaxVersion != "" {
+			tlsMaxVersion = allowedTLSVersions[c.GRPC.TLSMaxVersion]
+		}
 		baseTLSConfig := &tls.Config{
-			MinVersion:               tls.VersionTLS12,
+			MinVersion:               uint16(tlsMinVersion),
+			MaxVersion:               uint16(tlsMaxVersion),
 			CipherSuites:             allowedTLSCiphers,
 			PreferServerCipherSuites: true,
 		}
@@ -422,8 +436,18 @@ func runServe(options serveOptions) error {
 			return fmt.Errorf("listening (%s) on %s: %v", name, c.Web.HTTPS, err)
 		}
 
+		tlsMinVersion := tls.VersionTLS12
+		if c.Web.TLSMinVersion != "" {
+			tlsMinVersion = allowedTLSVersions[c.Web.TLSMinVersion]
+		}
+		tlsMaxVersion := 0 // default for max is whatever Go defaults to
+		if c.Web.TLSMaxVersion != "" {
+			tlsMaxVersion = allowedTLSVersions[c.Web.TLSMaxVersion]
+		}
+
 		baseTLSConfig := &tls.Config{
-			MinVersion:               tls.VersionTLS12,
+			MinVersion:               uint16(tlsMinVersion),
+			MaxVersion:               uint16(tlsMaxVersion),
 			CipherSuites:             allowedTLSCiphers,
 			PreferServerCipherSuites: true,
 		}
diff --git a/config.yaml.dist b/config.yaml.dist
index 12d9b927..b7e1410f 100644
--- a/config.yaml.dist
+++ b/config.yaml.dist
@@ -55,6 +55,8 @@ web:
   # https: 127.0.0.1:5554
   # tlsCert: /etc/dex/tls.crt
   # tlsKey: /etc/dex/tls.key
+  # tlsMinVersion: 1.2
+  # tlsMaxVersion: 1.3
 
 # Dex UI configuration
 # frontend:
-- 
GitLab