-
Notifications
You must be signed in to change notification settings - Fork 510
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
Changes from 6 commits
a564243
be0f7c8
27d2320
4f34840
6eb353d
84df15d
3b7ff49
a694532
ee974d8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package pipeline | ||
|
||
import ( | ||
"github.com/grafana/tempo/pkg/api" | ||
"io" | ||
"net/http" | ||
"regexp" | ||
"strings" | ||
"sync" | ||
) | ||
|
||
type traceQueryFilterWare struct { | ||
next http.RoundTripper | ||
filters []*regexp.Regexp | ||
} | ||
|
||
func NewTraceQueryFilterWare(next http.RoundTripper) http.RoundTripper { | ||
return &traceQueryFilterWare{ | ||
next: next, | ||
} | ||
} | ||
|
||
func NewTraceQueryFilterWareWithDenyList(denyList []string) Middleware { | ||
filter := make([]*regexp.Regexp, len(denyList)+1) | ||
for i := range denyList { | ||
exp, err := regexp.Compile(denyList[i]) | ||
if err == nil { | ||
filter[i] = exp | ||
} | ||
} | ||
|
||
return MiddlewareFunc(func(next http.RoundTripper) http.RoundTripper { | ||
return traceQueryFilterWare{ | ||
next: next, | ||
filters: filter, | ||
} | ||
}) | ||
} | ||
|
||
func (c traceQueryFilterWare) RoundTrip(req *http.Request) (*http.Response, error) { | ||
resp, err := c.next.RoundTrip(req) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
if err != nil { | ||
return resp, err | ||
} | ||
//Better way to do this? | ||
if len(c.filters) == 0 { | ||
return resp, nil | ||
} | ||
//need wait group | ||
u, err := api.ParseSearchRequest(req) | ||
if err != nil { | ||
return resp, err | ||
} | ||
|
||
qry := u.Query | ||
|
||
if len(qry) == 0 { | ||
return resp, nil | ||
} | ||
|
||
//Not sure this is a good idea / the best way to do this | ||
//Also probably not necessary | ||
|
||
match := make(chan bool, len(c.filters)) | ||
wg := sync.WaitGroup{} | ||
for range c.filters { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
wg.Add(1) | ||
} | ||
|
||
go func(qry string) { | ||
defer wg.Done() | ||
for _, re := range c.filters { | ||
if re.MatchString(qry) { | ||
match <- true | ||
return | ||
} | ||
} | ||
match <- false | ||
}(qry) | ||
|
||
go func() { | ||
wg.Wait() | ||
close(match) | ||
}() | ||
|
||
if <-match { | ||
|
||
return &http.Response{ | ||
StatusCode: http.StatusBadRequest, | ||
Status: http.StatusText(http.StatusBadRequest), | ||
Body: io.NopCloser(strings.NewReader("Query is temporarily blocked by your administrator.")), | ||
}, nil | ||
|
||
} | ||
return resp, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
package pipeline | ||
joe-elliott marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment.
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
?