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

Add options to set headers on upstream http requests #154

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

graemechristie
Copy link

@graemechristie graemechristie commented Jul 14, 2023

This PR adds the command line option extra-http-header, which facilitates setting arbitrary http headers on the upstream prometheus request. This is required to support queries to prometheus compatible endpoints like Grafana Mimir, which expect an "X-Scope-OrgID:" header in the request to specify the tenant to query the data for.

Additionally, I have added a rewrite-host-header-to parameter, which supports rewriting the host to a different value than the source request. This would be needed in the case where the upstream prometheus endpoint is an nginx proxy or simliar using host base proxying. This addresses the issue #135.

@graemechristie graemechristie force-pushed the add-header-command-line-options branch from 1b520e4 to 88275aa Compare July 14, 2023 08:53
@graemechristie graemechristie marked this pull request as draft July 14, 2023 09:05
@graemechristie graemechristie force-pushed the add-header-command-line-options branch from 2163b32 to 034166d Compare July 14, 2023 14:50
@graemechristie graemechristie marked this pull request as ready for review July 14, 2023 14:54
@graemechristie
Copy link
Author

Does not seem to be alot of activity in this repo. @SuperQ could you have a look at this PR ?

@graemechristie
Copy link
Author

@simonpasquier are you able to look this PR ?

@@ -262,6 +282,7 @@ func (sle StaticLabelEnforcer) ExtractLabel(next http.HandlerFunc) http.Handler
}

func NewRoutes(upstream *url.URL, label string, extractLabeler ExtractLabeler, opts ...Option) (*routes, error) {
//extraHttpHeaders []string, rewriteHostHeader string
Copy link
Contributor

Choose a reason for hiding this comment

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

to be removed?

Copy link
Author

Choose a reason for hiding this comment

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

I've removed that

main.go Outdated Show resolved Hide resolved
injectproxy/routes.go Outdated Show resolved Hide resolved
injectproxy/routes.go Outdated Show resolved Hide resolved
injectproxy/routes.go Outdated Show resolved Hide resolved
@@ -81,6 +83,8 @@ func main() {
"This option is checked after Prometheus APIs, you cannot override enforced API endpoints to be not enforced with this option. Use carefully as it can easily cause a data leak if the provided path is an important "+
"API (like /api/v1/configuration) which isn't enforced by prom-label-proxy. NOTE: \"all\" matching paths like \"/\" or \"\" and regex are not allowed.")
flagset.BoolVar(&errorOnReplace, "error-on-replace", false, "When specified, the proxy will return HTTP status code 400 if the query already contains a label matcher that differs from the one the proxy would inject.")
flagset.Var(&extraHttpHeaders, "extra-http-header", "Additional HTTP headers to add to the upstream prometheus query in the format 'header: value'. Can be repeated multiple times for additional headers.")
flagset.StringVar(&rewriteHostHeader, "rewrite-host-header-to", "", "Rewrite host header to supplied value when sending the query to the upstream URL.")
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we need a dedicated arg for the Host header or if we just handle it differently in the proxy? In the latter case, you'd do prom-label-proxy --extra-http-header "Host: foo.example.com" --extra-http-header "X-Scope-OrgID: 123". It would be one less argument to remember.

Copy link
Author

Choose a reason for hiding this comment

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

I lodged this PR a long time ago ... and I cannot remember if there was a valid reason for needing the separate argument/behaviour, or I just thought it was preferable to specify the simple rewrite-host-header argument than needing to know the internal mechanics and set the header manually. Regrardless - I primarily need just the extra http headers functionality to support Mimir so I am happy to resubmit this PR with just that option, and we can add the other option in a separate PR if it is required.

Copy link
Author

Choose a reason for hiding this comment

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

@simonpasquier see my comment on line 293 above.

Copy link
Author

Choose a reason for hiding this comment

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

So conceptually, "extra-http-headers" are user defined HTTP headers outside of the well known/mandatory headers such as host:. Thus the -extra-http-headers option only covers these headers in line with how the Go SDK handles these. Host is a specific use case of a mandatory/well known header that needs to be overridden in legitimate scenarios, so that has it's own flag. if we were to allow "Host:" to be overridden with the --extra-http-header flag, we would probably want to override all of the other mandatory http header edge cases as well, for very little or no benefit.

Choose a reason for hiding this comment

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

reason to separate Host header come from lines 288-300 from routes.go

Rewrite: func(r *httputil.ProxyRequest) {
r.SetURL(upstream)
if opt.rewriteHostHeader == "" {
r.Out.Host = r.In.Host
Copy link
Author

Choose a reason for hiding this comment

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

OK, I think this is the reason for the separate arguments. I do not think you can set well known mandatory headers like host using headers collection on the request, it has to be set using host property. You could technically scan for the key of "host" in the extra headers collection, then if found remove it from the collection and execute this logic .. but that feels hacky. I think the original implementation is the correct behaviour. @simonpasquier - Would you prefer I leave this as is or just remove the ability to set the host header all together ?

Choose a reason for hiding this comment

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

As Host header is very specific, I think it's better to let it alone, and add some treatment (iterate all extra header and check if ...) on each query seems not very optimized

@graemechristie graemechristie force-pushed the add-header-command-line-options branch 2 times, most recently from 6d6e516 to 9d63218 Compare November 1, 2023 10:13
graemechristie and others added 10 commits November 1, 2023 18:14
…to prometheus

Added option to set host header on upstream query to prometheus - addresses prometheus-community#135

Signed-off-by: Graeme Christie <gchristie@bunnings.com.au>
Signed-off-by: Graeme Christie <gchristie@bunnings.com.au>
Signed-off-by: Graeme Christie <gchristie@bunnings.com.au>
Signed-off-by: Graeme Christie <gchristie@bunnings.com.au>
Co-authored-by: Simon Pasquier <spasquie@redhat.com>
Signed-off-by: Graeme Christie <gchristie@bunnings.com.au>
Co-authored-by: Simon Pasquier <spasquie@redhat.com>
Signed-off-by: Graeme Christie <gchristie@bunnings.com.au>
Co-authored-by: Simon Pasquier <spasquie@redhat.com>
Signed-off-by: Graeme Christie <gchristie@bunnings.com.au>
Co-authored-by: Simon Pasquier <spasquie@redhat.com>
Signed-off-by: Graeme Christie <gchristie@bunnings.com.au>
Signed-off-by: Graeme Christie <gchristie@bunnings.com.au>
Signed-off-by: Graeme Christie <gchristie@bunnings.com.au>
Copy link

@kyrianae kyrianae left a comment

Choose a reason for hiding this comment

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

All seems ok to me
I tested this on my own use case and it's working, I did'nt any issue

Rewrite: func(r *httputil.ProxyRequest) {
r.SetURL(upstream)
if opt.rewriteHostHeader == "" {
r.Out.Host = r.In.Host

Choose a reason for hiding this comment

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

As Host header is very specific, I think it's better to let it alone, and add some treatment (iterate all extra header and check if ...) on each query seems not very optimized

@@ -81,6 +83,8 @@ func main() {
"This option is checked after Prometheus APIs, you cannot override enforced API endpoints to be not enforced with this option. Use carefully as it can easily cause a data leak if the provided path is an important "+
"API (like /api/v1/configuration) which isn't enforced by prom-label-proxy. NOTE: \"all\" matching paths like \"/\" or \"\" and regex are not allowed.")
flagset.BoolVar(&errorOnReplace, "error-on-replace", false, "When specified, the proxy will return HTTP status code 400 if the query already contains a label matcher that differs from the one the proxy would inject.")
flagset.Var(&extraHttpHeaders, "extra-http-header", "Additional HTTP headers to add to the upstream prometheus query in the format 'header: value'. Can be repeated multiple times for additional headers.")
flagset.StringVar(&rewriteHostHeader, "rewrite-host-header-to", "", "Rewrite host header to supplied value when sending the query to the upstream URL.")

Choose a reason for hiding this comment

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

reason to separate Host header come from lines 288-300 from routes.go

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.

3 participants