-
Notifications
You must be signed in to change notification settings - Fork 548
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
frontend: Allow blocking raw http requests
This is a setting that can be set in user overrides like this: ``` user1: blocked_requests: - path: /api/v1/series - method: DELETE - query_params foo: bar ``` Or a combination of these. Each entry is an AND condition
- Loading branch information
1 parent
fe1d711
commit 1777468
Showing
14 changed files
with
275 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-only | ||
|
||
package querymiddleware | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/go-kit/log" | ||
"github.com/go-kit/log/level" | ||
"github.com/grafana/dskit/tenant" | ||
"github.com/prometheus/client_golang/prometheus" | ||
"github.com/prometheus/client_golang/prometheus/promauto" | ||
|
||
apierror "github.com/grafana/mimir/pkg/api/error" | ||
"github.com/grafana/mimir/pkg/util/globalerror" | ||
) | ||
|
||
func newRequestBlockedError() error { | ||
return apierror.New(apierror.TypeBadData, globalerror.RequestBlocked.Message("the request has been blocked by the cluster administrator")) | ||
} | ||
|
||
type requestBlocker struct { | ||
limits Limits | ||
logger log.Logger | ||
blockedRequestsCounter *prometheus.CounterVec | ||
} | ||
|
||
func newRequestBlocker( | ||
limits Limits, | ||
logger log.Logger, | ||
registerer prometheus.Registerer, | ||
) *requestBlocker { | ||
blockedRequestsCounter := promauto.With(registerer).NewCounterVec(prometheus.CounterOpts{ | ||
Name: "cortex_query_frontend_rejected_requests_total", | ||
Help: "Number of HTTP requests that were rejected by the cluster administrator.", | ||
}, []string{"user"}) | ||
return &requestBlocker{ | ||
limits: limits, | ||
logger: logger, | ||
blockedRequestsCounter: blockedRequestsCounter, | ||
} | ||
} | ||
|
||
func (rb *requestBlocker) isBlocked(r *http.Request) error { | ||
tenants, err := tenant.TenantIDs(r.Context()) | ||
if err != nil { | ||
return nil | ||
} | ||
|
||
for _, tenant := range tenants { | ||
blockedRequests := rb.limits.BlockedRequests(tenant) | ||
|
||
for _, blockedRequest := range blockedRequests { | ||
if blockedPath := blockedRequest.Path; blockedPath != "" && blockedPath != r.URL.Path { | ||
continue | ||
} | ||
|
||
if blockedMethod := blockedRequest.Method; blockedMethod != "" && blockedMethod != r.Method { | ||
continue | ||
} | ||
|
||
if blockedParams := blockedRequest.QueryParams; len(blockedParams) > 0 { | ||
query := r.URL.Query() | ||
blockedByParams := false | ||
for key, blockedValue := range blockedParams { | ||
if query.Get(key) == blockedValue { | ||
blockedByParams = true | ||
break | ||
} | ||
} | ||
|
||
if !blockedByParams { | ||
continue | ||
} | ||
} | ||
|
||
level.Info(rb.logger).Log("msg", "request blocked", "user", tenant, "url", r.URL.String(), "method", r.Method) | ||
rb.blockedRequestsCounter.WithLabelValues(tenant).Inc() | ||
return newRequestBlockedError() | ||
} | ||
} | ||
return nil | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-only | ||
|
||
package querymiddleware | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/go-kit/log" | ||
"github.com/grafana/dskit/user" | ||
"github.com/prometheus/client_golang/prometheus" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/grafana/mimir/pkg/util/validation" | ||
) | ||
|
||
func TestRequestBlocker_IsBlocked(t *testing.T) { | ||
const userID = "user-1" | ||
// Mock the limits. | ||
limits := multiTenantMockLimits{ | ||
byTenant: map[string]mockLimits{ | ||
userID: { | ||
blockedRequests: []*validation.BlockedRequest{ | ||
{Path: "/blocked-by-path"}, | ||
{Method: "POST"}, | ||
{QueryParams: map[string]string{"foo": "bar"}}, | ||
{Path: "/blocked-by-path2", Method: "GET", QueryParams: map[string]string{"foo": "bar2"}}, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
tests := []struct { | ||
name string | ||
request func() *http.Request | ||
expected error | ||
}{ | ||
{ | ||
name: "request is not blocked", | ||
request: func() *http.Request { | ||
req, err := http.NewRequest(http.MethodGet, "/not-blocked", nil) | ||
require.NoError(t, err) | ||
return req | ||
}, | ||
expected: nil, | ||
}, | ||
{ | ||
name: "request is blocked by path", | ||
request: func() *http.Request { | ||
req, err := http.NewRequest(http.MethodGet, "/blocked-by-path", nil) | ||
require.NoError(t, err) | ||
return req | ||
}, | ||
expected: newRequestBlockedError(), | ||
}, | ||
{ | ||
name: "request is blocked by method", | ||
request: func() *http.Request { | ||
req, err := http.NewRequest(http.MethodPost, "/not-blocked", nil) | ||
require.NoError(t, err) | ||
return req | ||
}, | ||
expected: newRequestBlockedError(), | ||
}, | ||
{ | ||
name: "request is blocked by query params", | ||
request: func() *http.Request { | ||
req, err := http.NewRequest(http.MethodGet, "/not-blocked?foo=bar", nil) | ||
require.NoError(t, err) | ||
return req | ||
}, | ||
expected: newRequestBlockedError(), | ||
}, | ||
{ | ||
name: "request is blocked by path, method and query params", | ||
request: func() *http.Request { | ||
req, err := http.NewRequest(http.MethodGet, "/blocked-by-path2?foo=bar2", nil) | ||
require.NoError(t, err) | ||
return req | ||
}, | ||
expected: newRequestBlockedError(), | ||
}, | ||
{ | ||
name: "request does not fully match blocked request (different method)", | ||
request: func() *http.Request { | ||
req, err := http.NewRequest(http.MethodDelete, "/blocked-by-path2?foo=bar2", nil) | ||
require.NoError(t, err) | ||
return req | ||
}, | ||
expected: nil, | ||
}, | ||
{ | ||
name: "request does not fully match blocked request (different query params)", | ||
request: func() *http.Request { | ||
req, err := http.NewRequest(http.MethodGet, "/blocked-by-path2?foo=bar3", nil) | ||
require.NoError(t, err) | ||
return req | ||
}, | ||
expected: nil, | ||
}, | ||
{ | ||
name: "request does not fully match blocked request (different path)", | ||
request: func() *http.Request { | ||
req, err := http.NewRequest(http.MethodGet, "/blocked-by-path3?foo=bar2", nil) | ||
require.NoError(t, err) | ||
return req | ||
}, | ||
expected: nil, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
blocker := newRequestBlocker(limits, log.NewNopLogger(), prometheus.NewRegistry()) | ||
|
||
req := tt.request() | ||
ctx := user.InjectOrgID(context.Background(), userID) | ||
req = req.WithContext(ctx) | ||
|
||
err := blocker.isBlocked(req) | ||
assert.Equal(t, err, tt.expected) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-only | ||
|
||
package validation | ||
|
||
type BlockedRequest struct { | ||
Path string `yaml:"path,omitempty"` | ||
Method string `yaml:"method,omitempty"` | ||
QueryParams map[string]string `yaml:"query_params,omitempty"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters