@marcel91200 / caddy Mirror · read-only / commits / 2b22d2e6
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

Optionally enforce strict TLS SNI + HTTP Host matching, & misc. cleanup

We should look into a way to enable this by default when TLS client auth is configured for a server

Matthew Holt committed Jun 26, 2019 at 16:03 UTC 2b22d2e6ea7ffd17ae769bd8a2adae60e5a7d0bf
4 files changed +49 -14
context.go
+2 -2
@@ -13,8 +13,8 @@ import (
13 // Context is a type which defines the lifetime of modules that
14 // are loaded and provides access to the parent configuration
15 // that spawned the modules which are loaded. It should be used
16 -// with care and only wrapped with derivation functions from
17 -// the standard context package if you don't need the Caddy
16 +// with care and wrapped with derivation functions from the
17 +// standard context package only if you don't need the Caddy
18 // specific features. These contexts are cancelled when the
19 // lifetime of the modules loaded from it are over.
20 //
modules/caddyhttp/routes.go
+5 -5
@@ -162,11 +162,11 @@ func (routes RouteList) BuildCompositeRoute(rw http.ResponseWriter, req *http.Re
162 }
163
164 // wrapMiddleware wraps m such that it can be correctly
165 -// appended to a list of middleware. This is necessary
166 -// so that only the last middleware in a loop does not
167 -// become the only middleware of the stack, repeatedly
168 -// executed (i.e. it is necessary to keep a reference
169 -// to this m outside of the scope of a loop)!
165 +// appended to a list of middleware. This separate closure
166 +// is necessary so that only the last middleware in a loop
167 +// does not become the only middleware of the stack,
168 +// repeatedly executed (i.e. it is necessary to keep a
169 +// reference to this m outside of the scope of a loop)!
170 func wrapMiddleware(m MiddlewareHandler) Middleware {
171 return func(next HandlerFunc) HandlerFunc {
172 return func(w http.ResponseWriter, r *http.Request) error {
modules/caddyhttp/server.go
+38 -4
@@ -7,6 +7,7 @@ import (
7 "net"
8 "net/http"
9 "strconv"
10 + "strings"
11
12 "github.com/caddyserver/caddy"
13 "github.com/caddyserver/caddy/modules/caddytls"
@@ -25,6 +26,7 @@ type Server struct {
26 TLSConnPolicies caddytls.ConnectionPolicies `json:"tls_connection_policies,omitempty"`
27 AutoHTTPS *AutoHTTPSConfig `json:"automatic_https,omitempty"`
28 MaxRehandles int `json:"max_rehandles,omitempty"`
29 + StrictSNIHost bool `json:"strict_sni_host,omitempty"` // TODO: see if we can turn this on by default when clientauth is configured
30
31 tlsApp *caddytls.TLS
32 }
@@ -47,8 +49,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
49 addHTTPVarsToReplacer(repl, r, w)
50
51 // build and execute the main handler chain
50 - stack, w := s.Routes.BuildCompositeRoute(w, r)
51 - err := s.executeCompositeRoute(w, r, stack)
52 + stack, wrappedWriter := s.Routes.BuildCompositeRoute(w, r)
53 + stack = s.wrapPrimaryRoute(stack)
54 + err := s.executeCompositeRoute(wrappedWriter, r, stack)
55 if err != nil {
56 // add the raw error value to the request context
57 // so it can be accessed by error handlers
@@ -66,8 +69,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
69 }
70
71 if s.Errors != nil && len(s.Errors.Routes) > 0 {
69 - errStack, w := s.Errors.Routes.BuildCompositeRoute(w, r)
70 - err := s.executeCompositeRoute(w, r, errStack)
72 + errStack, wrappedWriter := s.Errors.Routes.BuildCompositeRoute(w, r)
73 + err := s.executeCompositeRoute(wrappedWriter, r, errStack)
74 if err != nil {
75 // TODO: what should we do if the error handler has an error?
76 log.Printf("[ERROR] [%s %s] handling error: %v", r.Method, r.RequestURI, err)
@@ -104,6 +107,37 @@ func (s *Server) executeCompositeRoute(w http.ResponseWriter, r *http.Request, s
107 return err
108 }
109
110 +// wrapPrimaryRoute wraps stack (a compiled middleware handler chain)
111 +// in s.enforcementHandler which performs crucial security checks, etc.
112 +func (s *Server) wrapPrimaryRoute(stack Handler) Handler {
113 + return HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
114 + return s.enforcementHandler(w, r, stack)
115 + })
116 +}
117 +
118 +// enforcementHandler is an implicit middleware which performs
119 +// standard checks before executing the HTTP middleware chain.
120 +func (s *Server) enforcementHandler(w http.ResponseWriter, r *http.Request, next Handler) error {
121 + // enforce strict host matching, which ensures that the SNI
122 + // value (if any), matches the Host header; essential for
123 + // servers that rely on TLS ClientAuth sharing a listener
124 + // with servers that do not; if not enforced, client could
125 + // bypass by sending benign SNI then restricted Host header
126 + if s.StrictSNIHost && r.TLS != nil {
127 + hostname, _, err := net.SplitHostPort(r.Host)
128 + if err != nil {
129 + hostname = r.Host // OK; probably lacked port
130 + }
131 + if strings.ToLower(r.TLS.ServerName) != strings.ToLower(hostname) {
132 + err := fmt.Errorf("strict host matching: TLS ServerName (%s) and HTTP Host (%s) values differ",
133 + r.TLS.ServerName, hostname)
134 + r.Close = true
135 + return Error(http.StatusForbidden, err)
136 + }
137 + }
138 + return next.ServeHTTP(w, r)
139 +}
140 +
141 func (s *Server) listenersUseAnyPortOtherThan(otherPort int) bool {
142 for _, lnAddr := range s.Listen {
143 _, addrs, err := parseListenAddr(lnAddr)
modules/caddytls/tls.go
+4 -3
@@ -88,15 +88,16 @@ func (t *TLS) Provision(ctx caddy.Context) error {
88
89 // Start activates the TLS module.
90 func (t *TLS) Start() error {
91 + magic := certmagic.New(t.certCache, certmagic.Config{
92 + Storage: t.ctx.Storage(),
93 + })
94 +
95 // load manual/static (unmanaged) certificates
96 for _, loader := range t.certificateLoaders {
97 certs, err := loader.LoadCertificates()
98 if err != nil {
99 return fmt.Errorf("loading certificates: %v", err)
100 }
97 - magic := certmagic.New(t.certCache, certmagic.Config{
98 - Storage: t.ctx.Storage(),
99 - })
101 for _, cert := range certs {
102 err := magic.CacheUnmanagedTLSCertificate(cert.Certificate, cert.Tags)
103 if err != nil {