diff options
author | Vladimir Shushlin <vshushlin@gitlab.com> | 2022-02-22 13:17:56 +0300 |
---|---|---|
committer | Vladimir Shushlin <vshushlin@gitlab.com> | 2022-02-22 13:17:56 +0300 |
commit | e7ad17b8d0818dd4c94f3a06e81781f4068c1b97 (patch) | |
tree | 026e2619a53da1abf202322c0d4e656ed2e93ba3 /test | |
parent | dbd3785baf9f6af3c0c6a76ef44b12f3fd49b68a (diff) | |
parent | dec4b09ac1f6fdf98487d4db61055c1e64358c15 (diff) |
Merge branch 'reject-tls-2' into 'master'
feat: add rate limits on the TLS connection level
See merge request gitlab-org/gitlab-pages!700
Diffstat (limited to 'test')
-rw-r--r-- | test/acceptance/helpers_test.go | 12 | ||||
-rw-r--r-- | test/acceptance/ratelimiter_test.go | 128 |
2 files changed, 139 insertions, 1 deletions
diff --git a/test/acceptance/helpers_test.go b/test/acceptance/helpers_test.go index c44058ba..1b514a85 100644 --- a/test/acceptance/helpers_test.go +++ b/test/acceptance/helpers_test.go @@ -602,3 +602,15 @@ func copyFile(dest, src string) error { _, err = io.Copy(destFile, srcFile) return err } + +// RequireMetricEqual requests prometheus metrics and makes sure metric is there +func RequireMetricEqual(t *testing.T, metricsAddress, metricWithValue string) { + resp, err := http.Get(fmt.Sprintf("http://%s/metrics", metricsAddress)) + require.NoError(t, err) + + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + require.Contains(t, string(body), metricWithValue) +} diff --git a/test/acceptance/ratelimiter_test.go b/test/acceptance/ratelimiter_test.go index 02ba54f5..a97fdfb1 100644 --- a/test/acceptance/ratelimiter_test.go +++ b/test/acceptance/ratelimiter_test.go @@ -3,6 +3,7 @@ package acceptance_test import ( "fmt" "net/http" + "strconv" "testing" "time" @@ -77,7 +78,7 @@ func TestIPRateLimits(t *testing.T) { } } -func TestDomainateLimits(t *testing.T) { +func TestDomainRateLimits(t *testing.T) { testhelpers.StubFeatureFlagValue(t, feature.EnforceDomainRateLimits.EnvVariable, true) for name, tc := range ratelimitedListeners { @@ -112,6 +113,131 @@ func TestDomainateLimits(t *testing.T) { } } +func TestTLSRateLimits(t *testing.T) { + tests := map[string]struct { + spec ListenSpec + domainLimit bool + sourceIP string + enforceEnabled bool + }{ + "https_with_domain_limit": { + spec: httpsListener, + domainLimit: true, + sourceIP: "127.0.0.1", + enforceEnabled: true, + }, + "https_with_domain_limit_not_enforced": { + spec: httpsListener, + domainLimit: true, + sourceIP: "127.0.0.1", + enforceEnabled: false, + }, + "https_with_ip_limit": { + spec: httpsListener, + sourceIP: "127.0.0.1", + enforceEnabled: true, + }, + "https_with_ip_limit_not_enforced": { + spec: httpsListener, + sourceIP: "127.0.0.1", + enforceEnabled: false, + }, + "proxyv2_with_domain_limit": { + spec: httpsProxyv2Listener, + domainLimit: true, + sourceIP: "10.1.1.1", + enforceEnabled: true, + }, + "proxyv2_with_domain_limit_not_enforced": { + spec: httpsProxyv2Listener, + domainLimit: true, + sourceIP: "10.1.1.1", + enforceEnabled: false, + }, + "proxyv2_with_ip_limit": { + spec: httpsProxyv2Listener, + sourceIP: "10.1.1.1", + enforceEnabled: true, + }, + "proxyv2_with_ip_limit_not_enforced": { + spec: httpsProxyv2Listener, + sourceIP: "10.1.1.1", + enforceEnabled: false, + }, + } + + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + rateLimit := 5 + + options := []processOption{ + withListeners([]ListenSpec{tt.spec}), + withExtraArgument("metrics-address", ":42345"), + } + + featureName := feature.EnforceIPTLSRateLimits.EnvVariable + limitName := "tls_connections_by_source_ip" + + if tt.domainLimit { + options = append(options, + withExtraArgument("rate-limit-tls-domain", fmt.Sprint(rateLimit)), + withExtraArgument("rate-limit-tls-domain-burst", fmt.Sprint(rateLimit))) + + featureName = feature.EnforceDomainTLSRateLimits.EnvVariable + limitName = "tls_connections_by_domain" + } else { + options = append(options, + withExtraArgument("rate-limit-tls-source-ip", fmt.Sprint(rateLimit)), + withExtraArgument("rate-limit-tls-source-ip-burst", fmt.Sprint(rateLimit))) + } + + testhelpers.StubFeatureFlagValue(t, featureName, tt.enforceEnabled) + logBuf := RunPagesProcess(t, options...) + + // when we start the process we make 1 requests to verify that process is up + // it gets counted in the rate limit for IP, but host is different + if !tt.domainLimit { + rateLimit-- + } + + for i := 0; i < 10; i++ { + rsp, err := makeTLSRequest(t, tt.spec) + + if i >= rateLimit { + assertLogFound(t, logBuf, []string{ + "TLS connection rate-limited", + "\"req_host\":\"group.gitlab-example.com\"", + fmt.Sprintf("\"source_ip\":\"%s\"", tt.sourceIP), + "\"enforced\":" + strconv.FormatBool(tt.enforceEnabled)}) + + if tt.enforceEnabled { + require.Error(t, err) + require.Contains(t, err.Error(), "remote error: tls: internal error") + } + + continue + } + + require.NoError(t, err, "request: %d failed", i) + require.NoError(t, rsp.Body.Close()) + require.Equal(t, http.StatusOK, rsp.StatusCode, "request: %d failed", i) + } + expectedMetric := fmt.Sprintf( + "gitlab_pages_rate_limit_blocked_count{enforced=\"%t\",limit_name=\"%s\"} %v", + tt.enforceEnabled, limitName, 10-rateLimit) + + RequireMetricEqual(t, "127.0.0.1:42345", expectedMetric) + }) + } +} + +func makeTLSRequest(t *testing.T, spec ListenSpec) (*http.Response, error) { + req, err := http.NewRequest("GET", "https://group.gitlab-example.com/project", nil) + require.NoError(t, err) + + return spec.Client().Do(req) +} + func assertLogFound(t *testing.T, logBuf *LogCaptureBuffer, expectedLogs []string) { t.Helper() |