Skip to content

Commit

Permalink
Improve session idle timeout, add session lifespan (#49855)
Browse files Browse the repository at this point in the history
This adds an absolute session timeout (lifespan) to user sessions.
It also improves the existing session timeout toast and the overall
user experience in several ways.
  • Loading branch information
jportner committed Nov 26, 2019
1 parent e721ec4 commit 38c17d6
Show file tree
Hide file tree
Showing 43 changed files with 1,524 additions and 385 deletions.
12 changes: 9 additions & 3 deletions docs/settings/security-settings.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,16 @@ is set to `true` if `server.ssl.certificate` and `server.ssl.key` are set. Set
this to `true` if SSL is configured outside of {kib} (for example, you are
routing requests through a load balancer or proxy).

`xpack.security.sessionTimeout`::
`xpack.security.session.idleTimeout`::
Sets the session duration (in milliseconds). By default, sessions stay active
until the browser is closed. When this is set to an explicit timeout, closing the
browser still requires the user to log back in to {kib}.
until the browser is closed. When this is set to an explicit idle timeout, closing
the browser still requires the user to log back in to {kib}.

`xpack.security.session.lifespan`::
Sets the maximum duration (in milliseconds), also known as "absolute timeout". By
default, a session can be renewed indefinitely. When this value is set, a session
will end once its lifespan is exceeded, even if the user is not idle. NOTE: if
`idleTimeout` is not set, this setting will still cause sessions to expire.

`xpack.security.loginAssistanceMessage`::
Adds a message to the login screen. Useful for displaying information about maintenance windows, links to corporate sign up pages etc.
7 changes: 4 additions & 3 deletions docs/user/security/authentication/index.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,10 @@ The following sections apply both to <<saml>> and <<oidc>>

Once the user logs in to {kib} Single Sign-On, either using SAML or OpenID Connect, {es} issues access and refresh tokens
that {kib} encrypts and stores them in its own session cookie. This way, the user isn't redirected to the Identity Provider
for every request that requires authentication. It also means that the {kib} session depends on the `xpack.security.sessionTimeout`
setting and the user is automatically logged out if the session expires. An access token that is stored in the session cookie
can expire, in which case {kib} will automatically renew it with a one-time-use refresh token and store it in the same cookie.
for every request that requires authentication. It also means that the {kib} session depends on the <<security-ui-settings,
`xpack.security.session.idleTimeout` and `xpack.security.session.lifespan`>> settings, and the user is automatically logged
out if the session expires. An access token that is stored in the session cookie can expire, in which case {kib} will
automatically renew it with a one-time-use refresh token and store it in the same cookie.

{kib} can only determine if an access token has expired if it receives a request that requires authentication. If both access
and refresh tokens have already expired (for example, after 24 hours of inactivity), {kib} initiates a new "handshake" and
Expand Down
27 changes: 21 additions & 6 deletions docs/user/security/securing-kibana.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,31 @@ xpack.security.encryptionKey: "something_at_least_32_characters"
For more information, see <<security-settings-kb,Security Settings in {kib}>>.
--

. Optional: Change the default session duration. By default, sessions stay
active until the browser is closed. To change the duration, set the
`xpack.security.sessionTimeout` property in the `kibana.yml` configuration file.
The timeout is specified in milliseconds. For example, set the timeout to 600000
to expire sessions after 10 minutes:
. Optional: Set a timeout to expire idle sessions. By default, a session stays
active until the browser is closed. To define a sliding session expiration, set
the `xpack.security.session.idleTimeout` property in the `kibana.yml`
configuration file. The idle timeout is specified in milliseconds. For example,
set the idle timeout to 600000 to expire idle sessions after 10 minutes:
+
--
[source,yaml]
--------------------------------------------------------------------------------
xpack.security.sessionTimeout: 600000
xpack.security.session.idleTimeout: 600000
--------------------------------------------------------------------------------
--

. Optional: Change the maximum session duration or "lifespan" -- also known as
the "absolute timeout". By default, a session stays active until the browser is
closed. If an idle timeout is defined, a session can still be extended
indefinitely. To define a maximum session lifespan, set the
`xpack.security.session.lifespan` property in the `kibana.yml` configuration
file. The lifespan is specified in milliseconds. For example, set the lifespan
to 28800000 to expire sessions after 8 hours:
+
--
[source,yaml]
--------------------------------------------------------------------------------
xpack.security.session.lifespan: 28800000
--------------------------------------------------------------------------------
--

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ kibana_vars=(
xpack.security.encryptionKey
xpack.security.secureCookies
xpack.security.sessionTimeout
xpack.security.session.idleTimeout
xpack.security.session.lifespan
xpack.security.loginAssistanceMessage
telemetry.enabled
telemetry.sendUsageFrom
Expand Down
5 changes: 5 additions & 0 deletions src/legacy/core_plugins/status_page/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ export default function (kibana) {
hidden: true,
url: '/status',
},
injectDefaultVars(server) {
return {
isStatusPageAnonymous: server.config().get('status.allowAnonymous'),
};
}
}
});
}
6 changes: 6 additions & 0 deletions src/plugins/status_page/kibana.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"id": "status_page",
"version": "kibana",
"server": false,
"ui": true
}
24 changes: 24 additions & 0 deletions src/plugins/status_page/public/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { PluginInitializer } from 'kibana/public';
import { StatusPagePlugin, StatusPagePluginSetup, StatusPagePluginStart } from './plugin';

export const plugin: PluginInitializer<StatusPagePluginSetup, StatusPagePluginStart> = () =>
new StatusPagePlugin();
39 changes: 39 additions & 0 deletions src/plugins/status_page/public/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { Plugin, CoreSetup } from 'kibana/public';

export class StatusPagePlugin implements Plugin<StatusPagePluginSetup, StatusPagePluginStart> {
public setup(core: CoreSetup) {
const isStatusPageAnonymous = core.injectedMetadata.getInjectedVar(
'isStatusPageAnonymous'
) as boolean;

if (isStatusPageAnonymous) {
core.http.anonymousPaths.register('/status');
}
}

public start() {}

public stop() {}
}

export type StatusPagePluginSetup = ReturnType<StatusPagePlugin['setup']>;
export type StatusPagePluginStart = ReturnType<StatusPagePlugin['start']>;
14 changes: 11 additions & 3 deletions x-pack/legacy/plugins/security/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ export const security = (kibana) => new kibana.Plugin({
enabled: Joi.boolean().default(true),
cookieName: Joi.any().description('This key is handled in the new platform security plugin ONLY'),
encryptionKey: Joi.any().description('This key is handled in the new platform security plugin ONLY'),
sessionTimeout: Joi.any().description('This key is handled in the new platform security plugin ONLY'),
session: Joi.object({
idleTimeout: Joi.any().description('This key is handled in the new platform security plugin ONLY'),
lifespan: Joi.any().description('This key is handled in the new platform security plugin ONLY'),
}).default(),
secureCookies: Joi.any().description('This key is handled in the new platform security plugin ONLY'),
loginAssistanceMessage: Joi.string().default(),
authorization: Joi.object({
Expand All @@ -44,9 +47,10 @@ export const security = (kibana) => new kibana.Plugin({
}).default();
},

deprecations: function ({ unused }) {
deprecations: function ({ rename, unused }) {
return [
unused('authorization.legacyFallback.enabled'),
rename('sessionTimeout', 'session.idleTimeout'),
];
},

Expand Down Expand Up @@ -89,7 +93,11 @@ export const security = (kibana) => new kibana.Plugin({

return {
secureCookies: securityPlugin.__legacyCompat.config.secureCookies,
sessionTimeout: securityPlugin.__legacyCompat.config.sessionTimeout,
session: {
tenant: server.newPlatform.setup.core.http.basePath.serverBasePath,
idleTimeout: securityPlugin.__legacyCompat.config.session.idleTimeout,
lifespan: securityPlugin.__legacyCompat.config.session.lifespan,
},
enableSpaceAwarePrivileges: server.config().get('xpack.spaces.enabled'),
};
},
Expand Down
14 changes: 3 additions & 11 deletions x-pack/legacy/plugins/security/public/hacks/on_session_timeout.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,20 @@
import _ from 'lodash';
import { uiModules } from 'ui/modules';
import { isSystemApiRequest } from 'ui/system_api';
import { Path } from 'plugins/xpack_main/services/path';
import { npSetup } from 'ui/new_platform';

/**
* Client session timeout is decreased by this number so that Kibana server
* can still access session content during logout request to properly clean
* user session up (invalidate access tokens, redirect to logout portal etc.).
* @type {number}
*/

const module = uiModules.get('security', []);
module.config(($httpProvider) => {
$httpProvider.interceptors.push((
$q,
) => {

const isUnauthenticated = Path.isUnauthenticated();
const isAnonymous = npSetup.core.http.anonymousPaths.isAnonymous(window.location.pathname);

function interceptorFactory(responseHandler) {
return function interceptor(response) {
if (!isUnauthenticated && !isSystemApiRequest(response.config)) {
npSetup.plugins.security.sessionTimeout.extend();
if (!isAnonymous && !isSystemApiRequest(response.config)) {
npSetup.plugins.security.sessionTimeout.extend(response.config.url);
}
return responseHandler(response);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ chrome
}
>
<EuiButton href={chrome.addBasePath('/')}>
<FormattedMessage id="xpack.security.loggedOut.login" defaultMessage="Login" />
<FormattedMessage id="xpack.security.loggedOut.login" defaultMessage="Log in" />
</EuiButton>
</AuthenticationStatePage>
</I18nContext>,
Expand Down
1 change: 1 addition & 0 deletions x-pack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@
"bluebird": "3.5.5",
"boom": "^7.2.0",
"brace": "0.11.1",
"broadcast-channel": "^3.0.3",
"chroma-js": "^1.4.1",
"classnames": "2.2.6",
"concat-stream": "1.6.2",
Expand Down
24 changes: 14 additions & 10 deletions x-pack/plugins/security/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,34 @@ import {
} from './session';

export class SecurityPlugin implements Plugin<SecurityPluginSetup, SecurityPluginStart> {
private sessionTimeout!: SessionTimeout;

public setup(core: CoreSetup) {
const { http, notifications, injectedMetadata } = core;
const { basePath, anonymousPaths } = http;
anonymousPaths.register('/login');
anonymousPaths.register('/logout');
anonymousPaths.register('/logged_out');

const sessionExpired = new SessionExpired(basePath);
const tenant = `${injectedMetadata.getInjectedVar('session.tenant', '')}`;
const sessionExpired = new SessionExpired(basePath, tenant);
http.intercept(new UnauthorizedResponseHttpInterceptor(sessionExpired, anonymousPaths));
const sessionTimeout = new SessionTimeout(
injectedMetadata.getInjectedVar('sessionTimeout', null) as number | null,
notifications,
sessionExpired,
http
);
http.intercept(new SessionTimeoutHttpInterceptor(sessionTimeout, anonymousPaths));
this.sessionTimeout = new SessionTimeout(notifications, sessionExpired, http, tenant);
http.intercept(new SessionTimeoutHttpInterceptor(this.sessionTimeout, anonymousPaths));

return {
anonymousPaths,
sessionTimeout,
sessionTimeout: this.sessionTimeout,
};
}

public start() {}
public start() {
this.sessionTimeout.start();
}

public stop() {
this.sessionTimeout.stop();
}
}

export type SecurityPluginSetup = ReturnType<SecurityPlugin['setup']>;
Expand Down
Loading

0 comments on commit 38c17d6

Please sign in to comment.