Restrict access to hapi Request in registerAuth#38763
Restrict access to hapi Request in registerAuth#38763mshustov merged 5 commits intoelastic:masterfrom
Conversation
e32deae to
b44d3db
Compare
💔 Build Failed |
a2f41d8 to
d1849b2
Compare
There was a problem hiding this comment.
if there are no authHeaders returned from registerAuth or registerAuth wasn't called, we will use headers from the incoming request.
There was a problem hiding this comment.
authHeaders is a map and we don't know its keys. @elastic/kibana-security we can make this value configurable when needed.
There was a problem hiding this comment.
question: secured can give a false sense of security especially when it's used in OSS or when security plugin is disabled, just sayin' 🙂 What does secured mean here?
There was a problem hiding this comment.
Headers aren't exposed by getFilteredHeaders method. We can call it filteredHeaders but it doesn't show an intention and doesn't answer why. 🤔
There was a problem hiding this comment.
Probably, we should make KibanaRequest an object literal created by a factory instead of the class to emulate private fields via closures. Also, the class exposes static methods are meant to be used only inside of the core.
There was a problem hiding this comment.
That sounds like a smart move to me, can be done in future PR.
There was a problem hiding this comment.
There was a problem hiding this comment.
should we extend all external cluster clients with this functionality or we should make it configurable?
There was a problem hiding this comment.
The only example I can contrive where we may want to call asScoped on a ClusterClient with a KibanaRequest and not get the auth from the internal HTTP server is the proxy plugin which has it's own HTTPServer. That said, that plugin is not planning to use header/credential forwarding, but it may make the createNewServer API behave strangely to 3rd party consumers.
There was a problem hiding this comment.
agree I want to extract all Kibana specific logic from HttpServer to HttpService in #38092
There was a problem hiding this comment.
- for now, Security can use FakeRequest to perform a request to
shield.authenticate.
also we can move all logic related to headers calculation to http_service. then cluster_client just call
return new ScopedClusterClient(
this.callAsInternalUser,
this.callAsCurrentUser,
this.getHeaders(request, this.config.requestHeadersWhitelist)
);what do you think?
- should we use
Legacy.Requestinstead ofRequestto emphasise it is a deprecated api? - request is optional
request?for BWC with the current code. what is the case whencluster.asScoped(undefined)and ``cluster.asScoped({headers: undefined})` are valid?
There was a problem hiding this comment.
should we use Legacy.Request instead of Request to emphasise it is a deprecated api?
That makes sense to me 👍
request is optional request? for BWC with the current code. what is the case when
cluster.asScoped(undefined) and ``cluster.asScoped({headers: undefined})` are valid?
Not sure I understand this issue. Both of those cases seem like they should have the same behavior. Am I missing something?
There was a problem hiding this comment.
Not sure I understand this issue. Both of those cases seem like they should have the same behavior. Am I missing something?
ok, re-phrase: is it a valid case to perform a request to ES when authorization header is not set? I thought we always require authentication creating default elasticuser. don't we?
There was a problem hiding this comment.
Yeah I'm not sure why request would ever be undefined, but the headers could be empty when Security is disabled or when using OSS Kibana. I believe that case would look more like cluster.asScoped({headers: {}}) (no Authorization header present).
💚 Build Succeeded |
💚 Build Succeeded |
Prevent exposing headers.authorization in KibanaRequest. Introduce a mechanism to associate authorization headers with an incoming request and retrieve its value to perform a request to elasticsearch cluster.
ddbdafe to
090fdde
Compare
| authenticate: adoptToHapiAuthFormat(fn, this.authState.set), | ||
| authenticate: adoptToHapiAuthFormat(fn, (req, { state, headers }) => { | ||
| this.authState.set(req, state); | ||
| this.authHeaders.set(req, headers); |
There was a problem hiding this comment.
I used 2 different storages because they have different use cases
- the state may be exposed to any downstream plugin
- headers are meant to be used only by core when call elasticsearch cluster
| private getHeaders( | ||
| request?: KibanaRequest | Request | FakeRequest | ||
| ): Record<string, string | string[] | undefined> { | ||
| if (!isRealRequest(request)) { |
There was a problem hiding this comment.
when migrate from Legacy to New platform, we don't need to support Request as a valid input and can switch to
if(isKibanaRequest(request)){
const authHeaders = this.getAuthHeaders(request);
const headers = toRawRequest(request).headers;
return { ...headers, ...authHeaders };
} else {
return request.headers;
}|
Pinging @elastic/kibana-platform |
|
Pinging @elastic/kibana-security |
💔 Build Failed |
💚 Build Succeeded |
There was a problem hiding this comment.
should we use Legacy.Request instead of Request to emphasise it is a deprecated api?
That makes sense to me 👍
request is optional request? for BWC with the current code. what is the case when
cluster.asScoped(undefined) and ``cluster.asScoped({headers: undefined})` are valid?
Not sure I understand this issue. Both of those cases seem like they should have the same behavior. Am I missing something?
There was a problem hiding this comment.
The only example I can contrive where we may want to call asScoped on a ClusterClient with a KibanaRequest and not get the auth from the internal HTTP server is the proxy plugin which has it's own HTTPServer. That said, that plugin is not planning to use header/credential forwarding, but it may make the createNewServer API behave strangely to 3rd party consumers.
There was a problem hiding this comment.
That sounds like a smart move to me, can be done in future PR.
💔 Build Failed |
💚 Build Succeeded |
* Prevent exposing Hapi.Request to registerAuth. Prevent exposing headers.authorization in KibanaRequest. Introduce a mechanism to associate authorization headers with an incoming request and retrieve its value to perform a request to elasticsearch cluster. * fix tests * address @joshdover comments
* Prevent exposing Hapi.Request to registerAuth. Prevent exposing headers.authorization in KibanaRequest. Introduce a mechanism to associate authorization headers with an incoming request and retrieve its value to perform a request to elasticsearch cluster. * fix tests * address @joshdover comments
|
@restrry can you please add a Dev Doc section to this PR for API changes document? |
|
@rayafratkina done |
💔 Build Failed |
Summary
In New Platform only a plugin calling
registerAuthhas read-access to the authorization header.This plugin can return headers that should be used to perform a request against Elasticsearch on behalf of end-user. Those headers are stored in the core and retrieved cluster client to perform a request.
If cluster client cannot find headers returned from
registerAuth, it will fallback to an incoming request headers.Checklist
Use
strikethroughsto remove checklist items you don't feel are applicable to this PR.[ ] This was checked for cross-browser compatibility, including a check against IE11[ ] Any text added follows EUI's writing guidelines, uses sentence case text and includes i18n support[ ] Documentation was added for features that require explanation or tutorials[ ] This was checked for keyboard-only and screenreader accessibilityFor maintainers
Dev docs
In the previous release, we introduced experimental
registerAuthhook in New Platform that allows end users to define custom authentication logic.We changed API of the
AuthenticationHandlerfunction.In 7.2:
In 7.3
We provide
KibanaRequestinstead ofhapiRequest object and remove sessionStorage from arguments. NowsessionStorageFactoryis returned as result ofregisterAuthcall.Note: as API is experimental is still a subject for further changes.