feat: add flag to disaable all rate limits (#5570)

This commit is contained in:
Dean Sheather
2023-01-05 12:05:20 -06:00
committed by GitHub
parent ab7e676b54
commit 5a968e2f93
16 changed files with 292 additions and 110 deletions

View File

@ -82,24 +82,20 @@ type Options struct {
Auditor audit.Auditor
AgentConnectionUpdateFrequency time.Duration
AgentInactiveDisconnectTimeout time.Duration
// APIRateLimit is the minutely throughput rate limit per user or ip.
// Setting a rate limit <0 will disable the rate limiter across the entire
// app. Specific routes may have their own limiters.
APIRateLimit int
AWSCertificates awsidentity.Certificates
Authorizer rbac.Authorizer
AzureCertificates x509.VerifyOptions
GoogleTokenValidator *idtoken.Validator
GithubOAuth2Config *GithubOAuth2Config
OIDCConfig *OIDCConfig
PrometheusRegistry *prometheus.Registry
SecureAuthCookie bool
SSHKeygenAlgorithm gitsshkey.Algorithm
Telemetry telemetry.Reporter
TracerProvider trace.TracerProvider
GitAuthConfigs []*gitauth.Config
RealIPConfig *httpmw.RealIPConfig
TrialGenerator func(ctx context.Context, email string) error
AWSCertificates awsidentity.Certificates
Authorizer rbac.Authorizer
AzureCertificates x509.VerifyOptions
GoogleTokenValidator *idtoken.Validator
GithubOAuth2Config *GithubOAuth2Config
OIDCConfig *OIDCConfig
PrometheusRegistry *prometheus.Registry
SecureAuthCookie bool
SSHKeygenAlgorithm gitsshkey.Algorithm
Telemetry telemetry.Reporter
TracerProvider trace.TracerProvider
GitAuthConfigs []*gitauth.Config
RealIPConfig *httpmw.RealIPConfig
TrialGenerator func(ctx context.Context, email string) error
// TLSCertificates is used to mesh DERP servers securely.
TLSCertificates []tls.Certificate
TailnetCoordinator tailnet.Coordinator
@ -107,6 +103,13 @@ type Options struct {
DERPMap *tailcfg.DERPMap
SwaggerEndpoint bool
// APIRateLimit is the minutely throughput rate limit per user or ip.
// Setting a rate limit <0 will disable the rate limiter across the entire
// app. Some specific routes have their own configurable rate limits.
APIRateLimit int
LoginRateLimit int
FilesRateLimit int
MetricsCacheRefreshInterval time.Duration
AgentStatsRefreshInterval time.Duration
Experimental bool
@ -157,6 +160,12 @@ func New(options *Options) *API {
if options.APIRateLimit == 0 {
options.APIRateLimit = 512
}
if options.LoginRateLimit == 0 {
options.LoginRateLimit = 60
}
if options.FilesRateLimit == 0 {
options.FilesRateLimit = 12
}
if options.Authorizer == nil {
options.Authorizer = rbac.NewAuthorizer()
}
@ -230,6 +239,10 @@ func New(options *Options) *API {
Optional: false,
})
// API rate limit middleware. The counter is local and not shared between
// replicas or instances of this middleware.
apiRateLimiter := httpmw.RateLimit(options.APIRateLimit, time.Minute)
r.Use(
httpmw.Recover(api.Logger),
tracing.StatusWriterMiddleware,
@ -241,8 +254,8 @@ func New(options *Options) *API {
// handleSubdomainApplications checks if the first subdomain is a valid
// app URL. If it is, it will serve that application.
api.handleSubdomainApplications(
apiRateLimiter,
// Middleware to impose on the served application.
httpmw.RateLimit(options.APIRateLimit, time.Minute),
httpmw.ExtractAPIKey(httpmw.ExtractAPIKeyConfig{
DB: options.Database,
OAuth2Configs: oauthConfigs,
@ -268,7 +281,7 @@ func New(options *Options) *API {
apps := func(r chi.Router) {
r.Use(
httpmw.RateLimit(options.APIRateLimit, time.Minute),
apiRateLimiter,
httpmw.ExtractAPIKey(httpmw.ExtractAPIKeyConfig{
DB: options.Database,
OAuth2Configs: oauthConfigs,
@ -315,8 +328,9 @@ func New(options *Options) *API {
r.NotFound(func(rw http.ResponseWriter, r *http.Request) { httpapi.RouteNotFound(rw) })
r.Use(
// Specific routes can specify smaller limits.
httpmw.RateLimit(options.APIRateLimit, time.Minute),
// Specific routes can specify different limits, but every rate
// limit must be configurable by the admin.
apiRateLimiter,
)
r.Get("/", apiRoot)
// All CSP errors will be logged
@ -339,9 +353,7 @@ func New(options *Options) *API {
r.Route("/files", func(r chi.Router) {
r.Use(
apiKeyMiddleware,
// This number is arbitrary, but reading/writing
// file content is expensive so it should be small.
httpmw.RateLimit(12, time.Minute),
httpmw.RateLimit(options.FilesRateLimit, time.Minute),
)
r.Get("/{fileID}", api.fileByID)
r.Post("/", api.postFile)
@ -426,25 +438,25 @@ func New(options *Options) *API {
r.Route("/users", func(r chi.Router) {
r.Get("/first", api.firstUser)
r.Post("/first", api.postFirstUser)
r.Group(func(r chi.Router) {
// We use a tight limit for password login to protect
// against audit-log write DoS, pbkdf2 DoS, and simple
// brute-force attacks.
//
// Making this too small can break tests.
r.Use(httpmw.RateLimit(60, time.Minute))
r.Post("/login", api.postLogin)
})
r.Get("/authmethods", api.userAuthMethods)
r.Route("/oauth2", func(r chi.Router) {
r.Route("/github", func(r chi.Router) {
r.Use(httpmw.ExtractOAuth2(options.GithubOAuth2Config, options.HTTPClient))
r.Get("/callback", api.userOAuth2Github)
r.Group(func(r chi.Router) {
// We use a tight limit for password login to protect against
// audit-log write DoS, pbkdf2 DoS, and simple brute-force
// attacks.
//
// This value is intentionally increased during tests.
r.Use(httpmw.RateLimit(options.LoginRateLimit, time.Minute))
r.Post("/login", api.postLogin)
r.Route("/oauth2", func(r chi.Router) {
r.Route("/github", func(r chi.Router) {
r.Use(httpmw.ExtractOAuth2(options.GithubOAuth2Config, options.HTTPClient))
r.Get("/callback", api.userOAuth2Github)
})
})
r.Route("/oidc/callback", func(r chi.Router) {
r.Use(httpmw.ExtractOAuth2(options.OIDCConfig, options.HTTPClient))
r.Get("/", api.userOIDC)
})
})
r.Route("/oidc/callback", func(r chi.Router) {
r.Use(httpmw.ExtractOAuth2(options.OIDCConfig, options.HTTPClient))
r.Get("/", api.userOIDC)
})
r.Group(func(r chi.Router) {
r.Use(