Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a configurable list of TraceQL queries that are immediately 400'ed #3780

Closed
wants to merge 9 commits into from

Conversation

maliciousbucket
Copy link

What this PR does:
Adds a new filter middleware to the search and metrics endpoints that matches the search query against a set of regex expressions. If it matches, a 400 result and generic message are returned.

Regex patterns can be added tot he config under FilterRegexes.

  • I did try to write unit tests for this based off existing tests, but I am unsure how to set them up and test this feature properly.
    • It was manually tested by hardcoding values into the filter and running the tests for the search handlers
  • The implementation of the matchng will probably need to be reworked

I would appreciate some guidance with regards to refactoring it and writign proper unit tests.

Which issue(s) this PR fixes:
Fixes #3769

Checklist

  • Tests updated
  • Documentation added
  • CHANGELOG.md updated - the order of entries should be [CHANGE], [FEATURE], [ENHANCEMENT], [BUGFIX]

@CLAassistant
Copy link

CLAassistant commented Jun 17, 2024

CLA assistant check
All committers have signed the CLA.

Copy link
Member

@joe-elliott joe-elliott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for giving this a shot!

Timeout time.Duration `yaml:"timeout,omitempty"`
Sharder SearchSharderConfig `yaml:",inline"`
SLO SLOConfig `yaml:",inline"`
FilterPatterns []string `yaml:"filter_patterns,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: for clarity rename to BlockedQueries

let's add the same field and pipeline item to MetricsConfig.

these two http requests are parsed differently so perhaps NewTraceQueryFilterWareWithDenyList takes a func like: func (*http.Request) string ?

}

func (c traceQueryFilterWare) RoundTrip(req *http.Request) (*http.Response, error) {
resp, err := c.next.RoundTrip(req)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's work on the order we are doing things in this function. it should be something like:

// if no filters just return c.next.RoundTrip()

// parse http request to get current query

// if current query has match in blocked queries then return 400 with a relevant body

// return c.next.RoundTrip

refactor NewTraceQueryFilterWare to take a func for parsing the query. Cleanup and refactor the Next function to follow correct order of operations
@maliciousbucket
Copy link
Author

Thanks for the feedback! I've done some refactoring and added some basic unit tests. Fairly sure I'm building the URL's wrong for the tests ( hence the use of the raw url in the ParseSearchRequetQuery function). What's the best way to make the traceql queries?

@@ -35,7 +35,7 @@ type SearchConfig struct {
Timeout time.Duration `yaml:"timeout,omitempty"`
Sharder SearchSharderConfig `yaml:",inline"`
SLO SLOConfig `yaml:",inline"`
FilterPatterns []string `yaml:"filter_patterns,omitempty"`
BlockedQueries []string `yaml:"blocked-queries_patterns,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
BlockedQueries []string `yaml:"blocked-queries_patterns,omitempty"`
BlockedQueries []string `yaml:"blocked_queries,omitempty"`

u, err := api.ParseSearchRequest(req)
if err != nil {
return resp, err
if c.filters == nil || len(c.filters) == 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in go len(<nil slice>) == 0

Suggested change
if c.filters == nil || len(c.filters) == 0 {
if len(c.filters) == 0 {


//query, _ := api.ParseSearchTagValuesRequestV2(req)

//query, _ := api.ParseSearchRequest(req)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should be this one.


match := make(chan bool, len(c.filters))
wg := sync.WaitGroup{}
for range c.filters {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for concurrency. let's keep this simple and just check them one at a time inline

@@ -97,7 +99,7 @@ func New(cfg Config, next http.RoundTripper, o overrides.Interface, reader tempo
multiTenantMiddleware(cfg, logger),
newAsyncSearchSharder(reader, o, cfg.Search.Sharder, logger),
},
[]pipeline.Middleware{cacheWare, statusCodeWare, retryWare},
[]pipeline.Middleware{cacheWare, statusCodeWare, retryWare, searchQueryFilterWare},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

searchQueryFilterWare should be the first in the list of the AsyncMiddleware. this will allow it to refuse a request before we do any work. it does mean it needs to be shaped like:

https://github.com/grafana/tempo/blob/main/modules/frontend/search_sharder.go#L51

@joe-elliott
Copy link
Member

We really appreciate your efforts here, but we have a sudden need for this internally and need to get this feature in. Thank you so much for giving this a shot!

Closing in favor of #3963

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Create a configurable list of TraceQL queries that are immediately 400'ed
3 participants