mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-03 19:17:29 -05:00 
			
		
		
		
	httpserver: Prevent TLS client authentication bypass in 3 ways (#2099)
- Introduce StrictHostMatching mode for sites that require clientauth - Error if QUIC is enabled whilst TLS clientauth is configured (Our QUIC implementation does not yet support TLS clientauth, but maybe it will in the future - fixes #2095) - Error if one but not all TLS configs for the same hostname have a different ClientAuth CA pool
This commit is contained in:
		
							parent
							
								
									2966db7b78
								
							
						
					
					
						commit
						4d9ee000c8
					
				@ -15,6 +15,7 @@
 | 
				
			|||||||
package httpserver
 | 
					package httpserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"crypto/tls"
 | 
				
			||||||
	"flag"
 | 
						"flag"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
@ -207,8 +208,13 @@ func (h *httpContext) InspectServerBlocks(sourceFile string, serverBlocks []cadd
 | 
				
			|||||||
// MakeServers uses the newly-created siteConfigs to
 | 
					// MakeServers uses the newly-created siteConfigs to
 | 
				
			||||||
// create and return a list of server instances.
 | 
					// create and return a list of server instances.
 | 
				
			||||||
func (h *httpContext) MakeServers() ([]caddy.Server, error) {
 | 
					func (h *httpContext) MakeServers() ([]caddy.Server, error) {
 | 
				
			||||||
	// make sure TLS is disabled for explicitly-HTTP sites
 | 
						// Iterate each site configuration and make sure that:
 | 
				
			||||||
	// (necessary when HTTP address shares a block containing tls)
 | 
						// 1) TLS is disabled for explicitly-HTTP sites (necessary
 | 
				
			||||||
 | 
						//    when an HTTP address shares a block containing tls)
 | 
				
			||||||
 | 
						// 2) if QUIC is enabled, TLS ClientAuth is not, because
 | 
				
			||||||
 | 
						//    currently, QUIC does not support ClientAuth (TODO:
 | 
				
			||||||
 | 
						//    revisit this when our QUIC implementation supports it)
 | 
				
			||||||
 | 
						// 3) if TLS ClientAuth is used, StrictHostMatching is on
 | 
				
			||||||
	for _, cfg := range h.siteConfigs {
 | 
						for _, cfg := range h.siteConfigs {
 | 
				
			||||||
		if !cfg.TLS.Enabled {
 | 
							if !cfg.TLS.Enabled {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
@ -230,6 +236,17 @@ func (h *httpContext) MakeServers() ([]caddy.Server, error) {
 | 
				
			|||||||
			// instead of 443 because it doesn't know about TLS.
 | 
								// instead of 443 because it doesn't know about TLS.
 | 
				
			||||||
			cfg.Addr.Port = HTTPSPort
 | 
								cfg.Addr.Port = HTTPSPort
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if cfg.TLS.ClientAuth != tls.NoClientCert {
 | 
				
			||||||
 | 
								if QUIC {
 | 
				
			||||||
 | 
									return nil, fmt.Errorf("cannot enable TLS client authentication with QUIC, because QUIC does not yet support it")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// this must be enabled so that a client cannot connect
 | 
				
			||||||
 | 
								// using SNI for another site on this listener that
 | 
				
			||||||
 | 
								// does NOT require ClientAuth, and then send HTTP
 | 
				
			||||||
 | 
								// requests with the Host header of this site which DOES
 | 
				
			||||||
 | 
								// require client auth, thus bypassing it...
 | 
				
			||||||
 | 
								cfg.StrictHostMatching = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// we must map (group) each config to a bind address
 | 
						// we must map (group) each config to a bind address
 | 
				
			||||||
@ -556,7 +573,6 @@ var directives = []string{
 | 
				
			|||||||
	"minify",       // github.com/hacdias/caddy-minify
 | 
						"minify",       // github.com/hacdias/caddy-minify
 | 
				
			||||||
	"ipfilter",     // github.com/pyed/ipfilter
 | 
						"ipfilter",     // github.com/pyed/ipfilter
 | 
				
			||||||
	"ratelimit",    // github.com/xuqingfeng/caddy-rate-limit
 | 
						"ratelimit",    // github.com/xuqingfeng/caddy-rate-limit
 | 
				
			||||||
	"search",       // github.com/pedronasser/caddy-search
 | 
					 | 
				
			||||||
	"expires",      // github.com/epicagency/caddy-expires
 | 
						"expires",      // github.com/epicagency/caddy-expires
 | 
				
			||||||
	"forwardproxy", // github.com/caddyserver/forwardproxy
 | 
						"forwardproxy", // github.com/caddyserver/forwardproxy
 | 
				
			||||||
	"basicauth",
 | 
						"basicauth",
 | 
				
			||||||
 | 
				
			|||||||
@ -416,6 +416,18 @@ func (s *Server) serveHTTP(w http.ResponseWriter, r *http.Request) (int, error)
 | 
				
			|||||||
		r.URL = trimPathPrefix(r.URL, pathPrefix)
 | 
							r.URL = trimPathPrefix(r.URL, pathPrefix)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// enforce strict host matching, which ensures that the SNI
 | 
				
			||||||
 | 
						// value (if any), matches the Host header; essential for
 | 
				
			||||||
 | 
						// sites that rely on TLS ClientAuth sharing a port with
 | 
				
			||||||
 | 
						// sites that do not - if mismatched, close the connection
 | 
				
			||||||
 | 
						if vhost.StrictHostMatching && r.TLS != nil &&
 | 
				
			||||||
 | 
							strings.ToLower(r.TLS.ServerName) != strings.ToLower(hostname) {
 | 
				
			||||||
 | 
							r.Close = true
 | 
				
			||||||
 | 
							log.Printf("[ERROR] %s - strict host matching: SNI (%s) and HTTP Host (%s) values differ",
 | 
				
			||||||
 | 
								vhost.Addr, r.TLS.ServerName, hostname)
 | 
				
			||||||
 | 
							return http.StatusForbidden, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return vhost.middlewareChain.ServeHTTP(w, r)
 | 
						return vhost.middlewareChain.ServeHTTP(w, r)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -36,6 +36,16 @@ type SiteConfig struct {
 | 
				
			|||||||
	// TLS configuration
 | 
						// TLS configuration
 | 
				
			||||||
	TLS *caddytls.Config
 | 
						TLS *caddytls.Config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If true, the Host header in the HTTP request must
 | 
				
			||||||
 | 
						// match the SNI value in the TLS handshake (if any).
 | 
				
			||||||
 | 
						// This should be enabled whenever a site relies on
 | 
				
			||||||
 | 
						// TLS client authentication, for example; or any time
 | 
				
			||||||
 | 
						// you want to enforce that THIS site's TLS config
 | 
				
			||||||
 | 
						// is used and not the TLS config of any other site
 | 
				
			||||||
 | 
						// on the same listener. TODO: Check how relevant this
 | 
				
			||||||
 | 
						// is with TLS 1.3.
 | 
				
			||||||
 | 
						StrictHostMatching bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Uncompiled middleware stack
 | 
						// Uncompiled middleware stack
 | 
				
			||||||
	middleware []Middleware
 | 
						middleware []Middleware
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -513,6 +513,14 @@ func assertConfigsCompatible(cfg1, cfg2 *Config) error {
 | 
				
			|||||||
	if c1.ClientAuth != c2.ClientAuth {
 | 
						if c1.ClientAuth != c2.ClientAuth {
 | 
				
			||||||
		return fmt.Errorf("client authentication policy mismatch")
 | 
							return fmt.Errorf("client authentication policy mismatch")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if c1.ClientAuth != tls.NoClientCert && c2.ClientAuth != tls.NoClientCert && c1.ClientCAs != c2.ClientCAs {
 | 
				
			||||||
 | 
							// Two hosts defined on the same listener are not compatible if they
 | 
				
			||||||
 | 
							// have ClientAuth enabled, because there's no guarantee beyond the
 | 
				
			||||||
 | 
							// hostname which config will be used (because SNI only has server name).
 | 
				
			||||||
 | 
							// To prevent clients from bypassing authentication, require that
 | 
				
			||||||
 | 
							// ClientAuth be configured in an unambiguous manner.
 | 
				
			||||||
 | 
							return fmt.Errorf("multiple hosts requiring client authentication ambiguously configured")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user