Skip to content

Commit

Permalink
Allow for regexps on the query params + add changelog
Browse files Browse the repository at this point in the history
  • Loading branch information
julienduchesne committed Jan 21, 2025
1 parent db71ad6 commit ebf7793
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
* [ENHANCEMENT] Query-frontend: include more information about read consistency in trace spans produced when using experimental ingest storage. #10412
* [ENHANCEMENT] Ingester: Hide tokens in ingester ring status page when ingest storage is enabled #10399
* [ENHANCEMENT] Ingester: add `active_series_additional_custom_trackers` configuration, in addition to the already existing `active_series_custom_trackers`. The `active_series_additional_custom_trackers` configuration allows you to configure additional custom trackers that get merged with `active_series_custom_trackers` at runtime. #10428
* [ENHANCEMENT] Query-frontend: Allow blocking raw http requests with the `blocked_requests` configuration. Requests can be blocked based on their path, method
* [BUGFIX] Distributor: Use a boolean to track changes while merging the ReplicaDesc components, rather than comparing the objects directly. #10185
* [BUGFIX] Querier: fix timeout responding to query-frontend when response size is very close to `-querier.frontend-client.grpc-max-send-msg-size`. #10154
* [BUGFIX] Query-frontend and querier: show warning/info annotations in some cases where they were missing (if a lazy querier was used). #10277
Expand Down
16 changes: 14 additions & 2 deletions pkg/frontend/querymiddleware/request_blocker.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package querymiddleware

import (
"net/http"
"regexp"

"github.com/go-kit/log"
"github.com/go-kit/log/level"
Expand Down Expand Up @@ -62,8 +63,19 @@ func (rb *requestBlocker) isBlocked(r *http.Request) error {
if blockedParams := blockedRequest.QueryParams; len(blockedParams) > 0 {
query := r.URL.Query()
blockedByParams := false
for key, blockedValue := range blockedParams {
if query.Get(key) == blockedValue {
for key, blocked := range blockedParams {
if blocked.IsRegexp {
blockedRegexp, err := regexp.Compile(blocked.Value)
if err != nil {
level.Error(rb.logger).Log("msg", "failed to compile regexp. Not blocking", "regexp", blocked.Value, "err", err)
continue
}

if blockedRegexp.MatchString(query.Get(key)) {
blockedByParams = true
break
}
} else if query.Get(key) == blocked.Value {
blockedByParams = true
break
}
Expand Down
33 changes: 31 additions & 2 deletions pkg/frontend/querymiddleware/request_blocker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,19 @@ func TestRequestBlocker_IsBlocked(t *testing.T) {
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"}},
{QueryParams: map[string]validation.BlockedRequestQueryParam{"foo": validation.BlockedRequestQueryParam{Value: "bar"}}},
{
Path: "/blocked-by-path2", Method: "GET",
QueryParams: map[string]validation.BlockedRequestQueryParam{"foo": validation.BlockedRequestQueryParam{Value: "bar2"}},
},
{
Path: "/block-by-query-regexp", Method: "GET",
QueryParams: map[string]validation.BlockedRequestQueryParam{"foo": validation.BlockedRequestQueryParam{Value: ".*hello.*", IsRegexp: true}},
},
// Invalid regexp should not block anything.
{
QueryParams: map[string]validation.BlockedRequestQueryParam{"foo": validation.BlockedRequestQueryParam{Value: "\bar", IsRegexp: true}},
},
},
},
},
Expand Down Expand Up @@ -109,6 +120,24 @@ func TestRequestBlocker_IsBlocked(t *testing.T) {
},
expected: nil,
},
{
name: "regexp does not match",
request: func() *http.Request {
req, err := http.NewRequest(http.MethodGet, "/block-by-query-regexp?foo=test", nil)
require.NoError(t, err)
return req
},
expected: nil,
},
{
name: "regexp matches",
request: func() *http.Request {
req, err := http.NewRequest(http.MethodGet, "/block-by-query-regexp?foo=my-value-hello-test", nil)
require.NoError(t, err)
return req
},
expected: newRequestBlockedError(),
},
}

for _, tt := range tests {
Expand Down
4 changes: 2 additions & 2 deletions pkg/frontend/querymiddleware/roundtrip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -524,8 +524,8 @@ func TestTripperware_BlockedRequests(t *testing.T) {
blockedRequests: []*validation.BlockedRequest{
{
Path: "/api/v1/series",
QueryParams: map[string]string{
"match[]": "{my_env=\"production\"}",
QueryParams: map[string]validation.BlockedRequestQueryParam{
"match[]": {Value: ".*\"production\".*", IsRegexp: true},
},
},
},
Expand Down
11 changes: 8 additions & 3 deletions pkg/util/validation/blocked_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
package validation

type BlockedRequest struct {
Path string `yaml:"path,omitempty"`
Method string `yaml:"method,omitempty"`
QueryParams map[string]string `yaml:"query_params,omitempty"`
Path string `yaml:"path,omitempty"`
Method string `yaml:"method,omitempty"`
QueryParams map[string]BlockedRequestQueryParam `yaml:"query_params,omitempty"`
}

type BlockedRequestQueryParam struct {
Value string `yaml:"value"`
IsRegexp bool `yaml:"is_regexp,omitempty"`
}

0 comments on commit ebf7793

Please sign in to comment.