allowedOrigins returns a list of origins that are allowed. If admin.Origins is nil (null), the provided listen address will be used as the default origin. If admin.Origins is empty, no origins will be allowed, effectively bricking the endpoint for non-unix-socket endpoints, but whatever.
(addr NetworkAddress)
| 298 | // empty, no origins will be allowed, effectively bricking the |
| 299 | // endpoint for non-unix-socket endpoints, but whatever. |
| 300 | func (admin AdminConfig) allowedOrigins(addr NetworkAddress) []*url.URL { |
| 301 | uniqueOrigins := make(map[string]struct{}) |
| 302 | for _, o := range admin.Origins { |
| 303 | uniqueOrigins[o] = struct{}{} |
| 304 | } |
| 305 | // RFC 2616, Section 14.26: |
| 306 | // "A client MUST include a Host header field in all HTTP/1.1 request |
| 307 | // messages. If the requested URI does not include an Internet host |
| 308 | // name for the service being requested, then the Host header field MUST |
| 309 | // be given with an empty value." |
| 310 | // |
| 311 | // UPDATE July 2023: Go broke this by patching a minor security bug in 1.20.6. |
| 312 | // Understandable, but frustrating. See: |
| 313 | // https://github.com/golang/go/issues/60374 |
| 314 | // See also the discussion here: |
| 315 | // https://github.com/golang/go/issues/61431 |
| 316 | // |
| 317 | // We can no longer conform to RFC 2616 Section 14.26 from either Go or curl |
| 318 | // in purity. (Curl allowed no host between 7.40 and 7.50, but now requires a |
| 319 | // bogus host; see https://superuser.com/a/925610.) If we disable Host/Origin |
| 320 | // security checks, the infosec community assures me that it is secure to do |
| 321 | // so, because: |
| 322 | // |
| 323 | // 1) Browsers do not allow access to unix sockets |
| 324 | // 2) DNS is irrelevant to unix sockets |
| 325 | // |
| 326 | // If either of those two statements ever fail to hold true, it is not the |
| 327 | // fault of Caddy. |
| 328 | // |
| 329 | // Thus, we do not fill out allowed origins and do not enforce Host |
| 330 | // requirements for unix sockets. Enforcing it leads to confusion and |
| 331 | // frustration, when UDS have their own permissions from the OS. |
| 332 | // Enforcing host requirements here is effectively security theater, |
| 333 | // and a false sense of security. |
| 334 | // |
| 335 | // See also the discussion in #6832. |
| 336 | if admin.Origins == nil && !addr.IsUnixNetwork() && !addr.IsFdNetwork() { |
| 337 | if addr.isLoopback() { |
| 338 | uniqueOrigins[net.JoinHostPort("localhost", addr.port())] = struct{}{} |
| 339 | uniqueOrigins[net.JoinHostPort("::1", addr.port())] = struct{}{} |
| 340 | uniqueOrigins[net.JoinHostPort("127.0.0.1", addr.port())] = struct{}{} |
| 341 | } else { |
| 342 | uniqueOrigins[addr.JoinHostPort(0)] = struct{}{} |
| 343 | } |
| 344 | } |
| 345 | allowed := make([]*url.URL, 0, len(uniqueOrigins)) |
| 346 | for originStr := range uniqueOrigins { |
| 347 | var origin *url.URL |
| 348 | if strings.Contains(originStr, "://") { |
| 349 | var err error |
| 350 | origin, err = url.Parse(originStr) |
| 351 | if err != nil { |
| 352 | continue |
| 353 | } |
| 354 | origin.Path = "" |
| 355 | origin.RawPath = "" |
| 356 | origin.Fragment = "" |
| 357 | origin.RawFragment = "" |