From 4c289fc6ad3d7c2afa69e36cdf2cc9a172cb1c9c Mon Sep 17 00:00:00 2001
From: Matthew Holt <mholt@users.noreply.github.com>
Date: Tue, 17 Sep 2019 23:13:21 -0600
Subject: [PATCH] Allow domain fronting with TLS client auth if explicitly
 configured

---
 modules/caddyhttp/caddyhttp.go | 20 ++++++++++++--------
 modules/caddyhttp/server.go    |  4 ++--
 2 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/modules/caddyhttp/caddyhttp.go b/modules/caddyhttp/caddyhttp.go
index c044a9bfd..0a26ad181 100644
--- a/modules/caddyhttp/caddyhttp.go
+++ b/modules/caddyhttp/caddyhttp.go
@@ -78,16 +78,20 @@ func (app *App) Provision(ctx caddy.Context) error {
 			srv.AutoHTTPS = new(AutoHTTPSConfig)
 		}
 
-		// disallow TLS client auth bypass which could
-		// otherwise be exploited by sending an unprotected
-		// SNI value during TLS handshake, then a protected
-		// Host header during HTTP request later on that
-		// connection
-		if srv.hasTLSClientAuth() {
-			srv.StrictSNIHost = true
+		// if not explicitly configured by the user, disallow TLS
+		// client auth bypass (domain fronting) which could
+		// otherwise be exploited by sending an unprotected SNI
+		// value during a TLS handshake, then putting a protected
+		// domain in the Host header after establishing connection;
+		// this is a safe default, but we allow users to override
+		// it for example in the case of running a proxy where
+		// domain fronting is desired and access is not restricted
+		// based on hostname
+		if srv.StrictSNIHost == nil && srv.hasTLSClientAuth() {
+			trueBool := true
+			srv.StrictSNIHost = &trueBool
 		}
 
-		// TODO: Test this function to ensure these replacements are performed
 		for i := range srv.Listen {
 			srv.Listen[i] = repl.ReplaceAll(srv.Listen[i], "")
 		}
diff --git a/modules/caddyhttp/server.go b/modules/caddyhttp/server.go
index b4952e124..0ccdeea07 100644
--- a/modules/caddyhttp/server.go
+++ b/modules/caddyhttp/server.go
@@ -42,7 +42,7 @@ type Server struct {
 	TLSConnPolicies   caddytls.ConnectionPolicies `json:"tls_connection_policies,omitempty"`
 	AutoHTTPS         *AutoHTTPSConfig            `json:"automatic_https,omitempty"`
 	MaxRehandles      *int                        `json:"max_rehandles,omitempty"`
-	StrictSNIHost     bool                        `json:"strict_sni_host,omitempty"`
+	StrictSNIHost     *bool                       `json:"strict_sni_host,omitempty"`
 
 	// This field is not subject to compatibility promises
 	ExperimentalHTTP3 bool `json:"experimental_http3,omitempty"`
@@ -164,7 +164,7 @@ func (s *Server) enforcementHandler(w http.ResponseWriter, r *http.Request, next
 	// servers that rely on TLS ClientAuth sharing a listener
 	// with servers that do not; if not enforced, client could
 	// bypass by sending benign SNI then restricted Host header
-	if s.StrictSNIHost && r.TLS != nil {
+	if s.StrictSNIHost != nil && *s.StrictSNIHost && r.TLS != nil {
 		hostname, _, err := net.SplitHostPort(r.Host)
 		if err != nil {
 			hostname = r.Host // OK; probably lacked port