@marcel91200 / caddy Mirror · read-only / commits / f66493ef
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 13, 00:30 UTC

core: Allow loopback hosts for admin endpoint (fix #5650) (#5664)

Matt Holt committed Aug 2, 2023 at 11:13 UTC f66493efef4d909fdeb68a2ce8131d58e17333b3
2 files changed +43 -14
admin.go
+25
@@ -318,7 +318,32 @@ func (admin AdminConfig) allowedOrigins(addr NetworkAddress) []*url.URL {
318 // messages. If the requested URI does not include an Internet host
319 // name for the service being requested, then the Host header field MUST
320 // be given with an empty value."
321 + //
322 + // UPDATE July 2023: Go broke this by patching a minor security bug in 1.20.6.
323 + // Understandable, but frustrating. See:
324 + // https://github.com/golang/go/issues/60374
325 + // See also the discussion here:
326 + // https://github.com/golang/go/issues/61431
327 + //
328 + // We can no longer conform to RFC 2616 Section 14.26 from either Go or curl
329 + // in purity. (Curl allowed no host between 7.40 and 7.50, but now requires a
330 + // bogus host; see https://superuser.com/a/925610.) If we disable Host/Origin
331 + // security checks, the infosec community assures me that it is secure to do
332 + // so, because:
333 + // 1) Browsers do not allow access to unix sockets
334 + // 2) DNS is irrelevant to unix sockets
335 + //
336 + // I am not quite ready to trust either of those external factors, so instead
337 + // of disabling Host/Origin checks, we now allow specific Host values when
338 + // accessing the admin endpoint over unix sockets. I definitely don't trust
339 + // DNS (e.g. I don't trust 'localhost' to always resolve to the local host),
340 + // and IP shouldn't even be used, but if it is for some reason, I think we can
341 + // at least be reasonably assured that 127.0.0.1 and ::1 route to the local
342 + // machine, meaning that a hypothetical browser origin would have to be on the
343 + // local machine as well.
344 uniqueOrigins[""] = struct{}{}
345 + uniqueOrigins["127.0.0.1"] = struct{}{}
346 + uniqueOrigins["::1"] = struct{}{}
347 } else {
348 uniqueOrigins[net.JoinHostPort("localhost", addr.port())] = struct{}{}
349 uniqueOrigins[net.JoinHostPort("::1", addr.port())] = struct{}{}
cmd/commandfuncs.go
+18 -14
@@ -610,7 +610,7 @@ func AdminAPIRequest(adminAddr, method, uri string, headers http.Header, body io
610 }
611 origin := "http://" + parsedAddr.JoinHostPort(0)
612 if parsedAddr.IsUnixNetwork() {
613 - origin = "http://unixsocket" // hack so that http.NewRequest() is happy
613 + origin = "http://127.0.0.1" // bogus host is a hack so that http.NewRequest() is happy
614 }
615
616 // form the request
@@ -619,20 +619,24 @@ func AdminAPIRequest(adminAddr, method, uri string, headers http.Header, body io
619 return nil, fmt.Errorf("making request: %v", err)
620 }
621 if parsedAddr.IsUnixNetwork() {
622 - // When listening on a unix socket, the admin endpoint doesn't
623 - // accept any Host header because there is no host:port for
624 - // a unix socket's address. The server's host check is fairly
625 - // strict for security reasons, so we don't allow just any
626 - // Host header. For unix sockets, the Host header must be
627 - // empty. Unfortunately, Go makes it impossible to make HTTP
628 - // requests with an empty Host header... except with this one
629 - // weird trick. (Hopefully they don't fix it. It's already
630 - // hard enough to use HTTP over unix sockets.)
622 + // We used to conform to RFC 2616 Section 14.26 which requires
623 + // an empty host header when there is no host, as is the case
624 + // with unix sockets. However, Go required a Host value so we
625 + // used a hack of a space character as the host (it would see
626 + // the Host was non-empty, then trim the space later). As of
627 + // Go 1.20.6 (July 2023), this hack no longer works. See:
628 + // https://github.com/golang/go/issues/60374
629 + // See also the discussion here:
630 + // https://github.com/golang/go/issues/61431
631 //
632 - // An equivalent curl command would be something like:
633 - // $ curl --unix-socket caddy.sock http:/:$REQUEST_URI
634 - req.URL.Host = " "
635 - req.Host = ""
632 + // After that, we now require a Host value of either 127.0.0.1
633 + // or ::1 if one is set. Above I choose to use 127.0.0.1. Even
634 + // though the value should be completely irrelevant (it could be
635 + // "srldkjfsd"), if for some reason the Host *is* used, at least
636 + // we can have some reasonable assurance it will stay on the local
637 + // machine and that browsers, if they ever allow access to unix
638 + // sockets, can still enforce CORS, ensuring it is still coming
639 + // from the local machine.
640 } else {
641 req.Header.Set("Origin", origin)
642 }