-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[New platform] Security plugin integration #33775
Comments
Pinging @elastic/kibana-platform |
Right now legacy platform defines 2 auth strategies:
|
@elastic/kibana-security would you mind to check everything here makes sense 😄? |
@restrry thanks for writing this up! Given @azasypkin's experience with the new platform, and his knowledge of our various authentication providers, I'd like to hear his opinion on this as well. Were you intending for this extension point to be utilized for both authentication and authorization, or would we have two different "lifecycle methods" that the new platform exposes? |
Does the authorization mechanism in the current state require incoming request interception? As I understand |
Spaces does currently in the master branch https://github.com/elastic/kibana/blob/master/x-pack/plugins/spaces/server/lib/space_request_interceptors.ts#L41 With the introduction of Feature Controls we're using the following code to intercept some API calls https://github.com/elastic/kibana/blob/granular-app-privileges/x-pack/plugins/security/server/lib/authorization/api_authorization.ts#L14 and we're doing something similar to block certain applications from being loaded https://github.com/elastic/kibana/blob/granular-app-privileges/x-pack/plugins/security/index.js#L231 |
Do we have any plans on defining framework-agnostic Kibana request lifecycle with lifecycle event based hooks similar to what Hapi has or there will be express-js-like mechanism?
It looks like we can group authc + all authz based interceptors internally and just register one Also it may depend on whether core will be treating API calls as a separate group of requests, IIRC there was a conversation about having something like |
Sorry for a long comment, I spent several days wrapping my head around current implementation and want to structure my thoughts. Right now we rely on 3 different stages for every request (in execution order in hapi framework, more info about hapi request lifecycle)
As a bottom line there are 2 main mechanics:
interface InterceptorToolkit {
next: () => ({ type: 'next' }),
redirected: (url: string) => ({ type: 'redirected', url }),
rejected: (error?: Error) => ({ type: 'rejected', error }),
}
core.http.registerResourceInterceptor((req: KibanaRequest, interceptorToolkit: InterceptorToolkit){
if(req.method === 'GET') return interceptorToolkit.redirect('/');
return interceptorToolkit.next();
});
// 3rd party plugin, for example dashboard_mode
plugins.security.addAuthorizationInterceptor(async function(req: KibanaRequest, interceptorToolkit: InterceptorToolkit, readonly user: UserStore, scopedTools){
if(user.roles.includes(...) || scopedTools.getSpaces(...)){
return interceptorToolkit.redirected(...)
}
});
// plugins/security.js
// in following steps we can restrict an access only to required auth headers instead of whole hapi request
core.http.registerAuthorizationInterceptor((req: HapiRequest, interceptorToolkit: InterceptorToolkit) => {
const authenticationResult = await authenticate(req);
if(authenticationResult.succeeded()){
for (const interceptor of this.getAuthorizationInterceptors()){
const authorizationResult = await interceptor(KibanaRequest.from(req), interceptorToolkit, authenticationResult.user, scopeToolsToRequest(req));
if(authorizationResult.redirected()) {
return interceptorToolkit.redirected(authorizationResult.redirectURL)
}
}); Or the implementation option where we separate authc/authz steps. interface AuthToolkit {
authenticated: () => ({ type: 'authenticated' }),
unauthenticated: (error?) => ({ type: 'unauthenticated', error }),
reject: (error: Error) => ({ type: 'reject', error }),
}
// in following steps we can restrict an access only to required auth headers instead of whole hapi request
core.http.registerAuthenticationInterceptor((req: HapiRequest, authToolkit: AuthToolkit, user: UserStore){
const authenticationResult = authenticate(req);
if(authenticationResult.succeeded()){
user.set(authenticationResult);
}
return authToolkit.authenticated();
});
core.http.registerAuthorizationInterceptor((req: HapiRequest, interceptorToolkit: InterceptorToolkit, readonly user: UserStore){
// user is scoped to the request. on the very first step we can support old approach with plugin.secuity.checkPriveledgesFor(request, user);
const result = plugin.secuity.checkPriveledgesFor(user);
if(result) {
return interceptorToolkit.next();
} else {
return interceptorToolkit.reject(new Error('not found'));
}
}); And note if we migrate Security plugin to NP we should change dependent parts (like |
Thanks for giving this so much thought, and a clear description of the current situation @restrry. I think it makes sense for the security plugin itself to handle all authentication and authorization, and provide extension points to the various plugins to "add" their own authorization. This allows the security plugin to determine when authentication/authorization should be performed, without requiring other plugin authors to understand the details of when security is enabled and auth should be performed. |
I'm comfortable with the merged approach through the security plugin as well. It's worth noting that one way or another the underlying ability to build this auth stuff through the |
I'm back to the problem. After talking with the Security team we figured out that several lifecycle stages are required.
|
I'm going to work on restriction access to raw hapi request object in Authorization headerKibana relays on Elasticsearch to do all heavy lifting for user authentication. Security plugin tries to apply different providers and authenticate a user by means of extending request headers with a custom authorization header. // some provider specific logic
request.headers.authorization = providerSpecificHeader;
try {
esClient.callWithRequest(request, 'shield.authenticate');
...
} catch(e) {
delete request.headers.authorization;
} Headers are mutated in order to retain public asScoped(req: { headers?: Headers } = {}) {...} Here I have a question: how critical if we expose Request body & query validationSAML provider expects a request to contain some provider specific information. |
I would absolutely prefer that they don't. The way that we do this today has always bothered me, and as far as I'm aware it's only being done this way because security was introduced after a significant portion of Kibana was already written. I'm open to reconsidering this entire approach, if you are :) Would it be possible for the
I believe we should be able to as we have this route where I believe we could perform this. @azasypkin you mind double-checking everything I've said here for correctness and to ensure you agree with my recommendations? |
In this case we might need to change api slightly: await server.plugins.security.authenticate(request, {
type: 'saml',
SAMLRequest: query.SAMLRequest,
SAMLResponse: body.SAMLResponse,
}); it could be useful, because we can use the same pattern for const { username, password } = request.body;
await server.plugins.security.authenticate(request, {
type: 'basic',
credentials: { username, password }
}); |
@restrry sounds reasonable to me! |
++, the validation schema should allow us to expect "unknown" keys as well (see OIDC route with Joi.object.unknown), don't remember if
Yes, I agree, thanks!
Exactly! That's what we're discussing for some time already: second argument for |
Authentication mechanism was migrated to the New Platform |
This issue is used to track progress and blockers for Security plugin New Platform migration:
Status
Blockers (in order of priority)
Provide a way to retrieve status changes of the Elasticsearch so that we can properly register privileges with the ES cluster Migrate status service and status page to New Platform #41983Get rid of dependency on XPack Main'sregisterLicenseCheckResultsGenerator
Kibana Platform plugins cannot access available features instart
method Kibana Platform plugins cannot access available features instart
method #65461Provide a way to render custom views HTML page rendering service #41964, [new-platform] Add support for chromeless applications #41981Allow defining SO mappings in the Kibana Platform plugin [Platform] SavedObject Mappings #50309Migrate Kibana plugin "System API" to New Platform Migrate Kibana plugin "System API" to New Platform #43970Allow rendering of "hidden"/chromeless apps (/login
,/logout
etc.) Update istanbul package #4198ExposeSavedObjectsClient
& Co. to NP plugins (e.g. register customScopedSavedObjectsClientFactory
, it's not a pressing need, this code can live in LP for some time) Migrate saved object service #33587Capabilities service. Plugin registers CapabilitiesModifier. Migrate capabilities mixin to New Platform service #45393Allow NP client-side plugins to access configuration values (e.g.xpack.security.sessionTimeout
) Add config value support in frontend to New Platform #41990Exposekibana.index
to NP plugins (used to prefix "applications" to apply Elasticsearch's application privileges). We can probably expose it as a part ofkibana
NP oss plugin if it's planned. need new platform plugin access tokibana.index
config value currently available in legacy plugins #46240Figure out how we can support or eliminate circular dependency between Spaces and Security NP plugins(added workaround for now, not a blocker)Add ability to retrieve current license information akaLicensing
NP plugin Migrate away from legacy xpack_main to new licensing plugin #43378NP loggers should be able to log optionalmeta
replicating legacylogWithMetadata
(we need this to migrateAuditLogger
to NP). Logging service should log meta data #44983Allow NP server-side plugins to accesspkg.version
(used to prefix API actions that are associated with the Elasticsearch's application privileges, and are used to perform the authorization checks) Expose packageInfo to the new platform plugins #45262Provide a way to define API routes and views with URLs relative to root or agree on a new BWC convention Introduce HttpApi and HttpResource services #44620ExposeserverBasePath
through CoreBasePath
service in addition to existing request scoped base pathFix issues with NP client-side HTTP interception http interception promise resolves when calling controller.halt() #42990 http interception responseError missing response #42311 http interception responseError missing request #42307addregisterAuth
basic implementation. PR: [New platform] HTTP & Security integration #34631addsuperseded byregisterOnRequest
basic implementation. PR: [New platform] HTTP & Security integration #34631OnPreAuth
hookintroduce separated Pre-Auth and Post-Auth hooks. PR: introduce pre-/post-auth request hooks for HttpServer #36690Protected/unprotected route definition. Any route should have the ability to declare itself asunprotected
to disable authentication. PR: introduce pre-/post-auth request hooks for HttpServer #36690The ability to "tag" routes as a part of authorization rules declaration. PR: Route tags #37344RefactorregisterAuth
to allow manipulating [cookie] session storage from any place ofregisterAuth
caller. PR: Session storage refactoring #37992restrict access to Hapi Request object in AuthenticationHandler. Discuss with Security team what extension points they need to implement authorization logic (access to authorization header, a resource tags, etc.) context: [New platform] HTTP & Security integration #34631 (comment)PR: Restrict access to hapi Request in registerAuth #38763
integrates NP and LP http servers to simplify Security integration. PR: New Platform and Legacy platform servers integration #39047extendkbn/config-schema
to supportunknown
keys forobject
type. PR: support unknown keys for object type in @kbn/schema-config #39448ScopedCookieSessionStorage
should gracefully handle case with multiple cookies to repeat Legacy platform logic, logging. PR: handle mupltiple cookies sent from a browser #39431Possible improvements
same site
session storage cookie option is configurable Allow for cookie'sSameSite
attribute to be configurable #60522Previous description
Security needs New Platform to provide a way to intercept all incoming requests to perform auth check. Security plugin is optional, thus flow shouldn't be affected if it is disabled. We can provide **async handler** that supports pausing request handling and finishing with one of the next scenarios: - resume execution if auth is succeeded. - interrupt in case of rejection. - redirectSecurity plugin operates low-level primitives and needs a way to controls:
Downstream plugins shouldn't have access to user credentials stored via those mechanisms. It's not a problem while we don't expose raw
request
to plugins.Original summary from #18024 (comment)
Won't be implemented in the first iteration
Related: #18024
Implementation
This auth handler can be Security specific, because it specifies cookie parameters - name, password(encryptionKey), path, secure, cookie validation. ```js setup(core, dependencies){ // If we don't want to expose registerAuth as a core capability // we can provide it only to Security plugin core.http.configureCookie({ name, password ...}); core.http.registerAuthMiddleware(async function(({cookie, headers}), authToolkit){ cookie.set(...) return authToolkit.succeeded(); ```Or the platform can provide the ability to register a request middleware. Middleware specifies to what request headers it wants to have access to
The text was updated successfully, but these errors were encountered: