@marcel91200 / caddy Mirror · read-only / commits / 32f7dd44
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 12, 15:45 UTC

chore: Update quic-go to v0.42.0 (#6176)

* update quic-go to v0.42.0 * use a rate limiter to control QUIC source address verification * Lint * remove deprecated ListenQUIC * remove number of requests tracking * increase the number of handshakes before source address verification is needed * remove references to request counters * remove deprecated listen* --------- Co-authored-by: Francis Lavoie <lavofr@gmail.com> Co-authored-by: WeidiDeng <weidi_deng@icloud.com>

Marten Seemann committed Mar 22, 2024 at 02:56 UTC 32f7dd44aea270fa6c50bbf80acf5b831fc6ffcd
4 files changed +34 -129
go.mod
+3 -3
@@ -18,7 +18,7 @@ require (
18 github.com/klauspost/cpuid/v2 v2.2.5
19 github.com/mholt/acmez v1.2.0
20 github.com/prometheus/client_golang v1.18.0
21 - github.com/quic-go/quic-go v0.41.0
21 + github.com/quic-go/quic-go v0.42.0
22 github.com/smallstep/certificates v0.25.0
23 github.com/smallstep/nosql v0.6.0
24 github.com/smallstep/truststore v0.12.1
@@ -41,7 +41,7 @@ require (
41 golang.org/x/net v0.21.0
42 golang.org/x/sync v0.5.0
43 golang.org/x/term v0.17.0
44 - google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f
44 + golang.org/x/time v0.5.0
45 gopkg.in/natefinch/lumberjack.v2 v2.2.1
46 gopkg.in/yaml.v3 v3.0.1
47 )
@@ -69,7 +69,7 @@ require (
69 go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect
70 go.opentelemetry.io/contrib/propagators/jaeger v1.17.0 // indirect
71 go.opentelemetry.io/contrib/propagators/ot v1.17.0 // indirect
72 - go.uber.org/mock v0.3.0 // indirect
72 + go.uber.org/mock v0.4.0 // indirect
73 google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f // indirect
74 google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect
75 )
go.sum
+4 -4
@@ -487,8 +487,8 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k
487 github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
488 github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
489 github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
490 -github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k=
491 -github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA=
490 +github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM=
491 +github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
492 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
493 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
494 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@@ -653,8 +653,8 @@ go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
653 go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
654 go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
655 go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
656 -go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
657 -go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
656 +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
657 +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
658 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
659 go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
660 go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
listeners.go
+26 -114
@@ -28,11 +28,11 @@ import (
28 "strings"
29 "sync"
30 "sync/atomic"
31 - "time"
31
32 "github.com/quic-go/quic-go"
33 "github.com/quic-go/quic-go/http3"
34 "go.uber.org/zap"
35 + "golang.org/x/time/rate"
36
37 "github.com/caddyserver/caddy/v2/internal"
38 )
@@ -406,48 +406,12 @@ func JoinNetworkAddress(network, host, port string) string {
406 return a
407 }
408
409 -// DEPRECATED: Use NetworkAddress.Listen instead. This function will likely be changed or removed in the future.
410 -func Listen(network, addr string) (net.Listener, error) {
411 - // a 0 timeout means Go uses its default
412 - return ListenTimeout(network, addr, 0)
413 -}
414 -
415 -// DEPRECATED: Use NetworkAddress.Listen instead. This function will likely be changed or removed in the future.
416 -func ListenTimeout(network, addr string, keepalivePeriod time.Duration) (net.Listener, error) {
417 - netAddr, err := ParseNetworkAddress(JoinNetworkAddress(network, addr, ""))
418 - if err != nil {
419 - return nil, err
420 - }
421 -
422 - ln, err := netAddr.Listen(context.TODO(), 0, net.ListenConfig{KeepAlive: keepalivePeriod})
423 - if err != nil {
424 - return nil, err
425 - }
426 -
427 - return ln.(net.Listener), nil
428 -}
429 -
430 -// DEPRECATED: Use NetworkAddress.Listen instead. This function will likely be changed or removed in the future.
431 -func ListenPacket(network, addr string) (net.PacketConn, error) {
432 - netAddr, err := ParseNetworkAddress(JoinNetworkAddress(network, addr, ""))
433 - if err != nil {
434 - return nil, err
435 - }
436 -
437 - ln, err := netAddr.Listen(context.TODO(), 0, net.ListenConfig{})
438 - if err != nil {
439 - return nil, err
440 - }
441 -
442 - return ln.(net.PacketConn), nil
443 -}
444 -
409 // ListenQUIC returns a quic.EarlyListener suitable for use in a Caddy module.
410 // The network will be transformed into a QUIC-compatible type (if unix, then
411 // unixgram will be used; otherwise, udp will be used).
412 //
413 // NOTE: This API is EXPERIMENTAL and may be changed or removed.
450 -func (na NetworkAddress) ListenQUIC(ctx context.Context, portOffset uint, config net.ListenConfig, tlsConf *tls.Config, activeRequests *int64) (http3.QUICEarlyListener, error) {
414 +func (na NetworkAddress) ListenQUIC(ctx context.Context, portOffset uint, config net.ListenConfig, tlsConf *tls.Config) (http3.QUICEarlyListener, error) {
415 lnKey := listenerKey("quic"+na.Network, na.JoinHostPort(portOffset))
416
417 sharedEarlyListener, _, err := listenerPool.LoadOrNew(lnKey, func() (Destructor, error) {
@@ -468,20 +432,22 @@ func (na NetworkAddress) ListenQUIC(ctx context.Context, portOffset uint, config
432 }
433 }
434
471 - sqs := newSharedQUICState(tlsConf, activeRequests)
435 + sqs := newSharedQUICState(tlsConf)
436 // http3.ConfigureTLSConfig only uses this field and tls App sets this field as well
437 //nolint:gosec
438 quicTlsConfig := &tls.Config{GetConfigForClient: sqs.getConfigForClient}
475 - earlyLn, err := quic.ListenEarly(h3ln, http3.ConfigureTLSConfig(quicTlsConfig), &quic.Config{
476 - Allow0RTT: true,
477 - RequireAddressValidation: func(clientAddr net.Addr) bool {
478 - // TODO: make tunable?
479 - return sqs.getActiveRequests() > 1000
480 - },
481 - })
439 + // Require clients to verify their source address when we're handling more than 1000 handshakes per second.
440 + // TODO: make tunable?
441 + limiter := rate.NewLimiter(1000, 1000)
442 + tr := &quic.Transport{
443 + Conn: h3ln,
444 + VerifySourceAddress: func(addr net.Addr) bool { return !limiter.Allow() },
445 + }
446 + earlyLn, err := tr.ListenEarly(http3.ConfigureTLSConfig(quicTlsConfig), &quic.Config{Allow0RTT: true})
447 if err != nil {
448 return nil, err
449 }
450 + // TODO: figure out when to close the listener and the transport
451 // using the original net.PacketConn to close them properly
452 return &sharedQuicListener{EarlyListener: earlyLn, packetConn: ln, sqs: sqs, key: lnKey}, nil
453 })
@@ -490,47 +456,8 @@ func (na NetworkAddress) ListenQUIC(ctx context.Context, portOffset uint, config
456 }
457
458 sql := sharedEarlyListener.(*sharedQuicListener)
493 - // add current tls.Config to sqs, so GetConfigForClient will always return the latest tls.Config in case of context cancellation,
494 - // and the request counter will reflect current http server
495 - ctx, cancel := sql.sqs.addState(tlsConf, activeRequests)
496 -
497 - return &fakeCloseQuicListener{
498 - sharedQuicListener: sql,
499 - context: ctx,
500 - contextCancel: cancel,
501 - }, nil
502 -}
503 -
504 -// DEPRECATED: Use NetworkAddress.ListenQUIC instead. This function will likely be changed or removed in the future.
505 -// TODO: See if we can find a more elegant solution closer to the new NetworkAddress.Listen API.
506 -func ListenQUIC(ln net.PacketConn, tlsConf *tls.Config, activeRequests *int64) (http3.QUICEarlyListener, error) {
507 - lnKey := listenerKey("quic+"+ln.LocalAddr().Network(), ln.LocalAddr().String())
508 -
509 - sharedEarlyListener, _, err := listenerPool.LoadOrNew(lnKey, func() (Destructor, error) {
510 - sqs := newSharedQUICState(tlsConf, activeRequests)
511 - // http3.ConfigureTLSConfig only uses this field and tls App sets this field as well
512 - //nolint:gosec
513 - quicTlsConfig := &tls.Config{GetConfigForClient: sqs.getConfigForClient}
514 - earlyLn, err := quic.ListenEarly(ln, http3.ConfigureTLSConfig(quicTlsConfig), &quic.Config{
515 - Allow0RTT: true,
516 - RequireAddressValidation: func(clientAddr net.Addr) bool {
517 - // TODO: make tunable?
518 - return sqs.getActiveRequests() > 1000
519 - },
520 - })
521 - if err != nil {
522 - return nil, err
523 - }
524 - return &sharedQuicListener{EarlyListener: earlyLn, sqs: sqs, key: lnKey}, nil
525 - })
526 - if err != nil {
527 - return nil, err
528 - }
529 -
530 - sql := sharedEarlyListener.(*sharedQuicListener)
531 - // add current tls.Config and request counter to sqs, so GetConfigForClient will always return the latest tls.Config in case of context cancellation,
532 - // and the request counter will reflect current http server
533 - ctx, cancel := sql.sqs.addState(tlsConf, activeRequests)
459 + // add current tls.Config to sqs, so GetConfigForClient will always return the latest tls.Config in case of context cancellation
460 + ctx, cancel := sql.sqs.addState(tlsConf)
461
462 return &fakeCloseQuicListener{
463 sharedQuicListener: sql,
@@ -551,25 +478,21 @@ type contextAndCancelFunc struct {
478 context.CancelFunc
479 }
480
554 -// sharedQUICState manages GetConfigForClient and current number of active requests
481 +// sharedQUICState manages GetConfigForClient
482 // see issue: https://github.com/caddyserver/caddy/pull/4849
483 type sharedQUICState struct {
557 - rmu sync.RWMutex
558 - tlsConfs map[*tls.Config]contextAndCancelFunc
559 - requestCounters map[*tls.Config]*int64
560 - activeTlsConf *tls.Config
561 - activeRequestsCounter *int64
484 + rmu sync.RWMutex
485 + tlsConfs map[*tls.Config]contextAndCancelFunc
486 + activeTlsConf *tls.Config
487 }
488
489 // newSharedQUICState creates a new sharedQUICState
565 -func newSharedQUICState(tlsConfig *tls.Config, activeRequests *int64) *sharedQUICState {
490 +func newSharedQUICState(tlsConfig *tls.Config) *sharedQUICState {
491 sqtc := &sharedQUICState{
567 - tlsConfs: make(map[*tls.Config]contextAndCancelFunc),
568 - requestCounters: make(map[*tls.Config]*int64),
569 - activeTlsConf: tlsConfig,
570 - activeRequestsCounter: activeRequests,
492 + tlsConfs: make(map[*tls.Config]contextAndCancelFunc),
493 + activeTlsConf: tlsConfig,
494 }
572 - sqtc.addState(tlsConfig, activeRequests)
495 + sqtc.addState(tlsConfig)
496 return sqtc
497 }
498
@@ -580,17 +503,9 @@ func (sqs *sharedQUICState) getConfigForClient(ch *tls.ClientHelloInfo) (*tls.Co
503 return sqs.activeTlsConf.GetConfigForClient(ch)
504 }
505
583 -// getActiveRequests returns the number of active requests
584 -func (sqs *sharedQUICState) getActiveRequests() int64 {
585 - // Prevent a race when a context is cancelled and active request counter is being changed
586 - sqs.rmu.RLock()
587 - defer sqs.rmu.RUnlock()
588 - return atomic.LoadInt64(sqs.activeRequestsCounter)
589 -}
590 -
506 // addState adds tls.Config and activeRequests to the map if not present and returns the corresponding context and its cancelFunc
592 -// so that when cancelled, the active tls.Config and request counter will change
593 -func (sqs *sharedQUICState) addState(tlsConfig *tls.Config, activeRequests *int64) (context.Context, context.CancelFunc) {
507 +// so that when cancelled, the active tls.Config will change
508 +func (sqs *sharedQUICState) addState(tlsConfig *tls.Config) (context.Context, context.CancelFunc) {
509 sqs.rmu.Lock()
510 defer sqs.rmu.Unlock()
511
@@ -606,19 +521,16 @@ func (sqs *sharedQUICState) addState(tlsConfig *tls.Config, activeRequests *int6
521 defer sqs.rmu.Unlock()
522
523 delete(sqs.tlsConfs, tlsConfig)
609 - delete(sqs.requestCounters, tlsConfig)
524 if sqs.activeTlsConf == tlsConfig {
611 - // select another tls.Config and request counter, if there is none,
525 + // select another tls.Config, if there is none,
526 // related sharedQuicListener will be destroyed anyway
613 - for tc, counter := range sqs.requestCounters {
527 + for tc := range sqs.tlsConfs {
528 sqs.activeTlsConf = tc
615 - sqs.activeRequestsCounter = counter
529 break
530 }
531 }
532 }
533 sqs.tlsConfs[tlsConfig] = contextAndCancelFunc{ctx, wrappedCancel}
621 - sqs.requestCounters[tlsConfig] = activeRequests
534 // there should be at most 2 tls.Configs
535 if len(sqs.tlsConfs) > 2 {
536 Log().Warn("quic listener tls configs are more than 2", zap.Int("number of configs", len(sqs.tlsConfs)))
modules/caddyhttp/server.go
+1 -8
@@ -27,7 +27,6 @@ import (
27 "runtime"
28 "strings"
29 "sync"
30 - "sync/atomic"
30 "time"
31
32 "github.com/caddyserver/certmagic"
@@ -43,8 +42,6 @@ import (
42
43 // Server describes an HTTP server.
44 type Server struct {
46 - activeRequests int64 // accessed atomically
47 -
45 // Socket addresses to which to bind listeners. Accepts
46 // [network addresses](/docs/conventions#network-addresses)
47 // that may include port ranges. Listener addresses must
@@ -274,10 +271,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
271
272 // advertise HTTP/3, if enabled
273 if s.h3server != nil {
277 - // keep track of active requests for QUIC transport purposes
278 - atomic.AddInt64(&s.activeRequests, 1)
279 - defer atomic.AddInt64(&s.activeRequests, -1)
280 -
274 if r.ProtoMajor < 3 {
275 err := s.h3server.SetQuicHeaders(w.Header())
276 if err != nil {
@@ -567,7 +560,7 @@ func (s *Server) findLastRouteWithHostMatcher() int {
560 // the listener, with Server s as the handler.
561 func (s *Server) serveHTTP3(addr caddy.NetworkAddress, tlsCfg *tls.Config) error {
562 addr.Network = getHTTP3Network(addr.Network)
570 - h3ln, err := addr.ListenQUIC(s.ctx, 0, net.ListenConfig{}, tlsCfg, &s.activeRequests)
563 + h3ln, err := addr.ListenQUIC(s.ctx, 0, net.ListenConfig{}, tlsCfg)
564 if err != nil {
565 return fmt.Errorf("starting HTTP/3 QUIC listener: %v", err)
566 }