Merge branch 'cloudflare:master' into master

This commit is contained in:
Areg Vrtanesyan 2025-09-03 11:49:14 +01:00 committed by GitHub
commit 0de8a73541
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 313 additions and 155 deletions

View File

@ -1,3 +1,13 @@
2025.8.1
- 2025-08-19 AUTH-7480 update fed callback url for login helper
- 2025-08-19 CUSTESC-53681: Correct QUIC connection management for datagram handlers
- 2025-08-12 AUTH-7260: Add support for login interstitial auto closure
2025.8.0
- 2025-08-07 vuln: Fix GO-2025-3770 vulnerability
- 2025-07-23 TUN-9583: set proper url and hostname for cloudflared tail command
- 2025-07-07 TUN-9542: Remove unsupported Debian-based releases
2025.7.0 2025.7.0
- 2025-07-03 TUN-9540: Use numeric user id for Dockerfiles - 2025-07-03 TUN-9540: Use numeric user id for Dockerfiles
- 2025-07-01 TUN-9161: Remove P256Kyber768Draft00PQKex curve from nonFips curve preferences - 2025-07-01 TUN-9161: Remove P256Kyber768Draft00PQKex curve from nonFips curve preferences

View File

@ -31,6 +31,8 @@ type StartOptions struct {
Headers http.Header Headers http.Header
Host string Host string
TLSClientConfig *tls.Config TLSClientConfig *tls.Config
AutoCloseInterstitial bool
IsFedramp bool
} }
// Connection wraps up all the needed functions to forward over the tunnel // Connection wraps up all the needed functions to forward over the tunnel
@ -46,7 +48,6 @@ type StdinoutStream struct{}
// Read will read from Stdin // Read will read from Stdin
func (c *StdinoutStream) Read(p []byte) (int, error) { func (c *StdinoutStream) Read(p []byte) (int, error) {
return os.Stdin.Read(p) return os.Stdin.Read(p)
} }
// Write will write to Stdout // Write will write to Stdout
@ -139,7 +140,7 @@ func BuildAccessRequest(options *StartOptions, log *zerolog.Logger) (*http.Reque
return nil, err return nil, err
} }
token, err := token.FetchTokenWithRedirect(req.URL, options.AppInfo, log) token, err := token.FetchTokenWithRedirect(req.URL, options.AppInfo, options.AutoCloseInterstitial, options.IsFedramp, log)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -47,6 +47,7 @@ func StartForwarder(forwarder config.Forwarder, shutdown <-chan struct{}, log *z
options := &carrier.StartOptions{ options := &carrier.StartOptions{
OriginURL: forwarder.URL, OriginURL: forwarder.URL,
Headers: headers, //TODO: TUN-2688 support custom headers from config file Headers: headers, //TODO: TUN-2688 support custom headers from config file
IsFedramp: forwarder.IsFedramp,
} }
// we could add a cmd line variable for this bool if we want the SOCK5 server to be on the client side // we could add a cmd line variable for this bool if we want the SOCK5 server to be on the client side
@ -92,6 +93,7 @@ func ssh(c *cli.Context) error {
OriginURL: url.String(), OriginURL: url.String(),
Headers: headers, Headers: headers,
Host: url.Host, Host: url.Host,
IsFedramp: c.Bool(fedrampFlag),
} }
if connectTo := c.String(sshConnectTo); connectTo != "" { if connectTo := c.String(sshConnectTo); connectTo != "" {

View File

@ -51,6 +51,7 @@ Host {{.Hostname}}
ProxyCommand {{.Cloudflared}} access ssh --hostname %h ProxyCommand {{.Cloudflared}} access ssh --hostname %h
{{end}} {{end}}
` `
fedrampFlag = "fedramp"
) )
const sentryDSN = "https://56a9c9fa5c364ab28f34b14f35ea0f1b@sentry.io/189878" const sentryDSN = "https://56a9c9fa5c364ab28f34b14f35ea0f1b@sentry.io/189878"
@ -79,6 +80,10 @@ func Commands() []*cli.Command {
Aliases: []string{"forward"}, Aliases: []string{"forward"},
Category: "Access", Category: "Access",
Usage: "access <subcommand>", Usage: "access <subcommand>",
Flags: []cli.Flag{&cli.BoolFlag{
Name: fedrampFlag,
Usage: "use when performing operations in fedramp account",
}},
Description: `Cloudflare Access protects internal resources by securing, authenticating and monitoring access Description: `Cloudflare Access protects internal resources by securing, authenticating and monitoring access
per-user and by application. With Cloudflare Access, only authenticated users with the required permissions are per-user and by application. With Cloudflare Access, only authenticated users with the required permissions are
able to reach sensitive resources. The commands provided here allow you to interact with Access protected able to reach sensitive resources. The commands provided here allow you to interact with Access protected
@ -104,6 +109,10 @@ func Commands() []*cli.Command {
Name: "no-verbose", Name: "no-verbose",
Usage: "print only the jwt to stdout", Usage: "print only the jwt to stdout",
}, },
&cli.BoolFlag{
Name: "auto-close",
Usage: "automatically close the auth interstitial after action",
},
&cli.StringFlag{ &cli.StringFlag{
Name: appURLFlag, Name: appURLFlag,
}, },
@ -322,7 +331,7 @@ func curl(c *cli.Context) error {
log.Info().Msg("You don't have an Access token set. Please run access token <access application> to fetch one.") log.Info().Msg("You don't have an Access token set. Please run access token <access application> to fetch one.")
return run("curl", cmdArgs...) return run("curl", cmdArgs...)
} }
tok, err = token.FetchToken(appURL, appInfo, log) tok, err = token.FetchToken(appURL, appInfo, c.Bool(cfdflags.AutoCloseInterstitial), c.Bool(fedrampFlag), log)
if err != nil { if err != nil {
log.Err(err).Msg("Failed to refresh token") log.Err(err).Msg("Failed to refresh token")
return err return err
@ -442,7 +451,7 @@ func sshGen(c *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
cfdToken, err := token.FetchTokenWithRedirect(fetchTokenURL, appInfo, log) cfdToken, err := token.FetchTokenWithRedirect(fetchTokenURL, appInfo, c.Bool(cfdflags.AutoCloseInterstitial), c.Bool(fedrampFlag), log)
if err != nil { if err != nil {
return err return err
} }
@ -542,7 +551,7 @@ func verifyTokenAtEdge(appUrl *url.URL, appInfo *token.AppInfo, c *cli.Context,
if c.IsSet(sshTokenSecretFlag) { if c.IsSet(sshTokenSecretFlag) {
headers.Add(cfAccessClientSecretHeader, c.String(sshTokenSecretFlag)) headers.Add(cfAccessClientSecretHeader, c.String(sshTokenSecretFlag))
} }
options := &carrier.StartOptions{AppInfo: appInfo, OriginURL: appUrl.String(), Headers: headers} options := &carrier.StartOptions{AppInfo: appInfo, OriginURL: appUrl.String(), Headers: headers, AutoCloseInterstitial: c.Bool(cfdflags.AutoCloseInterstitial), IsFedramp: c.Bool(fedrampFlag)}
if valid, err := isTokenValid(options, log); err != nil { if valid, err := isTokenValid(options, log); err != nil {
return err return err

View File

@ -163,4 +163,7 @@ const (
// Management hostname to signify incoming management requests // Management hostname to signify incoming management requests
ManagementHostname = "management-hostname" ManagementHostname = "management-hostname"
// Automatically close the login interstitial browser window after the user makes a decision.
AutoCloseInterstitial = "auto-close"
) )

View File

@ -12,6 +12,7 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
cfdflags "github.com/cloudflare/cloudflared/cmd/cloudflared/flags"
"github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/config"
"github.com/cloudflare/cloudflared/credentials" "github.com/cloudflare/cloudflared/credentials"
"github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/logger"
@ -21,9 +22,8 @@ import (
const ( const (
baseLoginURL = "https://dash.cloudflare.com/argotunnel" baseLoginURL = "https://dash.cloudflare.com/argotunnel"
callbackURL = "https://login.cloudflareaccess.org/" callbackURL = "https://login.cloudflareaccess.org/"
// For now these are the same but will change in the future once we know which URLs to use (TUN-8872) fedBaseLoginURL = "https://dash.fed.cloudflare.com/argotunnel"
fedBaseLoginURL = "https://dash.cloudflare.com/argotunnel" fedCallbackStoreURL = "https://login.fed.cloudflareaccess.org/"
fedCallbackStoreURL = "https://login.cloudflareaccess.org/"
fedRAMPParamName = "fedramp" fedRAMPParamName = "fedramp"
loginURLParamName = "loginURL" loginURLParamName = "loginURL"
callbackURLParamName = "callbackURL" callbackURLParamName = "callbackURL"
@ -97,6 +97,8 @@ func login(c *cli.Context) error {
callbackStoreURL, callbackStoreURL,
false, false,
false, false,
c.Bool(cfdflags.AutoCloseInterstitial),
isFEDRamp,
log, log,
) )
if err != nil { if err != nil {

View File

@ -155,10 +155,12 @@ func (sc *subcommandContext) create(name string, credentialsFilePath string, sec
if err != nil { if err != nil {
return nil, err return nil, err
} }
tunnelCredentials := connection.Credentials{ tunnelCredentials := connection.Credentials{
AccountTag: credential.AccountID(), AccountTag: credential.AccountID(),
TunnelSecret: tunnelSecret, TunnelSecret: tunnelSecret,
TunnelID: tunnel.ID, TunnelID: tunnel.ID,
Endpoint: credential.Endpoint(),
} }
usedCertPath := false usedCertPath := false
if credentialsFilePath == "" { if credentialsFilePath == "" {

View File

@ -1,7 +1,7 @@
package config package config
import ( import (
"crypto/md5" "crypto/sha256"
"fmt" "fmt"
"io" "io"
"strings" "strings"
@ -16,6 +16,7 @@ type Forwarder struct {
TokenClientID string `json:"service_token_id" yaml:"serviceTokenID"` TokenClientID string `json:"service_token_id" yaml:"serviceTokenID"`
TokenSecret string `json:"secret_token_id" yaml:"serviceTokenSecret"` TokenSecret string `json:"secret_token_id" yaml:"serviceTokenSecret"`
Destination string `json:"destination"` Destination string `json:"destination"`
IsFedramp bool `json:"is_fedramp" yaml:"isFedramp"`
} }
// Tunnel represents a tunnel that should be started // Tunnel represents a tunnel that should be started
@ -46,24 +47,24 @@ type Root struct {
// Hash returns the computed values to see if the forwarder values change // Hash returns the computed values to see if the forwarder values change
func (f *Forwarder) Hash() string { func (f *Forwarder) Hash() string {
h := md5.New() h := sha256.New()
io.WriteString(h, f.URL) _, _ = io.WriteString(h, f.URL)
io.WriteString(h, f.Listener) _, _ = io.WriteString(h, f.Listener)
io.WriteString(h, f.TokenClientID) _, _ = io.WriteString(h, f.TokenClientID)
io.WriteString(h, f.TokenSecret) _, _ = io.WriteString(h, f.TokenSecret)
io.WriteString(h, f.Destination) _, _ = io.WriteString(h, f.Destination)
return fmt.Sprintf("%x", h.Sum(nil)) return fmt.Sprintf("%x", h.Sum(nil))
} }
// Hash returns the computed values to see if the forwarder values change // Hash returns the computed values to see if the forwarder values change
func (r *DNSResolver) Hash() string { func (r *DNSResolver) Hash() string {
h := md5.New() h := sha256.New()
io.WriteString(h, r.Address) _, _ = io.WriteString(h, r.Address)
io.WriteString(h, strings.Join(r.Bootstraps, ",")) _, _ = io.WriteString(h, strings.Join(r.Bootstraps, ","))
io.WriteString(h, strings.Join(r.Upstreams, ",")) _, _ = io.WriteString(h, strings.Join(r.Upstreams, ","))
io.WriteString(h, fmt.Sprintf("%d", r.Port)) _, _ = io.WriteString(h, fmt.Sprintf("%d", r.Port))
io.WriteString(h, fmt.Sprintf("%d", r.MaxUpstreamConnections)) _, _ = io.WriteString(h, fmt.Sprintf("%d", r.MaxUpstreamConnections))
io.WriteString(h, fmt.Sprintf("%v", r.Enabled)) _, _ = io.WriteString(h, fmt.Sprintf("%v", r.Enabled))
return fmt.Sprintf("%x", h.Sum(nil)) return fmt.Sprintf("%x", h.Sum(nil))
} }

View File

@ -82,7 +82,7 @@ func (c *controlStream) ServeControlStream(
tunnelConfigGetter TunnelConfigJSONGetter, tunnelConfigGetter TunnelConfigJSONGetter,
) error { ) error {
registrationClient := c.registerClientFunc(ctx, rw, c.registerTimeout) registrationClient := c.registerClientFunc(ctx, rw, c.registerTimeout)
c.observer.logConnecting(c.connIndex, c.edgeAddress, c.protocol)
registrationDetails, err := registrationClient.RegisterConnection( registrationDetails, err := registrationClient.RegisterConnection(
ctx, ctx,
c.tunnelProperties.Credentials.Auth(), c.tunnelProperties.Credentials.Auth(),

View File

@ -1,7 +1,6 @@
package connection package connection
import ( import (
"github.com/cloudflare/cloudflared/edgediscovery"
tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
) )
@ -53,26 +52,26 @@ func serverRegistrationErrorFromRPC(err error) ServerRegisterTunnelError {
} }
} }
type muxerShutdownError struct{} type ControlStreamError struct{}
func (e muxerShutdownError) Error() string { var _ error = &ControlStreamError{}
return "muxer shutdown"
func (e *ControlStreamError) Error() string {
return "control stream encountered a failure while serving"
} }
var errMuxerStopped = muxerShutdownError{} type StreamListenerError struct{}
func isHandshakeErrRecoverable(err error, connIndex uint8, observer *Observer) bool { var _ error = &StreamListenerError{}
log := observer.log.With().
Uint8(LogFieldConnIndex, connIndex).
Err(err).
Logger()
switch err.(type) { func (e *StreamListenerError) Error() string {
case edgediscovery.DialError: return "accept stream listener encountered a failure while serving"
log.Error().Msg("Connection unable to dial edge") }
default:
log.Error().Msg("Connection failed") type DatagramManagerError struct{}
return false
} var _ error = &DatagramManagerError{}
return true
func (e *DatagramManagerError) Error() string {
return "datagram manager encountered a failure while serving"
} }

View File

@ -46,6 +46,15 @@ func (o *Observer) RegisterSink(sink EventSink) {
o.addSinkChan <- sink o.addSinkChan <- sink
} }
func (o *Observer) logConnecting(connIndex uint8, address net.IP, protocol Protocol) {
o.log.Debug().
Int(management.EventTypeKey, int(management.Cloudflared)).
Uint8(LogFieldConnIndex, connIndex).
IPAddr(LogFieldIPAddress, address).
Str(LogFieldProtocol, protocol.String()).
Msg("Registering tunnel connection")
}
func (o *Observer) logConnected(connectionID uuid.UUID, connIndex uint8, location string, address net.IP, protocol Protocol) { func (o *Observer) logConnected(connectionID uuid.UUID, connIndex uint8, location string, address net.IP, protocol Protocol) {
o.log.Info(). o.log.Info().
Int(management.EventTypeKey, int(management.Cloudflared)). Int(management.EventTypeKey, int(management.Cloudflared)).

View File

@ -3,6 +3,7 @@ package connection
import ( import (
"bufio" "bufio"
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
"net" "net"
@ -12,7 +13,6 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/pkg/errors"
"github.com/quic-go/quic-go" "github.com/quic-go/quic-go"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
@ -65,7 +65,7 @@ func NewTunnelConnection(
streamWriteTimeout time.Duration, streamWriteTimeout time.Duration,
gracePeriod time.Duration, gracePeriod time.Duration,
logger *zerolog.Logger, logger *zerolog.Logger,
) (TunnelConnection, error) { ) TunnelConnection {
return &quicConnection{ return &quicConnection{
conn: conn, conn: conn,
logger: logger, logger: logger,
@ -77,10 +77,11 @@ func NewTunnelConnection(
rpcTimeout: rpcTimeout, rpcTimeout: rpcTimeout,
streamWriteTimeout: streamWriteTimeout, streamWriteTimeout: streamWriteTimeout,
gracePeriod: gracePeriod, gracePeriod: gracePeriod,
}, nil }
} }
// Serve starts a QUIC connection that begins accepting streams. // Serve starts a QUIC connection that begins accepting streams.
// Returning a nil error means cloudflared will exit for good and will not attempt to reconnect.
func (q *quicConnection) Serve(ctx context.Context) error { func (q *quicConnection) Serve(ctx context.Context) error {
// The edge assumes the first stream is used for the control plane // The edge assumes the first stream is used for the control plane
controlStream, err := q.conn.OpenStream() controlStream, err := q.conn.OpenStream()
@ -88,16 +89,16 @@ func (q *quicConnection) Serve(ctx context.Context) error {
return fmt.Errorf("failed to open a registration control stream: %w", err) return fmt.Errorf("failed to open a registration control stream: %w", err)
} }
// If either goroutine returns nil error, we rely on this cancellation to make sure the other goroutine exits
// as fast as possible as well. Nil error means we want to exit for good (caller code won't retry serving this
// connection).
// If either goroutine returns a non nil error, then the error group cancels the context, thus also canceling the // If either goroutine returns a non nil error, then the error group cancels the context, thus also canceling the
// other goroutine as fast as possible. // other goroutines. We enforce returning a not-nil error for each function started in the errgroup by logging
ctx, cancel := context.WithCancel(ctx) // the error returned and returning a custom error type instead.
errGroup, ctx := errgroup.WithContext(ctx) errGroup, ctx := errgroup.WithContext(ctx)
// In the future, if cloudflared can autonomously push traffic to the edge, we have to make sure the control // Close the quic connection if any of the following routines return from the errgroup (regardless of their error)
// stream is already fully registered before the other goroutines can proceed. // because they are no longer processing requests for the connection.
defer q.Close()
// Start the control stream routine
errGroup.Go(func() error { errGroup.Go(func() error {
// err is equal to nil if we exit due to unregistration. If that happens we want to wait the full // err is equal to nil if we exit due to unregistration. If that happens we want to wait the full
// amount of the grace period, allowing requests to finish before we cancel the context, which will // amount of the grace period, allowing requests to finish before we cancel the context, which will
@ -114,16 +115,26 @@ func (q *quicConnection) Serve(ctx context.Context) error {
} }
} }
} }
cancel() if err != nil {
return err q.logger.Error().Err(err).Msg("failed to serve the control stream")
}
return &ControlStreamError{}
}) })
// Start the accept stream loop routine
errGroup.Go(func() error { errGroup.Go(func() error {
defer cancel() err := q.acceptStream(ctx)
return q.acceptStream(ctx) if err != nil {
q.logger.Error().Err(err).Msg("failed to accept incoming stream requests")
}
return &StreamListenerError{}
}) })
// Start the datagram handler routine
errGroup.Go(func() error { errGroup.Go(func() error {
defer cancel() err := q.datagramHandler.Serve(ctx)
return q.datagramHandler.Serve(ctx) if err != nil {
q.logger.Error().Err(err).Msg("failed to run the datagram handler")
}
return &DatagramManagerError{}
}) })
return errGroup.Wait() return errGroup.Wait()
@ -140,7 +151,6 @@ func (q *quicConnection) Close() {
} }
func (q *quicConnection) acceptStream(ctx context.Context) error { func (q *quicConnection) acceptStream(ctx context.Context) error {
defer q.Close()
for { for {
quicStream, err := q.conn.AcceptStream(ctx) quicStream, err := q.conn.AcceptStream(ctx)
if err != nil { if err != nil {
@ -230,7 +240,7 @@ func (q *quicConnection) dispatchRequest(ctx context.Context, stream *rpcquic.Re
ConnIndex: q.connIndex, ConnIndex: q.connIndex,
}), rwa.connectResponseSent }), rwa.connectResponseSent
default: default:
return errors.Errorf("unsupported error type: %s", request.Type), false return fmt.Errorf("unsupported error type: %s", request.Type), false
} }
} }

View File

@ -847,7 +847,7 @@ func testTunnelConnection(t *testing.T, serverAddr netip.AddrPort, index uint8)
&log, &log,
} }
tunnelConn, err := NewTunnelConnection( tunnelConn := NewTunnelConnection(
ctx, ctx,
conn, conn,
index, index,
@ -860,7 +860,6 @@ func testTunnelConnection(t *testing.T, serverAddr netip.AddrPort, index uint8)
0*time.Second, 0*time.Second,
&log, &log,
) )
require.NoError(t, err)
return tunnelConn, datagramConn return tunnelConn, datagramConn
} }

View File

@ -98,24 +98,17 @@ func NewDatagramV2Connection(ctx context.Context,
} }
func (d *datagramV2Connection) Serve(ctx context.Context) error { func (d *datagramV2Connection) Serve(ctx context.Context) error {
// If either goroutine returns nil error, we rely on this cancellation to make sure the other goroutine exits // If either goroutine from the errgroup returns at all (error or nil), we rely on its cancellation to make sure
// as fast as possible as well. Nil error means we want to exit for good (caller code won't retry serving this // the other goroutines as well.
// connection).
// If either goroutine returns a non nil error, then the error group cancels the context, thus also canceling the
// other goroutine as fast as possible.
ctx, cancel := context.WithCancel(ctx)
errGroup, ctx := errgroup.WithContext(ctx) errGroup, ctx := errgroup.WithContext(ctx)
errGroup.Go(func() error { errGroup.Go(func() error {
defer cancel()
return d.sessionManager.Serve(ctx) return d.sessionManager.Serve(ctx)
}) })
errGroup.Go(func() error { errGroup.Go(func() error {
defer cancel()
return d.datagramMuxer.ServeReceive(ctx) return d.datagramMuxer.ServeReceive(ctx)
}) })
errGroup.Go(func() error { errGroup.Go(func() error {
defer cancel()
return d.packetRouter.Serve(ctx) return d.packetRouter.Serve(ctx)
}) })

View File

@ -23,6 +23,10 @@ func (c User) AccountID() string {
return c.cert.AccountID return c.cert.AccountID
} }
func (c User) Endpoint() string {
return c.cert.Endpoint
}
func (c User) ZoneID() string { func (c User) ZoneID() string {
return c.cert.ZoneID return c.cert.ZoneID
} }

2
go.mod
View File

@ -10,7 +10,7 @@ require (
github.com/fortytw2/leaktest v1.3.0 github.com/fortytw2/leaktest v1.3.0
github.com/fsnotify/fsnotify v1.4.9 github.com/fsnotify/fsnotify v1.4.9
github.com/getsentry/sentry-go v0.16.0 github.com/getsentry/sentry-go v0.16.0
github.com/go-chi/chi/v5 v5.0.10 github.com/go-chi/chi/v5 v5.2.2
github.com/go-chi/cors v1.2.1 github.com/go-chi/cors v1.2.1
github.com/go-jose/go-jose/v4 v4.1.0 github.com/go-jose/go-jose/v4 v4.1.0
github.com/gobwas/ws v1.2.1 github.com/gobwas/ws v1.2.1

4
go.sum
View File

@ -58,8 +58,8 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=

View File

@ -175,7 +175,7 @@ func (c *datagramConn) Serve(ctx context.Context) error {
// Monitor the context of cloudflared // Monitor the context of cloudflared
case <-ctx.Done(): case <-ctx.Done():
return ctx.Err() return ctx.Err()
// Monitor the context of the underlying connection // Monitor the context of the underlying quic connection
case <-connCtx.Done(): case <-connCtx.Done():
return connCtx.Err() return connCtx.Err()
// Monitor for any hard errors from reading the connection // Monitor for any hard errors from reading the connection

View File

@ -132,6 +132,7 @@ func (s *Supervisor) Run(
if err == errEarlyShutdown { if err == errEarlyShutdown {
return nil return nil
} }
s.log.Logger().Error().Err(err).Msg("initial tunnel connection failed")
return err return err
} }
var tunnelsWaiting []int var tunnelsWaiting []int
@ -154,6 +155,7 @@ func (s *Supervisor) Run(
// (note that this may also be caused by context cancellation) // (note that this may also be caused by context cancellation)
case tunnelError := <-s.tunnelErrors: case tunnelError := <-s.tunnelErrors:
tunnelsActive-- tunnelsActive--
s.log.ConnAwareLogger().Err(tunnelError.err).Int(connection.LogFieldConnIndex, tunnelError.index).Msg("Connection terminated")
if tunnelError.err != nil && !shuttingDown { if tunnelError.err != nil && !shuttingDown {
switch tunnelError.err.(type) { switch tunnelError.err.(type) {
case ReconnectSignal: case ReconnectSignal:
@ -166,7 +168,6 @@ func (s *Supervisor) Run(
if _, retry := s.tunnelsProtocolFallback[tunnelError.index].GetMaxBackoffDuration(ctx); !retry { if _, retry := s.tunnelsProtocolFallback[tunnelError.index].GetMaxBackoffDuration(ctx); !retry {
continue continue
} }
s.log.ConnAwareLogger().Err(tunnelError.err).Int(connection.LogFieldConnIndex, tunnelError.index).Msg("Connection terminated")
tunnelsWaiting = append(tunnelsWaiting, tunnelError.index) tunnelsWaiting = append(tunnelsWaiting, tunnelError.index)
s.waitForNextTunnel(tunnelError.index) s.waitForNextTunnel(tunnelError.index)
@ -285,7 +286,10 @@ func (s *Supervisor) startFirstTunnel(
*quic.IdleTimeoutError, *quic.IdleTimeoutError,
*quic.ApplicationError, *quic.ApplicationError,
edgediscovery.DialError, edgediscovery.DialError,
*connection.EdgeQuicDialError: *connection.EdgeQuicDialError,
*connection.ControlStreamError,
*connection.StreamListenerError,
*connection.DatagramManagerError:
// Try again for these types of errors // Try again for these types of errors
default: default:
// Uncaught errors should bail startup // Uncaught errors should bail startup
@ -301,13 +305,9 @@ func (s *Supervisor) startTunnel(
index int, index int,
connectedSignal *signal.Signal, connectedSignal *signal.Signal,
) { ) {
var err error
defer func() {
s.tunnelErrors <- tunnelError{index: index, err: err}
}()
// nolint: gosec // nolint: gosec
err = s.edgeTunnelServer.Serve(ctx, uint8(index), s.tunnelsProtocolFallback[index], connectedSignal) err := s.edgeTunnelServer.Serve(ctx, uint8(index), s.tunnelsProtocolFallback[index], connectedSignal)
s.tunnelErrors <- tunnelError{index: index, err: err}
} }
func (s *Supervisor) newConnectedTunnelSignal(index int) *signal.Signal { func (s *Supervisor) newConnectedTunnelSignal(index int) *signal.Signal {

View File

@ -556,6 +556,7 @@ func (e *EdgeTunnelServer) serveQUIC(
pqMode := connOptions.FeatureSnapshot.PostQuantum pqMode := connOptions.FeatureSnapshot.PostQuantum
curvePref, err := curvePreference(pqMode, fips.IsFipsEnabled(), tlsConfig.CurvePreferences) curvePref, err := curvePreference(pqMode, fips.IsFipsEnabled(), tlsConfig.CurvePreferences)
if err != nil { if err != nil {
connLogger.ConnAwareLogger().Err(err).Msgf("failed to get curve preferences")
return err, true return err, true
} }
@ -627,7 +628,7 @@ func (e *EdgeTunnelServer) serveQUIC(
} }
// Wrap the [quic.Connection] as a TunnelConnection // Wrap the [quic.Connection] as a TunnelConnection
tunnelConn, err := connection.NewTunnelConnection( tunnelConn := connection.NewTunnelConnection(
ctx, ctx,
conn, conn,
connIndex, connIndex,
@ -640,17 +641,13 @@ func (e *EdgeTunnelServer) serveQUIC(
e.config.GracePeriod, e.config.GracePeriod,
connLogger.Logger(), connLogger.Logger(),
) )
if err != nil {
connLogger.ConnAwareLogger().Err(err).Msgf("Failed to create new tunnel connection")
return err, true
}
// Serve the TunnelConnection // Serve the TunnelConnection
errGroup, serveCtx := errgroup.WithContext(ctx) errGroup, serveCtx := errgroup.WithContext(ctx)
errGroup.Go(func() error { errGroup.Go(func() error {
err := tunnelConn.Serve(serveCtx) err := tunnelConn.Serve(serveCtx)
if err != nil { if err != nil {
connLogger.ConnAwareLogger().Err(err).Msg("Failed to serve tunnel connection") connLogger.ConnAwareLogger().Err(err).Msg("failed to serve tunnel connection")
} }
return err return err
}) })

View File

@ -185,18 +185,18 @@ func Init(version string) {
// FetchTokenWithRedirect will either load a stored token or generate a new one // FetchTokenWithRedirect will either load a stored token or generate a new one
// it appends the full url as the redirect URL to the access cli request if opening the browser // it appends the full url as the redirect URL to the access cli request if opening the browser
func FetchTokenWithRedirect(appURL *url.URL, appInfo *AppInfo, log *zerolog.Logger) (string, error) { func FetchTokenWithRedirect(appURL *url.URL, appInfo *AppInfo, autoClose bool, isFedramp bool, log *zerolog.Logger) (string, error) {
return getToken(appURL, appInfo, false, log) return getToken(appURL, appInfo, false, autoClose, isFedramp, log)
} }
// FetchToken will either load a stored token or generate a new one // FetchToken will either load a stored token or generate a new one
// it appends the host of the appURL as the redirect URL to the access cli request if opening the browser // it appends the host of the appURL as the redirect URL to the access cli request if opening the browser
func FetchToken(appURL *url.URL, appInfo *AppInfo, log *zerolog.Logger) (string, error) { func FetchToken(appURL *url.URL, appInfo *AppInfo, autoClose bool, isFedramp bool, log *zerolog.Logger) (string, error) {
return getToken(appURL, appInfo, true, log) return getToken(appURL, appInfo, true, autoClose, isFedramp, log)
} }
// getToken will either load a stored token or generate a new one // getToken will either load a stored token or generate a new one
func getToken(appURL *url.URL, appInfo *AppInfo, useHostOnly bool, log *zerolog.Logger) (string, error) { func getToken(appURL *url.URL, appInfo *AppInfo, useHostOnly bool, autoClose bool, isFedramp bool, log *zerolog.Logger) (string, error) {
if token, err := GetAppTokenIfExists(appInfo); token != "" && err == nil { if token, err := GetAppTokenIfExists(appInfo); token != "" && err == nil {
return token, nil return token, nil
} }
@ -249,18 +249,19 @@ func getToken(appURL *url.URL, appInfo *AppInfo, useHostOnly bool, log *zerolog.
return appToken, nil return appToken, nil
} }
} }
return getTokensFromEdge(appURL, appInfo.AppAUD, appTokenPath, orgTokenPath, useHostOnly, log) return getTokensFromEdge(appURL, appInfo.AppAUD, appTokenPath, orgTokenPath, useHostOnly, autoClose, isFedramp, log)
} }
// getTokensFromEdge will attempt to use the transfer service to retrieve an app and org token, save them to disk, // getTokensFromEdge will attempt to use the transfer service to retrieve an app and org token, save them to disk,
// and return the app token. // and return the app token.
func getTokensFromEdge(appURL *url.URL, appAUD, appTokenPath, orgTokenPath string, useHostOnly bool, log *zerolog.Logger) (string, error) { func getTokensFromEdge(appURL *url.URL, appAUD, appTokenPath, orgTokenPath string, useHostOnly bool, autoClose bool, isFedramp bool, log *zerolog.Logger) (string, error) {
fmt.Println("Get tokens from edge ", autoClose)
// If no org token exists or if it couldn't be exchanged for an app token, then run the transfer service flow. // If no org token exists or if it couldn't be exchanged for an app token, then run the transfer service flow.
// this weird parameter is the resource name (token) and the key/value // this weird parameter is the resource name (token) and the key/value
// we want to send to the transfer service. the key is token and the value // we want to send to the transfer service. the key is token and the value
// is blank (basically just the id generated in the transfer service) // is blank (basically just the id generated in the transfer service)
resourceData, err := RunTransfer(appURL, appAUD, keyName, keyName, "", true, useHostOnly, log) resourceData, err := RunTransfer(appURL, appAUD, keyName, keyName, "", true, useHostOnly, autoClose, isFedramp, log)
if err != nil { if err != nil {
return "", errors.Wrap(err, "failed to run transfer service") return "", errors.Wrap(err, "failed to run transfer service")
} }

View File

@ -16,6 +16,7 @@ import (
const ( const (
baseStoreURL = "https://login.cloudflareaccess.org/" baseStoreURL = "https://login.cloudflareaccess.org/"
fedStoreURL = "https://login.fed.cloudflareaccess.org/"
clientTimeout = time.Second * 60 clientTimeout = time.Second * 60
) )
@ -25,12 +26,12 @@ const (
// The "dance" we refer to is building a HTTP request, opening that in a browser waiting for // The "dance" we refer to is building a HTTP request, opening that in a browser waiting for
// the user to complete an action, while it long polls in the background waiting for an // the user to complete an action, while it long polls in the background waiting for an
// action to be completed to download the resource. // action to be completed to download the resource.
func RunTransfer(transferURL *url.URL, appAUD, resourceName, key, value string, shouldEncrypt bool, useHostOnly bool, log *zerolog.Logger) ([]byte, error) { func RunTransfer(transferURL *url.URL, appAUD, resourceName, key, value string, shouldEncrypt bool, useHostOnly bool, autoClose bool, fedramp bool, log *zerolog.Logger) ([]byte, error) {
encrypterClient, err := NewEncrypter("cloudflared_priv.pem", "cloudflared_pub.pem") encrypterClient, err := NewEncrypter("cloudflared_priv.pem", "cloudflared_pub.pem")
if err != nil { if err != nil {
return nil, err return nil, err
} }
requestURL, err := buildRequestURL(transferURL, appAUD, key, value+encrypterClient.PublicKey(), shouldEncrypt, useHostOnly) requestURL, err := buildRequestURL(transferURL, appAUD, key, value+encrypterClient.PublicKey(), shouldEncrypt, useHostOnly, autoClose)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -45,8 +46,14 @@ func RunTransfer(transferURL *url.URL, appAUD, resourceName, key, value string,
var resourceData []byte var resourceData []byte
storeURL := baseStoreURL
if fedramp {
storeURL = fedStoreURL
}
if shouldEncrypt { if shouldEncrypt {
buf, key, err := transferRequest(baseStoreURL+"transfer/"+encrypterClient.PublicKey(), log) buf, key, err := transferRequest(storeURL+"transfer/"+encrypterClient.PublicKey(), log)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -62,7 +69,7 @@ func RunTransfer(transferURL *url.URL, appAUD, resourceName, key, value string,
resourceData = decrypted resourceData = decrypted
} else { } else {
buf, _, err := transferRequest(baseStoreURL+encrypterClient.PublicKey(), log) buf, _, err := transferRequest(storeURL+encrypterClient.PublicKey(), log)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -75,7 +82,7 @@ func RunTransfer(transferURL *url.URL, appAUD, resourceName, key, value string,
// BuildRequestURL creates a request suitable for a resource transfer. // BuildRequestURL creates a request suitable for a resource transfer.
// it will return a constructed url based off the base url and query key/value provided. // it will return a constructed url based off the base url and query key/value provided.
// cli will build a url for cli transfer request. // cli will build a url for cli transfer request.
func buildRequestURL(baseURL *url.URL, appAUD string, key, value string, cli, useHostOnly bool) (string, error) { func buildRequestURL(baseURL *url.URL, appAUD string, key, value string, cli, useHostOnly bool, autoClose bool) (string, error) {
q := baseURL.Query() q := baseURL.Query()
q.Set(key, value) q.Set(key, value)
q.Set("aud", appAUD) q.Set("aud", appAUD)
@ -90,6 +97,10 @@ func buildRequestURL(baseURL *url.URL, appAUD string, key, value string, cli, us
q.Set("redirect_url", baseURL.String()) // we add the token as a query param on both the redirect_url and the main url q.Set("redirect_url", baseURL.String()) // we add the token as a query param on both the redirect_url and the main url
q.Set("send_org_token", "true") // indicates that the cli endpoint should return both the org and app token q.Set("send_org_token", "true") // indicates that the cli endpoint should return both the org and app token
q.Set("edge_token_transfer", "true") // use new LoginHelper service built on workers q.Set("edge_token_transfer", "true") // use new LoginHelper service built on workers
if autoClose {
q.Set("close_interstitial", "true") // Automatically close the success window.
}
baseURL.RawQuery = q.Encode() // and this actual baseURL. baseURL.RawQuery = q.Encode() // and this actual baseURL.
baseURL.Path = "cdn-cgi/access/cli" baseURL.Path = "cdn-cgi/access/cli"
return baseURL.String(), nil return baseURL.String(), nil
@ -127,7 +138,12 @@ func poll(client *http.Client, requestURL string, log *zerolog.Logger) ([]byte,
// ignore everything other than server errors as the resource // ignore everything other than server errors as the resource
// may not exist until the user does the interaction // may not exist until the user does the interaction
if resp.StatusCode >= 500 { if resp.StatusCode >= 500 {
return nil, "", fmt.Errorf("error on request %d", resp.StatusCode) buf := new(bytes.Buffer)
if _, err := io.Copy(buf, resp.Body); err != nil {
return nil, "", err
}
return nil, "", fmt.Errorf("error on request %d: %s", resp.StatusCode, buf.String())
} }
if resp.StatusCode != 200 { if resp.StatusCode != 200 {
log.Info().Msg("Waiting for login...") log.Info().Msg("Waiting for login...")

View File

@ -1,9 +1,19 @@
# Changelog # Changelog
## v5.0.12 (2024-02-16)
- History of changes: see https://github.com/go-chi/chi/compare/v5.0.11...v5.0.12
## v5.0.11 (2023-12-19)
- History of changes: see https://github.com/go-chi/chi/compare/v5.0.10...v5.0.11
## v5.0.10 (2023-07-13) ## v5.0.10 (2023-07-13)
- Fixed small edge case in tests of v5.0.9 for older Go versions - Fixed small edge case in tests of v5.0.9 for older Go versions
- History of changes: see https://github.com/go-chi/chi/compare/v5.0.8...v5.0.10 - History of changes: see https://github.com/go-chi/chi/compare/v5.0.9...v5.0.10
## v5.0.9 (2023-07-13) ## v5.0.9 (2023-07-13)
@ -306,7 +316,7 @@ Cheers all, happy coding!
request-scoped values. We're very excited about the new context addition and are proud to request-scoped values. We're very excited about the new context addition and are proud to
introduce chi v2, a minimal and powerful routing package for building large HTTP services, introduce chi v2, a minimal and powerful routing package for building large HTTP services,
with zero external dependencies. Chi focuses on idiomatic design and encourages the use of with zero external dependencies. Chi focuses on idiomatic design and encourages the use of
stdlib HTTP handlers and middlwares. stdlib HTTP handlers and middlewares.
- chi v2 deprecates its `chi.Handler` interface and requires `http.Handler` or `http.HandlerFunc` - chi v2 deprecates its `chi.Handler` interface and requires `http.Handler` or `http.HandlerFunc`
- chi v2 stores URL routing parameters and patterns in the standard request context: `r.Context()` - chi v2 stores URL routing parameters and patterns in the standard request context: `r.Context()`
- chi v2 lower-level routing context is accessible by `chi.RouteContext(r.Context()) *chi.Context`, - chi v2 lower-level routing context is accessible by `chi.RouteContext(r.Context()) *chi.Context`,

View File

@ -14,7 +14,7 @@
A typical workflow is: A typical workflow is:
1. [Fork the repository.][fork] [This tip maybe also helpful.][go-fork-tip] 1. [Fork the repository.][fork]
2. [Create a topic branch.][branch] 2. [Create a topic branch.][branch]
3. Add tests for your change. 3. Add tests for your change.
4. Run `go test`. If your tests pass, return to the step 3. 4. Run `go test`. If your tests pass, return to the step 3.
@ -24,8 +24,8 @@ A typical workflow is:
8. [Submit a pull request.][pull-req] 8. [Submit a pull request.][pull-req]
[go-install]: https://golang.org/doc/install [go-install]: https://golang.org/doc/install
[go-fork-tip]: http://blog.campoy.cat/2014/03/github-and-go-forking-pull-requests-and.html [fork]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo
[fork]: https://help.github.com/articles/fork-a-repo [branch]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches
[branch]: http://learn.github.com/p/branching.html [git-help]: https://docs.github.com/en
[git-help]: https://guides.github.com [pull-req]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests
[pull-req]: https://help.github.com/articles/using-pull-requests

View File

@ -20,7 +20,9 @@ and [docgen](https://github.com/go-chi/docgen). We hope you enjoy it too!
## Install ## Install
`go get -u github.com/go-chi/chi/v5` ```sh
go get -u github.com/go-chi/chi/v5
```
## Features ## Features
@ -65,7 +67,7 @@ func main() {
**REST Preview:** **REST Preview:**
Here is a little preview of how routing looks like with chi. Also take a look at the generated routing docs Here is a little preview of what routing looks like with chi. Also take a look at the generated routing docs
in JSON ([routes.json](https://github.com/go-chi/chi/blob/master/_examples/rest/routes.json)) and in in JSON ([routes.json](https://github.com/go-chi/chi/blob/master/_examples/rest/routes.json)) and in
Markdown ([routes.md](https://github.com/go-chi/chi/blob/master/_examples/rest/routes.md)). Markdown ([routes.md](https://github.com/go-chi/chi/blob/master/_examples/rest/routes.md)).
@ -194,7 +196,7 @@ type Router interface {
// path, with a fresh middleware stack for the inline-Router. // path, with a fresh middleware stack for the inline-Router.
Group(fn func(r Router)) Router Group(fn func(r Router)) Router
// Route mounts a sub-Router along a `pattern`` string. // Route mounts a sub-Router along a `pattern` string.
Route(pattern string, fn func(r Router)) Router Route(pattern string, fn func(r Router)) Router
// Mount attaches another http.Handler along ./pattern/* // Mount attaches another http.Handler along ./pattern/*
@ -354,6 +356,7 @@ with `net/http` can be used with chi's mux.
| [RouteHeaders] | Route handling for request headers | | [RouteHeaders] | Route handling for request headers |
| [SetHeader] | Short-hand middleware to set a response header key/value | | [SetHeader] | Short-hand middleware to set a response header key/value |
| [StripSlashes] | Strip slashes on routing paths | | [StripSlashes] | Strip slashes on routing paths |
| [Sunset] | Sunset set Deprecation/Sunset header to response |
| [Throttle] | Puts a ceiling on the number of concurrent requests | | [Throttle] | Puts a ceiling on the number of concurrent requests |
| [Timeout] | Signals to the request context when the timeout deadline is reached | | [Timeout] | Signals to the request context when the timeout deadline is reached |
| [URLFormat] | Parse extension from url and put it on request context | | [URLFormat] | Parse extension from url and put it on request context |
@ -380,6 +383,7 @@ with `net/http` can be used with chi's mux.
[RouteHeaders]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RouteHeaders [RouteHeaders]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RouteHeaders
[SetHeader]: https://pkg.go.dev/github.com/go-chi/chi/middleware#SetHeader [SetHeader]: https://pkg.go.dev/github.com/go-chi/chi/middleware#SetHeader
[StripSlashes]: https://pkg.go.dev/github.com/go-chi/chi/middleware#StripSlashes [StripSlashes]: https://pkg.go.dev/github.com/go-chi/chi/middleware#StripSlashes
[Sunset]: https://pkg.go.dev/github.com/go-chi/chi/v5/middleware#Sunset
[Throttle]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Throttle [Throttle]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Throttle
[ThrottleBacklog]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ThrottleBacklog [ThrottleBacklog]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ThrottleBacklog
[ThrottleWithOpts]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ThrottleWithOpts [ThrottleWithOpts]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ThrottleWithOpts
@ -467,7 +471,8 @@ how setting context on a request in Go works.
* Carl Jackson for https://github.com/zenazn/goji * Carl Jackson for https://github.com/zenazn/goji
* Parts of chi's thinking comes from goji, and chi's middleware package * Parts of chi's thinking comes from goji, and chi's middleware package
sources from goji. sources from [goji](https://github.com/zenazn/goji/tree/master/web/middleware).
* Please see goji's [LICENSE](https://github.com/zenazn/goji/blob/master/LICENSE) (MIT)
* Armon Dadgar for https://github.com/armon/go-radix * Armon Dadgar for https://github.com/armon/go-radix
* Contributions: [@VojtechVitek](https://github.com/VojtechVitek) * Contributions: [@VojtechVitek](https://github.com/VojtechVitek)
@ -494,7 +499,7 @@ Copyright (c) 2015-present [Peter Kieltyka](https://github.com/pkieltyka)
Licensed under [MIT License](./LICENSE) Licensed under [MIT License](./LICENSE)
[GoDoc]: https://pkg.go.dev/github.com/go-chi/chi?tab=versions [GoDoc]: https://pkg.go.dev/github.com/go-chi/chi/v5
[GoDoc Widget]: https://godoc.org/github.com/go-chi/chi?status.svg [GoDoc Widget]: https://godoc.org/github.com/go-chi/chi?status.svg
[Travis]: https://travis-ci.org/go-chi/chi [Travis]: https://travis-ci.org/go-chi/chi
[Travis Widget]: https://travis-ci.org/go-chi/chi.svg?branch=master [Travis Widget]: https://travis-ci.org/go-chi/chi.svg?branch=master

5
vendor/github.com/go-chi/chi/v5/SECURITY.md generated vendored Normal file
View File

@ -0,0 +1,5 @@
# Reporting Security Issues
We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/go-chi/chi/security/advisories/new) tab.

View File

@ -37,8 +37,7 @@
// //
// A placeholder with a name followed by a colon allows a regular // A placeholder with a name followed by a colon allows a regular
// expression match, for example {number:\\d+}. The regular expression // expression match, for example {number:\\d+}. The regular expression
// syntax is Go's normal regexp RE2 syntax, except that regular expressions // syntax is Go's normal regexp RE2 syntax, except that / will never be
// including { or } are not supported, and / will never be
// matched. An anonymous regexp pattern is allowed, using an empty string // matched. An anonymous regexp pattern is allowed, using an empty string
// before the colon in the placeholder, such as {:\\d+} // before the colon in the placeholder, such as {:\\d+}
// //
@ -51,7 +50,7 @@
// "/user/{name}" matches "/user/jsmith" but not "/user/jsmith/info" or "/user/jsmith/" // "/user/{name}" matches "/user/jsmith" but not "/user/jsmith/info" or "/user/jsmith/"
// "/user/{name}/info" matches "/user/jsmith/info" // "/user/{name}/info" matches "/user/jsmith/info"
// "/page/*" matches "/page/intro/latest" // "/page/*" matches "/page/intro/latest"
// "/page/{other}/index" also matches "/page/intro/latest" // "/page/{other}/latest" also matches "/page/intro/latest"
// "/date/{yyyy:\\d\\d\\d\\d}/{mm:\\d\\d}/{dd:\\d\\d}" matches "/date/2017/04/01" // "/date/{yyyy:\\d\\d\\d\\d}/{mm:\\d\\d}/{dd:\\d\\d}" matches "/date/2017/04/01"
package chi package chi
@ -127,6 +126,10 @@ type Routes interface {
// the method/path - similar to routing a http request, but without // the method/path - similar to routing a http request, but without
// executing the handler thereafter. // executing the handler thereafter.
Match(rctx *Context, method, path string) bool Match(rctx *Context, method, path string) bool
// Find searches the routing tree for the pattern that matches
// the method/path.
Find(rctx *Context, method, path string) string
} }
// Middlewares type is a slice of standard middleware handlers with methods // Middlewares type is a slice of standard middleware handlers with methods

View File

@ -60,7 +60,7 @@ type Context struct {
URLParams RouteParams URLParams RouteParams
// Route parameters matched for the current sub-router. It is // Route parameters matched for the current sub-router. It is
// intentionally unexported so it cant be tampered. // intentionally unexported so it can't be tampered.
routeParams RouteParams routeParams RouteParams
// The endpoint routing pattern that matched the request URI path // The endpoint routing pattern that matched the request URI path
@ -74,9 +74,8 @@ type Context struct {
// patterns across a stack of sub-routers. // patterns across a stack of sub-routers.
RoutePatterns []string RoutePatterns []string
// methodNotAllowed hint
methodNotAllowed bool
methodsAllowed []methodTyp // allowed methods in case of a 405 methodsAllowed []methodTyp // allowed methods in case of a 405
methodNotAllowed bool
} }
// Reset a routing context to its initial state. // Reset a routing context to its initial state.
@ -92,6 +91,7 @@ func (x *Context) Reset() {
x.routeParams.Keys = x.routeParams.Keys[:0] x.routeParams.Keys = x.routeParams.Keys[:0]
x.routeParams.Values = x.routeParams.Values[:0] x.routeParams.Values = x.routeParams.Values[:0]
x.methodNotAllowed = false x.methodNotAllowed = false
x.methodsAllowed = x.methodsAllowed[:0]
x.parentCtx = nil x.parentCtx = nil
} }
@ -109,7 +109,7 @@ func (x *Context) URLParam(key string) string {
// RoutePattern builds the routing pattern string for the particular // RoutePattern builds the routing pattern string for the particular
// request, at the particular point during routing. This means, the value // request, at the particular point during routing. This means, the value
// will change throughout the execution of a request in a router. That is // will change throughout the execution of a request in a router. That is
// why its advised to only use this value after calling the next handler. // why it's advised to only use this value after calling the next handler.
// //
// For example, // For example,
// //
@ -121,10 +121,15 @@ func (x *Context) URLParam(key string) string {
// }) // })
// } // }
func (x *Context) RoutePattern() string { func (x *Context) RoutePattern() string {
if x == nil {
return ""
}
routePattern := strings.Join(x.RoutePatterns, "") routePattern := strings.Join(x.RoutePatterns, "")
routePattern = replaceWildcards(routePattern) routePattern = replaceWildcards(routePattern)
if routePattern != "/" {
routePattern = strings.TrimSuffix(routePattern, "//") routePattern = strings.TrimSuffix(routePattern, "//")
routePattern = strings.TrimSuffix(routePattern, "/") routePattern = strings.TrimSuffix(routePattern, "/")
}
return routePattern return routePattern
} }

View File

@ -107,12 +107,22 @@ func (mx *Mux) Use(middlewares ...func(http.Handler) http.Handler) {
// Handle adds the route `pattern` that matches any http method to // Handle adds the route `pattern` that matches any http method to
// execute the `handler` http.Handler. // execute the `handler` http.Handler.
func (mx *Mux) Handle(pattern string, handler http.Handler) { func (mx *Mux) Handle(pattern string, handler http.Handler) {
if method, rest, found := strings.Cut(pattern, " "); found {
mx.Method(method, rest, handler)
return
}
mx.handle(mALL, pattern, handler) mx.handle(mALL, pattern, handler)
} }
// HandleFunc adds the route `pattern` that matches any http method to // HandleFunc adds the route `pattern` that matches any http method to
// execute the `handlerFn` http.HandlerFunc. // execute the `handlerFn` http.HandlerFunc.
func (mx *Mux) HandleFunc(pattern string, handlerFn http.HandlerFunc) { func (mx *Mux) HandleFunc(pattern string, handlerFn http.HandlerFunc) {
if method, rest, found := strings.Cut(pattern, " "); found {
mx.Method(method, rest, handlerFn)
return
}
mx.handle(mALL, pattern, handlerFn) mx.handle(mALL, pattern, handlerFn)
} }
@ -250,20 +260,19 @@ func (mx *Mux) With(middlewares ...func(http.Handler) http.Handler) Router {
return im return im
} }
// Group creates a new inline-Mux with a fresh middleware stack. It's useful // Group creates a new inline-Mux with a copy of middleware stack. It's useful
// for a group of handlers along the same routing path that use an additional // for a group of handlers along the same routing path that use an additional
// set of middlewares. See _examples/. // set of middlewares. See _examples/.
func (mx *Mux) Group(fn func(r Router)) Router { func (mx *Mux) Group(fn func(r Router)) Router {
im := mx.With().(*Mux) im := mx.With()
if fn != nil { if fn != nil {
fn(im) fn(im)
} }
return im return im
} }
// Route creates a new Mux with a fresh middleware stack and mounts it // Route creates a new Mux and mounts it along the `pattern` as a subrouter.
// along the `pattern` as a subrouter. Effectively, this is a short-hand // Effectively, this is a short-hand call to Mount. See _examples/.
// call to Mount. See _examples/.
func (mx *Mux) Route(pattern string, fn func(r Router)) Router { func (mx *Mux) Route(pattern string, fn func(r Router)) Router {
if fn == nil { if fn == nil {
panic(fmt.Sprintf("chi: attempting to Route() a nil subrouter on '%s'", pattern)) panic(fmt.Sprintf("chi: attempting to Route() a nil subrouter on '%s'", pattern))
@ -352,19 +361,40 @@ func (mx *Mux) Middlewares() Middlewares {
// Note: the *Context state is updated during execution, so manage // Note: the *Context state is updated during execution, so manage
// the state carefully or make a NewRouteContext(). // the state carefully or make a NewRouteContext().
func (mx *Mux) Match(rctx *Context, method, path string) bool { func (mx *Mux) Match(rctx *Context, method, path string) bool {
return mx.Find(rctx, method, path) != ""
}
// Find searches the routing tree for the pattern that matches
// the method/path.
//
// Note: the *Context state is updated during execution, so manage
// the state carefully or make a NewRouteContext().
func (mx *Mux) Find(rctx *Context, method, path string) string {
m, ok := methodMap[method] m, ok := methodMap[method]
if !ok { if !ok {
return false return ""
} }
node, _, h := mx.tree.FindRoute(rctx, m, path) node, _, _ := mx.tree.FindRoute(rctx, m, path)
pattern := rctx.routePattern
if node != nil {
if node.subroutes == nil {
e := node.endpoints[m]
return e.pattern
}
if node != nil && node.subroutes != nil {
rctx.RoutePath = mx.nextRoutePath(rctx) rctx.RoutePath = mx.nextRoutePath(rctx)
return node.subroutes.Match(rctx, method, rctx.RoutePath) subPattern := node.subroutes.Find(rctx, method, rctx.RoutePath)
if subPattern == "" {
return ""
} }
return h != nil pattern = strings.TrimSuffix(pattern, "/*")
pattern += subPattern
}
return pattern
} }
// NotFoundHandler returns the default Mux 404 responder whenever a route // NotFoundHandler returns the default Mux 404 responder whenever a route
@ -441,6 +471,10 @@ func (mx *Mux) routeHTTP(w http.ResponseWriter, r *http.Request) {
// Find the route // Find the route
if _, _, h := mx.tree.FindRoute(rctx, method, routePath); h != nil { if _, _, h := mx.tree.FindRoute(rctx, method, routePath); h != nil {
if supportsPathValue {
setPathValue(rctx, r)
}
h.ServeHTTP(w, r) h.ServeHTTP(w, r)
return return
} }

21
vendor/github.com/go-chi/chi/v5/path_value.go generated vendored Normal file
View File

@ -0,0 +1,21 @@
//go:build go1.22 && !tinygo
// +build go1.22,!tinygo
package chi
import "net/http"
// supportsPathValue is true if the Go version is 1.22 and above.
//
// If this is true, `net/http.Request` has methods `SetPathValue` and `PathValue`.
const supportsPathValue = true
// setPathValue sets the path values in the Request value
// based on the provided request context.
func setPathValue(rctx *Context, r *http.Request) {
for i, key := range rctx.URLParams.Keys {
value := rctx.URLParams.Values[i]
r.SetPathValue(key, value)
}
}

19
vendor/github.com/go-chi/chi/v5/path_value_fallback.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
//go:build !go1.22 || tinygo
// +build !go1.22 tinygo
package chi
import "net/http"
// supportsPathValue is true if the Go version is 1.22 and above.
//
// If this is true, `net/http.Request` has methods `SetPathValue` and `PathValue`.
const supportsPathValue = false
// setPathValue sets the path values in the Request value
// based on the provided request context.
//
// setPathValue is only supported in Go 1.22 and above so
// this is just a blank function so that it compiles.
func setPathValue(rctx *Context, r *http.Request) {
}

View File

@ -730,11 +730,9 @@ func patNextSegment(pattern string) (nodeTyp, string, string, byte, int, int) {
tail = pattern[pe] tail = pattern[pe]
} }
var rexpat string key, rexpat, isRegexp := strings.Cut(key, ":")
if idx := strings.Index(key, ":"); idx >= 0 { if isRegexp {
nt = ntRegexp nt = ntRegexp
rexpat = key[idx+1:]
key = key[:idx]
} }
if len(rexpat) > 0 { if len(rexpat) > 0 {

4
vendor/modules.txt vendored
View File

@ -87,8 +87,8 @@ github.com/getsentry/sentry-go/internal/otel/baggage/internal/baggage
github.com/getsentry/sentry-go/internal/ratelimit github.com/getsentry/sentry-go/internal/ratelimit
# github.com/gin-gonic/gin v1.9.1 # github.com/gin-gonic/gin v1.9.1
## explicit; go 1.20 ## explicit; go 1.20
# github.com/go-chi/chi/v5 v5.0.10 # github.com/go-chi/chi/v5 v5.2.2
## explicit; go 1.14 ## explicit; go 1.20
github.com/go-chi/chi/v5 github.com/go-chi/chi/v5
# github.com/go-chi/cors v1.2.1 # github.com/go-chi/cors v1.2.1
## explicit; go 1.14 ## explicit; go 1.14