-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Passthrough OAuth bearer token supplied to Query service through to ES storage #1599
Conversation
@@ -38,21 +38,18 @@ const ( | |||
|
|||
// ServiceOperationStorage stores service to operation pairs. | |||
type ServiceOperationStorage struct { | |||
ctx context.Context |
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.
+1
cmd/query/app/server.go
Outdated
func BearTokenPropagationHandler(logger *zap.Logger, h http.Handler) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
ctx := r.Context() | ||
authHeaderValue := r.Header.Get("Authorization") |
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.
What is the format of Authorization header?
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.
According to https://tools.ietf.org/html/rfc6750#section-2.1
It should be in the form : Authorization: "Bearer <token>"
cmd/query/app/server.go
Outdated
if authHeaderValue != "" { | ||
bearerToken := strings.Split(authHeaderValue, " ") | ||
if len(bearerToken) == 2 { | ||
ctx = context.WithValue(ctx, "Storage-Token", bearerToken) |
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.
The recommended way of storing values in Context is by using private keys:
type storageContextKeyType string
var storageContextKey = storageContextKeyType("Storage-Token")
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.
Thanks for the recomendation, I'll change it
cmd/query/app/server.go
Outdated
if queryOpts.BearerTokenPropagation { | ||
compressHandler = handlers.CompressHandler(BearTokenPropagationHandler(logger, r)) | ||
} else { | ||
compressHandler = handlers.CompressHandler(r) |
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.
avoid duplicating wrappers. There's no business meaning of calling CompressHandler twice in different context.
I suggest var h http.Hander = r
and then keep overwriting h
with every new decorator (including in L90)
cmd/query/app/server.go
Outdated
return &http.Server{ | ||
Handler: recoveryHandler(compressHandler), | ||
} | ||
} | ||
|
||
func BearTokenPropagationHandler(logger *zap.Logger, h http.Handler) http.Handler { |
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.
doesn't need to be public
pkg/es/config/config.go
Outdated
} | ||
} else { | ||
httpClient.Transport = httpTransport | ||
options = append(options, elastic.SetBasicAuth(c.Username, c.Password)) | ||
} | ||
|
||
if c.TokenFromContext { | ||
httpClient.Transport = &tokenAuthTransport{ |
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.
this will overwrite L281
pkg/es/config/config.go
Outdated
token := r.Context().Value("Storage-Token") | ||
if token != nil && token != "" { | ||
r.Header.Set("Authorization", "Bearer "+token.(string)) | ||
return tr.wrapped.RoundTrip(r) |
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.
repeats L356-357, please DRY
@@ -231,7 +231,7 @@ func (s *SpanReader) GetServices(ctx context.Context) ([]string, error) { | |||
defer span.Finish() | |||
currentTime := time.Now() | |||
jaegerIndices := s.timeRangeIndices(s.serviceIndexPrefix, currentTime.Add(-s.maxSpanAge), currentTime) | |||
return s.serviceOperationStorage.getServices(jaegerIndices) | |||
return s.serviceOperationStorage.getServices(jaegerIndices, ctx) |
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.
ctx
should be in the first position
e48b633
to
94aa253
Compare
Codecov Report
@@ Coverage Diff @@
## master #1599 +/- ##
==========================================
+ Coverage 98.72% 98.72% +<.01%
==========================================
Files 191 193 +2
Lines 9182 9211 +29
==========================================
+ Hits 9065 9094 +29
+ Misses 91 90 -1
- Partials 26 27 +1
Continue to review full report at Codecov.
|
22e1a92
to
e04a824
Compare
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.
Looks very good! I have only a couple of comments, especially about sanitizing the bearer value and about making sure that only the appropriate authorization types are propagated down.
headerValue := strings.Split(authHeaderValue, " ") | ||
token := "" | ||
if len(headerValue) == 2 { | ||
token = headerValue[1] |
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.
The way it is, you are probably propagating non-bearer authorization as well, such as Basic
. If this is what you want, you should rename the functions from bearerTokenPropagation
to authorizationHeaderPropagation
.
If you didn't intend to propagate the basic auth as well, check explicitly for the authorization type ("Bearer" == headerValue[0]
).
func bearTokenPropagationHandler(logger *zap.Logger, h http.Handler) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
ctx := r.Context() | ||
authHeaderValue := r.Header.Get("Authorization") |
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.
I think we also need to be checking for X-Forwarded-Access-Token
- as (for example) if using the OpenShift OAuthProxy - it will receive the bearer token from the UI, and if configured to do so, will pass it in the upstream request in the X-Forwarded-Access-Token
header instead of Authorization
.
@jpkrohling does this sound reasonable to you?
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.
Yes, it does. That's a common header set by reverse proxies.
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.
I put support for X-Forwarded-Access-Token, I tried to get token from there if Authorization header is not defined.
fbc712d
to
ecf5ed6
Compare
|
||
// ContextWithBearerToken set bearer token in context | ||
func ContextWithBearerToken(ctx context.Context, token string) context.Context { | ||
return context.WithValue(ctx, bearerToken, token) |
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.
Should this check whether the token has a value? Currently, if Authorization
is not Bearer
the supplied string will be empty? If empty token, then could just return the ctx
.
pkg/es/config/config.go
Outdated
r.Header.Set("Authorization", "Bearer "+tr.token) | ||
token := tr.token | ||
if tr.allowOverrideFromCtx { | ||
token, _ = spanstore.GetBearerToken(r.Context()) |
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.
Would be better to return the token into a local var, check a value has been found, before overwriting the token
. That way - if a request is sent in without a bearer token, it will still revert to the supplied tr.token
.
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.
well, I wasn't sure if we wanted to do that, but ok I'll do the change
@rubenvp8510 Once the changes have been applied, could you do a quick test using the operator with Oauth proxy cli parameter |
@objectiser sure! |
ecf5ed6
to
1649c83
Compare
…S storage Signed-off-by: Ruben Vargas <ruben.vp8510@gmail.com>
1649c83
to
53aafca
Compare
) | ||
|
||
|
||
func Test_bearTokenPropagationHandler(t *testing.T) { |
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.
Look, I found another bear!
Which problem is this PR solving?
To enable users to disable the propagation, this PR includes the possibility of enable/disable this behavior using a flag.
Short description of the changes