@marcel91200 / caddy Mirror · read-only
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 11, 01:00 UTC

master
go 679 lines 21.5 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package caddyhttp
16
17 import (
18 "bytes"
19 "context"
20 "crypto/ecdsa"
21 "crypto/ed25519"
22 "crypto/rsa"
23 "crypto/sha256"
24 "crypto/tls"
25 "crypto/x509"
26 "encoding/asn1"
27 "encoding/base64"
28 "encoding/pem"
29 "errors"
30 "fmt"
31 "io"
32 "net"
33 "net/http"
34 "net/netip"
35 "net/textproto"
36 "net/url"
37 "path"
38 "strconv"
39 "strings"
40 "time"
41
42 "github.com/google/uuid"
43 "go.uber.org/zap"
44
45 "github.com/caddyserver/caddy/v2"
46 "github.com/caddyserver/caddy/v2/modules/caddytls"
47 )
48
49 // NewTestReplacer creates a replacer for an http.Request
50 // for use in tests that are not in this package
51 func NewTestReplacer(req *http.Request) *caddy.Replacer {
52 repl := caddy.NewReplacer()
53 ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl)
54 *req = *req.WithContext(ctx)
55 addHTTPVarsToReplacer(repl, req, nil)
56 return repl
57 }
58
59 func addHTTPVarsToReplacer(repl *caddy.Replacer, req *http.Request, w http.ResponseWriter) {
60 SetVar(req.Context(), "start_time", time.Now())
61
62 httpVars := func(key string) (any, bool) {
63 if req != nil {
64 // query string parameters
65 if strings.HasPrefix(key, reqURIQueryReplPrefix) {
66 vals := req.URL.Query()[key[len(reqURIQueryReplPrefix):]]
67 // always return true, since the query param might
68 // be present only in some requests
69 return strings.Join(vals, ","), true
70 }
71
72 // request header fields
73 if strings.HasPrefix(key, reqHeaderReplPrefix) {
74 field := key[len(reqHeaderReplPrefix):]
75 vals := req.Header[textproto.CanonicalMIMEHeaderKey(field)]
76 // always return true, since the header field might
77 // be present only in some requests
78 return strings.Join(vals, ","), true
79 }
80
81 // cookies
82 if strings.HasPrefix(key, reqCookieReplPrefix) {
83 name := key[len(reqCookieReplPrefix):]
84 for _, cookie := range req.Cookies() {
85 if strings.EqualFold(name, cookie.Name) {
86 // always return true, since the cookie might
87 // be present only in some requests
88 return cookie.Value, true
89 }
90 }
91 return "", true
92 }
93
94 // http.request.tls.*
95 if strings.HasPrefix(key, reqTLSReplPrefix) {
96 return getReqTLSReplacement(req, key)
97 }
98
99 switch key {
100 case "http.request.method":
101 return req.Method, true
102 case "http.request.scheme":
103 if req.TLS != nil {
104 return "https", true
105 }
106 return "http", true
107 case "http.request.proto":
108 return req.Proto, true
109 case "http.request.proto_name":
110 if req.ProtoMajor == 2 {
111 return "HTTP/2", true
112 }
113 if req.ProtoMajor == 3 {
114 return "HTTP/3", true
115 }
116 return req.Proto, true
117 case "http.request.host":
118 host, _, err := net.SplitHostPort(req.Host)
119 if err != nil {
120 return req.Host, true // OK; there probably was no port
121 }
122 return host, true
123 case "http.request.port":
124 _, port, _ := net.SplitHostPort(req.Host)
125 if portNum, err := strconv.Atoi(port); err == nil {
126 return portNum, true
127 }
128 return port, true
129 case "http.request.hostport":
130 return req.Host, true
131 case "http.request.local":
132 localAddr, _ := req.Context().Value(http.LocalAddrContextKey).(net.Addr)
133 return localAddr.String(), true
134 case "http.request.local.host":
135 localAddr, _ := req.Context().Value(http.LocalAddrContextKey).(net.Addr)
136 host, _, err := net.SplitHostPort(localAddr.String())
137 if err != nil {
138 // localAddr is host:port for tcp and udp sockets and /unix/socket.path
139 // for unix sockets. net.SplitHostPort only operates on tcp and udp sockets,
140 // not unix sockets and will fail with the latter.
141 // We assume when net.SplitHostPort fails, localAddr is a unix socket and thus
142 // already "split" and save to return.
143 return localAddr, true
144 }
145 return host, true
146 case "http.request.local.port":
147 localAddr, _ := req.Context().Value(http.LocalAddrContextKey).(net.Addr)
148 _, port, _ := net.SplitHostPort(localAddr.String())
149 if portNum, err := strconv.Atoi(port); err == nil {
150 return portNum, true
151 }
152 return port, true
153 case "http.request.remote":
154 if req.TLS != nil && !req.TLS.HandshakeComplete {
155 // without a complete handshake (QUIC "early data") we can't trust the remote IP address to not be spoofed
156 return nil, true
157 }
158 return req.RemoteAddr, true
159 case "http.request.remote.host":
160 if req.TLS != nil && !req.TLS.HandshakeComplete {
161 // without a complete handshake (QUIC "early data") we can't trust the remote IP address to not be spoofed
162 return nil, true
163 }
164 host, _, err := net.SplitHostPort(req.RemoteAddr)
165 if err != nil {
166 // req.RemoteAddr is host:port for tcp and udp sockets and /unix/socket.path
167 // for unix sockets. net.SplitHostPort only operates on tcp and udp sockets,
168 // not unix sockets and will fail with the latter.
169 // We assume when net.SplitHostPort fails, req.RemoteAddr is a unix socket
170 // and thus already "split" and save to return.
171 return req.RemoteAddr, true
172 }
173 return host, true
174 case "http.request.remote.port":
175 _, port, _ := net.SplitHostPort(req.RemoteAddr)
176 if portNum, err := strconv.Atoi(port); err == nil {
177 return portNum, true
178 }
179 return port, true
180
181 // current URI, including any internal rewrites
182 case "http.request.uri":
183 return req.URL.RequestURI(), true
184 case "http.request.uri_escaped":
185 return url.QueryEscape(req.URL.RequestURI()), true
186 case "http.request.uri.path":
187 return req.URL.Path, true
188 case "http.request.uri.path_escaped":
189 return url.QueryEscape(req.URL.Path), true
190 case "http.request.uri.path.file":
191 _, file := path.Split(req.URL.Path)
192 return file, true
193 case "http.request.uri.path.dir":
194 dir, _ := path.Split(req.URL.Path)
195 return dir, true
196 case "http.request.uri.path.file.base":
197 return strings.TrimSuffix(path.Base(req.URL.Path), path.Ext(req.URL.Path)), true
198 case "http.request.uri.path.file.ext":
199 return path.Ext(req.URL.Path), true
200 case "http.request.uri.query":
201 return req.URL.RawQuery, true
202 case "http.request.uri.query_escaped":
203 return url.QueryEscape(req.URL.RawQuery), true
204 case "http.request.uri.prefixed_query":
205 if req.URL.RawQuery == "" {
206 return "", true
207 }
208 return "?" + req.URL.RawQuery, true
209 case "http.request.duration":
210 start := GetVar(req.Context(), "start_time").(time.Time)
211 return time.Since(start), true
212 case "http.request.duration_ms":
213 start := GetVar(req.Context(), "start_time").(time.Time)
214 return time.Since(start).Seconds() * 1e3, true // multiply seconds to preserve decimal (see #4666)
215
216 case "http.request.uuid":
217 // fetch the UUID for this request, generating and caching it
218 // on first access so requests that never reference the UUID
219 // don't pay for the allocation
220 id, ok := GetVar(req.Context(), "uuid").(*requestID)
221 if !ok {
222 id = new(requestID)
223 SetVar(req.Context(), "uuid", id)
224 }
225
226 // set it to this request's access log
227 extra := req.Context().Value(ExtraLogFieldsCtxKey).(*ExtraLogFields)
228 extra.Set(zap.String("uuid", id.String()))
229
230 return id.String(), true
231
232 case "http.request.body":
233 if req.Body == nil {
234 return "", true
235 }
236 body, err := readRequestBodyForPlaceholder(req)
237 if err != nil {
238 return err, true
239 }
240 return string(body), true
241
242 case "http.request.body_base64":
243 if req.Body == nil {
244 return "", true
245 }
246 body, err := readRequestBodyForPlaceholder(req)
247 if err != nil {
248 return err, true
249 }
250 return base64.StdEncoding.EncodeToString(body), true
251
252 // original request, before any internal changes
253 case "http.request.orig_method":
254 or, _ := req.Context().Value(OriginalRequestCtxKey).(http.Request)
255 return or.Method, true
256 case "http.request.orig_uri":
257 or, _ := req.Context().Value(OriginalRequestCtxKey).(http.Request)
258 return or.RequestURI, true
259 case "http.request.orig_uri.path":
260 or, _ := req.Context().Value(OriginalRequestCtxKey).(http.Request)
261 return or.URL.Path, true
262 case "http.request.orig_uri.path.file":
263 or, _ := req.Context().Value(OriginalRequestCtxKey).(http.Request)
264 _, file := path.Split(or.URL.Path)
265 return file, true
266 case "http.request.orig_uri.path.file_escaped":
267 // split the escaped path rather than escaping the decoded
268 // file name, since an encoded slash (%2F) in the request
269 // is part of the last segment, not a separator
270 or, _ := req.Context().Value(OriginalRequestCtxKey).(http.Request)
271 _, file := path.Split(or.URL.EscapedPath())
272 return file, true
273 case "http.request.orig_uri.path.dir":
274 or, _ := req.Context().Value(OriginalRequestCtxKey).(http.Request)
275 dir, _ := path.Split(or.URL.Path)
276 return dir, true
277 case "http.request.orig_uri.query":
278 or, _ := req.Context().Value(OriginalRequestCtxKey).(http.Request)
279 return or.URL.RawQuery, true
280 case "http.request.orig_uri.prefixed_query":
281 or, _ := req.Context().Value(OriginalRequestCtxKey).(http.Request)
282 if or.URL.RawQuery == "" {
283 return "", true
284 }
285 return "?" + or.URL.RawQuery, true
286 }
287
288 // remote IP range/prefix (e.g. keep top 24 bits of 1.2.3.4 => "1.2.3.0/24")
289 // syntax: "/V4,V6" where V4 = IPv4 bits, and V6 = IPv6 bits; if no comma, then same bit length used for both
290 // (EXPERIMENTAL)
291 if strings.HasPrefix(key, "http.request.remote.host/") {
292 host, _, err := net.SplitHostPort(req.RemoteAddr)
293 if err != nil {
294 host = req.RemoteAddr // assume no port, I guess?
295 }
296 addr, err := netip.ParseAddr(host)
297 if err != nil {
298 return host, true // not an IP address
299 }
300 // extract the bits from the end of the placeholder (start after "/") then split on ","
301 bitsBoth := key[strings.Index(key, "/")+1:]
302 ipv4BitsStr, ipv6BitsStr, cutOK := strings.Cut(bitsBoth, ",")
303 bitsStr := ipv4BitsStr
304 if addr.Is6() && cutOK {
305 bitsStr = ipv6BitsStr
306 }
307 // convert to integer then compute prefix
308 bits, err := strconv.Atoi(bitsStr)
309 if err != nil {
310 return "", true
311 }
312 prefix, err := addr.Prefix(bits)
313 if err != nil {
314 return "", true
315 }
316 return prefix.String(), true
317 }
318
319 // hostname labels (case insensitive, so normalize to lowercase)
320 if strings.HasPrefix(key, reqHostLabelsReplPrefix) {
321 idxStr := key[len(reqHostLabelsReplPrefix):]
322 idx, err := strconv.Atoi(idxStr)
323 if err != nil || idx < 0 {
324 return "", false
325 }
326 reqHost, _, err := net.SplitHostPort(req.Host)
327 if err != nil {
328 reqHost = req.Host // OK; assume there was no port
329 }
330 hostLabels := strings.Split(reqHost, ".")
331 if idx >= len(hostLabels) {
332 return "", true
333 }
334 return strings.ToLower(hostLabels[len(hostLabels)-idx-1]), true
335 }
336
337 // path parts
338 if strings.HasPrefix(key, reqURIPathReplPrefix) {
339 idxStr := key[len(reqURIPathReplPrefix):]
340 idx, err := strconv.Atoi(idxStr)
341 if err != nil {
342 return "", false
343 }
344 pathParts := strings.Split(req.URL.Path, "/")
345 if len(pathParts) > 0 && pathParts[0] == "" {
346 pathParts = pathParts[1:]
347 }
348 if idx < 0 {
349 return "", false
350 }
351 if idx >= len(pathParts) {
352 return "", true
353 }
354 return pathParts[idx], true
355 }
356
357 // orig uri path parts
358 if strings.HasPrefix(key, reqOrigURIPathReplPrefix) {
359 idxStr := key[len(reqOrigURIPathReplPrefix):]
360 idx, err := strconv.Atoi(idxStr)
361 if err != nil {
362 return "", false
363 }
364 or, _ := req.Context().Value(OriginalRequestCtxKey).(http.Request)
365 pathParts := strings.Split(or.URL.Path, "/")
366 if len(pathParts) > 0 && pathParts[0] == "" {
367 pathParts = pathParts[1:]
368 }
369 if idx < 0 {
370 return "", false
371 }
372 if idx >= len(pathParts) {
373 return "", true
374 }
375 return pathParts[idx], true
376 }
377
378 // middleware variables
379 if strings.HasPrefix(key, varsReplPrefix) {
380 varName := key[len(varsReplPrefix):]
381 raw := GetVar(req.Context(), varName)
382 // variables can be dynamic, so always return true
383 // even when it may not be set; treat as empty then
384 return raw, true
385 }
386 }
387
388 if w != nil {
389 // response header fields
390 if strings.HasPrefix(key, respHeaderReplPrefix) {
391 field := key[len(respHeaderReplPrefix):]
392 vals := w.Header()[textproto.CanonicalMIMEHeaderKey(field)]
393 // always return true, since the header field might
394 // be present only in some responses
395 return strings.Join(vals, ","), true
396 }
397 }
398
399 switch key {
400 case "http.shutting_down":
401 server := req.Context().Value(ServerCtxKey).(*Server)
402 return server.shutdownAt.Load() != nil, true
403 case "http.time_until_shutdown":
404 server := req.Context().Value(ServerCtxKey).(*Server)
405 t := server.shutdownAt.Load()
406 if t == nil {
407 return nil, true
408 }
409 return time.Until(*t), true
410 }
411
412 return nil, false
413 }
414
415 repl.Map(httpVars)
416 }
417
418 // RequestBodyLimitError is returned by the {http.request.body} and
419 // {http.request.body_base64} placeholders when reading the request body
420 // exceeded a request_body max_size limit. Placeholder consumers such as
421 // templates and the vars matchers recognize exactly this condition and no
422 // other error, so unrelated errors keep their existing behavior.
423 type RequestBodyLimitError struct {
424 Err error // the underlying reason, e.g. *http.MaxBytesError
425 }
426
427 // Error returns the reason the request body could not be read in full.
428 func (e RequestBodyLimitError) Error() string {
429 return fmt.Sprintf("request body: %v", e.Err)
430 }
431
432 // StatusCode returns 413 to match the request_body handler's response to an
433 // oversized body.
434 func (e RequestBodyLimitError) StatusCode() int {
435 return http.StatusRequestEntityTooLarge
436 }
437
438 // Unwrap returns the underlying error value. See the `errors` package for info.
439 func (e RequestBodyLimitError) Unwrap() error { return e.Err }
440
441 // readRequestBodyForPlaceholder reads the request body into a buffer for the
442 // {http.request.body} and {http.request.body_base64} placeholders. Usually the
443 // body is copied verbatim and the buffered replacement is installed on req. If
444 // the read fails because the request_body max_size limit was exceeded, the
445 // request_body handler wraps the http.MaxBytesError into a HandlerError
446 // carrying status 413; here that error is converted to a RequestBodyLimitError,
447 // which the placeholder consumers recognize, instead of silently returning the
448 // truncated prefix as though it were the complete body. Only a max_size-driven
449 // handler error is converted; any other read error is still ignored, as it was
450 // before, because the request is probably not going to be served normally
451 // anyway and we want to keep the placeholder's behavior unchanged in that case.
452 func readRequestBodyForPlaceholder(req *http.Request) ([]byte, error) {
453 // normally net/http will close the body for us, but since we are replacing
454 // it with a fake one, we have to ensure we close the real body ourselves
455 defer req.Body.Close()
456
457 // read the request body into a buffer (can't pool because we don't know its
458 // lifetime and would have to make a copy anyway)
459 buf := new(bytes.Buffer)
460 _, err := io.Copy(buf, req.Body)
461 if err != nil {
462 var handlerErr HandlerError
463 var maxBytes *http.MaxBytesError
464 if errors.As(err, &handlerErr) && errors.As(handlerErr.Err, &maxBytes) {
465 // return the dedicated body-limit marker carrying only the
466 // MaxBytesError; the generated ID and stack trace of the handler
467 // error are intentionally dropped so a placeholder stringified
468 // into a response body (e.g. respond {http.request.body}) cannot
469 // leak the call stack to the client. consumers recognize this
470 // marker and no other error, so unrelated HandlerError values
471 // keep their existing behavior.
472 return nil, RequestBodyLimitError{Err: maxBytes}
473 }
474 }
475
476 // replace the real body with the buffered data
477 req.Body = io.NopCloser(buf)
478 return buf.Bytes(), nil
479 }
480
481 func getReqTLSReplacement(req *http.Request, key string) (any, bool) {
482 if req == nil {
483 return nil, false
484 }
485 state := req.TLS
486 if state == nil {
487 state = new(tls.ConnectionState)
488 }
489 value, known := getTLSReplacement(state, key)
490 if req.TLS == nil {
491 // Use the same field parser to recognise valid placeholders but
492 // do not substitute values from the empty state for a plain HTTP request.
493 return nil, known
494 }
495 return value, known
496 }
497
498 func getTLSReplacement(state *tls.ConnectionState, key string) (any, bool) {
499 if len(key) < len(reqTLSReplPrefix) {
500 return nil, false
501 }
502
503 field := strings.ToLower(key[len(reqTLSReplPrefix):])
504
505 if strings.HasPrefix(field, "client.") {
506 cert := getTLSPeerCert(state)
507 if cert == nil {
508 // Instead of returning (nil, false) here, we set it to a dummy
509 // value to fix #7530. This way, even if there is no client cert,
510 // evaluating placeholders with ReplaceKnown() will still remove
511 // the placeholder, which would be expected. It is not expected
512 // for the placeholder to sometimes get removed based on whether
513 // the client presented a cert. We also do not return true here
514 // because we probably should remain accurate about whether a
515 // placeholder is, in fact, known or not.
516 // (This allocation may be slightly inefficient.)
517 cert = new(x509.Certificate)
518 }
519
520 // subject alternate names (SANs)
521 if strings.HasPrefix(field, "client.san.") {
522 field = field[len("client.san."):]
523 var fieldName string
524 var fieldValue any
525 switch {
526 case strings.HasPrefix(field, "dns_names"):
527 fieldName = "dns_names"
528 fieldValue = cert.DNSNames
529 case strings.HasPrefix(field, "emails"):
530 fieldName = "emails"
531 fieldValue = cert.EmailAddresses
532 case strings.HasPrefix(field, "ips"):
533 fieldName = "ips"
534 fieldValue = cert.IPAddresses
535 case strings.HasPrefix(field, "uris"):
536 fieldName = "uris"
537 fieldValue = cert.URIs
538 default:
539 return nil, false
540 }
541 field = field[len(fieldName):]
542
543 // if no index was specified, return the whole list
544 if field == "" {
545 return fieldValue, true
546 }
547 if len(field) < 2 || field[0] != '.' {
548 return nil, false
549 }
550 field = field[1:] // trim '.' between field name and index
551
552 // get the numeric index
553 idx, err := strconv.Atoi(field)
554 if err != nil || idx < 0 {
555 return nil, false
556 }
557
558 // access the indexed element and return it
559 switch v := fieldValue.(type) {
560 case []string:
561 if idx >= len(v) {
562 return nil, true
563 }
564 return v[idx], true
565 case []net.IP:
566 if idx >= len(v) {
567 return nil, true
568 }
569 return v[idx], true
570 case []*url.URL:
571 if idx >= len(v) {
572 return nil, true
573 }
574 return v[idx], true
575 }
576 }
577
578 switch field {
579 case "client.fingerprint":
580 return fmt.Sprintf("%x", sha256.Sum256(cert.Raw)), true
581 case "client.public_key", "client.public_key_sha256":
582 if cert.PublicKey == nil {
583 return nil, true
584 }
585 pubKeyBytes, err := marshalPublicKey(cert.PublicKey)
586 if err != nil {
587 return nil, true
588 }
589 if strings.HasSuffix(field, "_sha256") {
590 return fmt.Sprintf("%x", sha256.Sum256(pubKeyBytes)), true
591 }
592 return fmt.Sprintf("%x", pubKeyBytes), true
593 case "client.issuer":
594 return cert.Issuer, true
595 case "client.serial":
596 return cert.SerialNumber, true
597 case "client.subject":
598 return cert.Subject, true
599 case "client.certificate_pem":
600 block := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
601 return pem.EncodeToMemory(&block), true
602 case "client.certificate_der_base64":
603 return base64.StdEncoding.EncodeToString(cert.Raw), true
604 default:
605 return nil, false
606 }
607 }
608
609 switch field {
610 case "version":
611 return caddytls.ProtocolName(state.Version), true
612 case "cipher_suite":
613 return tls.CipherSuiteName(state.CipherSuite), true
614 case "resumed":
615 return state.DidResume, true
616 case "proto":
617 return state.NegotiatedProtocol, true
618 case "proto_mutual":
619 // req.TLS.NegotiatedProtocolIsMutual is deprecated - it's always true.
620 return true, true
621 case "server_name":
622 return state.ServerName, true
623 case "ech":
624 return state.ECHAccepted, true
625 }
626 return nil, false
627 }
628
629 // marshalPublicKey returns the byte encoding of pubKey.
630 func marshalPublicKey(pubKey any) ([]byte, error) {
631 switch key := pubKey.(type) {
632 case *rsa.PublicKey:
633 return asn1.Marshal(key)
634 case *ecdsa.PublicKey:
635 e, err := key.ECDH()
636 if err != nil {
637 return nil, err
638 }
639 return e.Bytes(), nil
640 case ed25519.PublicKey:
641 return key, nil
642 }
643 return nil, fmt.Errorf("unrecognized public key type: %T", pubKey)
644 }
645
646 // getTLSPeerCert retrieves the first peer certificate from a TLS session.
647 // Returns nil if no peer cert is in use.
648 func getTLSPeerCert(cs *tls.ConnectionState) *x509.Certificate {
649 if len(cs.PeerCertificates) == 0 {
650 return nil
651 }
652 return cs.PeerCertificates[0]
653 }
654
655 type requestID struct {
656 value string
657 }
658
659 // Lazy generates UUID string or return cached value if present
660 func (rid *requestID) String() string {
661 if rid.value == "" {
662 if id, err := uuid.NewRandom(); err == nil {
663 rid.value = id.String()
664 }
665 }
666 return rid.value
667 }
668
669 const (
670 reqCookieReplPrefix = "http.request.cookie."
671 reqHeaderReplPrefix = "http.request.header."
672 reqHostLabelsReplPrefix = "http.request.host.labels."
673 reqTLSReplPrefix = "http.request.tls."
674 reqURIPathReplPrefix = "http.request.uri.path."
675 reqURIQueryReplPrefix = "http.request.uri.query."
676 respHeaderReplPrefix = "http.response.header."
677 varsReplPrefix = "http.vars."
678 reqOrigURIPathReplPrefix = "http.request.orig_uri.path."
679 )