Skip to content

Commit

Permalink
feat: add api-rate-limit flag (#5013)
Browse files Browse the repository at this point in the history
  • Loading branch information
deansheather committed Nov 10, 2022
1 parent 2042b57 commit 8e5af82
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 1 deletion.
6 changes: 6 additions & 0 deletions cli/deployment/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,12 @@ func newConfig() *codersdk.DeploymentConfig {
Default: 10 * time.Minute,
},
},
APIRateLimit: &codersdk.DeploymentConfigField[int]{
Name: "API Rate Limit",
Usage: "Maximum number of requests per minute allowed to the API per user, or per IP address for unauthenticated users. Negative values mean no rate limit. Some API endpoints are always rate limited regardless of this value to prevent denial-of-service attacks.",
Flag: "api-rate-limit",
Default: 512,
},
Experimental: &codersdk.DeploymentConfigField[bool]{
Name: "Experimental",
Usage: "Enable experimental features. Experimental features are not ready for production.",
Expand Down
1 change: 1 addition & 0 deletions cli/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co
AgentStatsRefreshInterval: cfg.AgentStatRefreshInterval.Value,
DeploymentConfig: cfg,
PrometheusRegistry: prometheus.NewRegistry(),
APIRateLimit: cfg.APIRateLimit.Value,
}
if tlsConfig != nil {
options.TLSCertificates = tlsConfig.Certificates
Expand Down
88 changes: 88 additions & 0 deletions cli/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,94 @@ func TestServer(t *testing.T) {
cancelFunc()
<-serverErr
})

t.Run("RateLimit", func(t *testing.T) {
t.Parallel()

t.Run("Default", func(t *testing.T) {
t.Parallel()
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()

root, cfg := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--access-url", "http://example.com",
)
serverErr := make(chan error, 1)
go func() {
serverErr <- root.ExecuteContext(ctx)
}()
accessURL := waitAccessURL(t, cfg)
client := codersdk.New(accessURL)

resp, err := client.Request(ctx, http.MethodGet, "/api/v2/buildinfo", nil)
require.NoError(t, err)
defer resp.Body.Close()
require.Equal(t, http.StatusOK, resp.StatusCode)
require.Equal(t, "512", resp.Header.Get("X-Ratelimit-Limit"))
cancelFunc()
<-serverErr
})

t.Run("Changed", func(t *testing.T) {
t.Parallel()
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()

val := "100"
root, cfg := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--access-url", "http://example.com",
"--api-rate-limit", val,
)
serverErr := make(chan error, 1)
go func() {
serverErr <- root.ExecuteContext(ctx)
}()
accessURL := waitAccessURL(t, cfg)
client := codersdk.New(accessURL)

resp, err := client.Request(ctx, http.MethodGet, "/api/v2/buildinfo", nil)
require.NoError(t, err)
defer resp.Body.Close()
require.Equal(t, http.StatusOK, resp.StatusCode)
require.Equal(t, val, resp.Header.Get("X-Ratelimit-Limit"))
cancelFunc()
<-serverErr
})

t.Run("Disabled", func(t *testing.T) {
t.Parallel()
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()

root, cfg := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--access-url", "http://example.com",
"--api-rate-limit", "-1",
)
serverErr := make(chan error, 1)
go func() {
serverErr <- root.ExecuteContext(ctx)
}()
accessURL := waitAccessURL(t, cfg)
client := codersdk.New(accessURL)

resp, err := client.Request(ctx, http.MethodGet, "/api/v2/buildinfo", nil)
require.NoError(t, err)
defer resp.Body.Close()
require.Equal(t, http.StatusOK, resp.StatusCode)
require.Equal(t, "", resp.Header.Get("X-Ratelimit-Limit"))
cancelFunc()
<-serverErr
})
})
}

func generateTLSCertificate(t testing.TB, commonName ...string) (certPath, keyPath string) {
Expand Down
8 changes: 8 additions & 0 deletions cli/testdata/coder_server_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ Flags:
-a, --address string Bind address of the server.
Consumes $CODER_ADDRESS (default
"127.0.0.1:3000")
--api-rate-limit int Maximum number of requests per minute
allowed to the API per user, or per IP
address for unauthenticated users.
Negative values mean no rate limit. Some
API endpoints are always rate limited
regardless of this value to prevent
denial-of-service attacks.
Consumes $CODER_API_RATE_LIMIT (default 512)
--cache-dir string The directory to cache temporary files.
If unspecified and $CACHE_DIRECTORY is
set, it will be used for compatibility
Expand Down
6 changes: 5 additions & 1 deletion coderd/database/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}")

first=true
for fi in queries/*.sql.go; do
# Find the last line from the imports section and add 1.
# Find the last line from the imports section and add 1. We have to
# disable pipefail temporarily to avoid ERRPIPE errors when piping into
# `head -n1`.
set +o pipefail
cut=$(grep -n ')' "$fi" | head -n 1 | cut -d: -f1)
set -o pipefail
cut=$((cut + 1))

# Copy the header from the first file only, ignoring the source comment.
Expand Down
1 change: 1 addition & 0 deletions codersdk/deploymentconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type DeploymentConfig struct {
SCIMAPIKey *DeploymentConfigField[string] `json:"scim_api_key" typescript:",notnull"`
UserWorkspaceQuota *DeploymentConfigField[int] `json:"user_workspace_quota" typescript:",notnull"`
Provisioner *ProvisionerConfig `json:"provisioner" typescript:",notnull"`
APIRateLimit *DeploymentConfigField[int] `json:"api_rate_limit" typescript:",notnull"`
Experimental *DeploymentConfigField[bool] `json:"experimental" typescript:",notnull"`
}

Expand Down
1 change: 1 addition & 0 deletions site/src/api/typesGenerated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ export interface DeploymentConfig {
readonly scim_api_key: DeploymentConfigField<string>
readonly user_workspace_quota: DeploymentConfigField<number>
readonly provisioner: ProvisionerConfig
readonly api_rate_limit: DeploymentConfigField<number>
readonly experimental: DeploymentConfigField<boolean>
}

Expand Down

0 comments on commit 8e5af82

Please sign in to comment.