From ca17b2dbb6f3ad9a7005c318c2d7ff4db656e983 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Tue, 20 Jul 2021 18:30:36 +0200 Subject: [PATCH] Provide sensible defaults for `xpack.security.session.{lifespan|idleTimeout}`. (#106061) --- docs/settings/security-settings.asciidoc | 6 +-- .../user/security/session-management.asciidoc | 10 ++-- x-pack/plugins/security/server/config.test.ts | 54 ++++++++++--------- x-pack/plugins/security/server/config.ts | 23 +++----- .../server/session_management/session.test.ts | 16 +++--- .../session_management/session_index.test.ts | 12 +++-- 6 files changed, 61 insertions(+), 60 deletions(-) diff --git a/docs/settings/security-settings.asciidoc b/docs/settings/security-settings.asciidoc index a0dd8750ffc8f6..455ee76deefe30 100644 --- a/docs/settings/security-settings.asciidoc +++ b/docs/settings/security-settings.asciidoc @@ -271,7 +271,7 @@ You can configure the following settings in the `kibana.yml` file. |[[xpack-session-idleTimeout]] `xpack.security.session.idleTimeout` {ess-icon} | Ensures that user sessions will expire after a period of inactivity. This and <> are both -highly recommended. You can also specify this setting for <>. If this is _not_ set or set to `0`, then sessions will never expire due to inactivity. By default, this setting is not set. +highly recommended. You can also specify this setting for <>. If this is set to `0`, then sessions will never expire due to inactivity. By default, this value is 1 hour. 2+a| [TIP] @@ -281,8 +281,8 @@ Use a string of `[ms\|s\|m\|h\|d\|w\|M\|Y]` (e.g. '20m', '24h', '7d', '1w |[[xpack-session-lifespan]] `xpack.security.session.lifespan` {ess-icon} | Ensures that user sessions will expire after the defined time period. This behavior is also known as an "absolute timeout". If -this is _not_ set or set to `0`, user sessions could stay active indefinitely. This and <> are both highly -recommended. You can also specify this setting for <>. By default, this setting is not set. +this is set to `0`, user sessions could stay active indefinitely. This and <> are both highly +recommended. You can also specify this setting for <>. By default, this value is 30 days. 2+a| [TIP] diff --git a/docs/user/security/session-management.asciidoc b/docs/user/security/session-management.asciidoc index ac7a777eb05807..b0f27d45bb826e 100644 --- a/docs/user/security/session-management.asciidoc +++ b/docs/user/security/session-management.asciidoc @@ -12,24 +12,24 @@ To manage user sessions programmatically, {kib} exposes <[ms|s|m|h|d|w|M|Y]` (e.g. '20m', '24h', '7d', '1w'). For example, set the idle timeout to expire sessions after 1 hour of inactivity: +By default, sessions expire after 1 hour of inactivity. To define another value for a sliding session expiration, set the property in the `kibana.yml` configuration file. The idle timeout is formatted as a duration of `[ms|s|m|h|d|w|M|Y]` (e.g. '20m', '24h', '7d', '1w'). For example, set the idle timeout to expire sessions after 30 minutes of inactivity: -- [source,yaml] -------------------------------------------------------------------------------- -xpack.security.session.idleTimeout: "1h" +xpack.security.session.idleTimeout: "30m" -------------------------------------------------------------------------------- -- [[session-lifespan]] ==== Session lifespan -You can use `xpack.security.session.lifespan` to configure the maximum session duration or "lifespan" -- also known as the "absolute timeout". This and `xpack.security.session.idleTimeout` are both highly recommended. By default, sessions don't have a fixed lifespan, and if an idle timeout is defined, a session can still be extended indefinitely. To define a maximum session lifespan, set the property in the `kibana.yml` configuration file. The lifespan is formatted as a duration of `[ms|s|m|h|d|w|M|Y]` (e.g. '20m', '24h', '7d', '1w'). For example, set the lifespan to expire sessions after 30 days: +You can use `xpack.security.session.lifespan` to configure the maximum session duration or "lifespan" -- also known as the "absolute timeout". This and `xpack.security.session.idleTimeout` are both highly recommended. By default, a maximum session lifespan is 30 days. To define another lifespan, set the property in the `kibana.yml` configuration file. The lifespan is formatted as a duration of `[ms|s|m|h|d|w|M|Y]` (e.g. '20m', '24h', '7d', '1w'). For example, set the lifespan to expire sessions after 7 days: -- [source,yaml] -------------------------------------------------------------------------------- -xpack.security.session.lifespan: "30d" +xpack.security.session.lifespan: "7d" -------------------------------------------------------------------------------- -- @@ -38,7 +38,7 @@ xpack.security.session.lifespan: "30d" [IMPORTANT] ============================================================================ -If you specify neither session idle timeout nor lifespan, then {kib} will not automatically remove session information from the index unless you explicitly log out. This might lead to an infinitely growing session index. Configure the idle timeout and lifespan settings for the {kib} sessions so that they can be cleaned up even if you don't explicitly log out. +If you disable session idle timeout and lifespan, then Kibana will not automatically remove session information from the index unless you explicitly log out. This might lead to an infinitely growing session index. As long as either idle timeout or lifespan is configured, Kibana sessions will be cleaned up even if you don't explicitly log out. ============================================================================ You can configure the interval at which {kib} tries to remove expired and invalid sessions from the session index. By default, this value is 1 hour and cannot be less than 10 seconds. To define another interval, set the `xpack.security.session.cleanupInterval` property in the `kibana.yml` configuration file. The interval is formatted as a duration of `[ms|s|m|h|d|w|M|Y]` (e.g. '20m', '24h', '7d', '1w'). For example, schedule the session index cleanup to perform once a day: diff --git a/x-pack/plugins/security/server/config.test.ts b/x-pack/plugins/security/server/config.test.ts index 9e266d774e86ec..4593d9a7ad6820 100644 --- a/x-pack/plugins/security/server/config.test.ts +++ b/x-pack/plugins/security/server/config.test.ts @@ -61,6 +61,8 @@ describe('config schema', () => { "secureCookies": false, "session": Object { "cleanupInterval": "PT1H", + "idleTimeout": "PT1H", + "lifespan": "P30D", }, } `); @@ -110,6 +112,8 @@ describe('config schema', () => { "secureCookies": false, "session": Object { "cleanupInterval": "PT1H", + "idleTimeout": "PT1H", + "lifespan": "P30D", }, } `); @@ -158,6 +162,8 @@ describe('config schema', () => { "secureCookies": false, "session": Object { "cleanupInterval": "PT1H", + "idleTimeout": "PT1H", + "lifespan": "P30D", }, } `); @@ -1615,11 +1621,11 @@ describe('createConfig()', () => { it('returns default values if neither global nor provider specific settings are set', async () => { expect(createMockConfig().session.getExpirationTimeouts({ type: 'basic', name: 'basic1' })) .toMatchInlineSnapshot(` - Object { - "idleTimeout": null, - "lifespan": null, - } - `); + Object { + "idleTimeout": "PT1H", + "lifespan": "P30D", + } + `); }); it('correctly handles explicitly disabled global settings', async () => { @@ -1653,11 +1659,11 @@ describe('createConfig()', () => { name: 'basic1', }) ).toMatchInlineSnapshot(` - Object { - "idleTimeout": "PT0.123S", - "lifespan": null, - } - `); + Object { + "idleTimeout": "PT0.123S", + "lifespan": "P30D", + } + `); expect( createMockConfig({ session: { lifespan: 456 } }).session.getExpirationTimeouts({ @@ -1665,11 +1671,11 @@ describe('createConfig()', () => { name: 'basic1', }) ).toMatchInlineSnapshot(` - Object { - "idleTimeout": null, - "lifespan": "PT0.456S", - } - `); + Object { + "idleTimeout": "PT1H", + "lifespan": "PT0.456S", + } + `); expect( createMockConfig({ @@ -1692,7 +1698,7 @@ describe('createConfig()', () => { ).toMatchInlineSnapshot(` Object { "idleTimeout": "PT0.123S", - "lifespan": null, + "lifespan": "P30D", } `); @@ -1703,7 +1709,7 @@ describe('createConfig()', () => { }) ).toMatchInlineSnapshot(` Object { - "idleTimeout": null, + "idleTimeout": "PT1H", "lifespan": "PT0.456S", } `); @@ -1734,14 +1740,14 @@ describe('createConfig()', () => { .toMatchInlineSnapshot(` Object { "idleTimeout": "PT0.321S", - "lifespan": null, + "lifespan": "P30D", } `); expect(configWithoutGlobal.session.getExpirationTimeouts({ type: 'saml', name: 'saml1' })) .toMatchInlineSnapshot(` Object { "idleTimeout": "PT5M32.211S", - "lifespan": null, + "lifespan": "P30D", } `); @@ -1758,14 +1764,14 @@ describe('createConfig()', () => { .toMatchInlineSnapshot(` Object { "idleTimeout": "PT0.321S", - "lifespan": null, + "lifespan": "P30D", } `); expect(configWithGlobal.session.getExpirationTimeouts({ type: 'saml', name: 'saml1' })) .toMatchInlineSnapshot(` Object { "idleTimeout": "PT5M32.211S", - "lifespan": null, + "lifespan": "P30D", } `); }); @@ -1783,14 +1789,14 @@ describe('createConfig()', () => { expect(configWithoutGlobal.session.getExpirationTimeouts({ type: 'basic', name: 'basic1' })) .toMatchInlineSnapshot(` Object { - "idleTimeout": null, + "idleTimeout": "PT1H", "lifespan": "PT0.654S", } `); expect(configWithoutGlobal.session.getExpirationTimeouts({ type: 'saml', name: 'saml1' })) .toMatchInlineSnapshot(` Object { - "idleTimeout": null, + "idleTimeout": "PT1H", "lifespan": "PT11M5.544S", } `); @@ -1807,7 +1813,7 @@ describe('createConfig()', () => { expect(configWithGlobal.session.getExpirationTimeouts({ type: 'basic', name: 'basic1' })) .toMatchInlineSnapshot(` Object { - "idleTimeout": null, + "idleTimeout": "PT1H", "lifespan": "PT0.654S", } `); diff --git a/x-pack/plugins/security/server/config.ts b/x-pack/plugins/security/server/config.ts index ce83a92e23ae7f..6ce161a8988109 100644 --- a/x-pack/plugins/security/server/config.ts +++ b/x-pack/plugins/security/server/config.ts @@ -209,8 +209,12 @@ export const ConfigSchema = schema.object({ schema.string({ minLength: 32, defaultValue: 'a'.repeat(32) }) ), session: schema.object({ - idleTimeout: schema.maybe(schema.oneOf([schema.duration(), schema.literal(null)])), - lifespan: schema.maybe(schema.oneOf([schema.duration(), schema.literal(null)])), + idleTimeout: schema.oneOf([schema.duration(), schema.literal(null)], { + defaultValue: schema.duration().validate('1h'), + }), + lifespan: schema.oneOf([schema.duration(), schema.literal(null)], { + defaultValue: schema.duration().validate('30d'), + }), cleanupInterval: schema.duration({ defaultValue: '1h', validate(value) { @@ -385,7 +389,6 @@ export function createConfig( } function getSessionConfig(session: RawConfigType['session'], providers: ProvidersConfigType) { - const defaultAnonymousSessionLifespan = schema.duration().validate('30d'); return { cleanupInterval: session.cleanupInterval, getExpirationTimeouts({ type, name }: AuthenticationProvider) { @@ -393,21 +396,9 @@ function getSessionConfig(session: RawConfigType['session'], providers: Provider // possible types of values: `Duration`, `null` and `undefined`. The `undefined` type means that // provider doesn't override session config and we should fall back to the global one instead. const providerSessionConfig = providers[type as keyof ProvidersConfigType]?.[name]?.session; - - // We treat anonymous sessions differently since users can create them without realizing it. This may lead to a - // non controllable amount of sessions stored in the session index. To reduce the impact we set a 30 days lifespan - // for the anonymous sessions in case neither global nor provider specific lifespan is configured explicitly. - // We can remove this code once https://github.com/elastic/kibana/issues/68885 is resolved. - const providerLifespan = - type === 'anonymous' && - providerSessionConfig?.lifespan === undefined && - session.lifespan === undefined - ? defaultAnonymousSessionLifespan - : providerSessionConfig?.lifespan; - const [idleTimeout, lifespan] = [ [session.idleTimeout, providerSessionConfig?.idleTimeout], - [session.lifespan, providerLifespan], + [session.lifespan, providerSessionConfig?.lifespan], ].map(([globalTimeout, providerTimeout]) => { const timeout = providerTimeout === undefined ? globalTimeout ?? null : providerTimeout; return timeout && timeout.asMilliseconds() > 0 ? timeout : null; diff --git a/x-pack/plugins/security/server/session_management/session.test.ts b/x-pack/plugins/security/server/session_management/session.test.ts index dfe6ba343ca3c0..e1c67dca667f75 100644 --- a/x-pack/plugins/security/server/session_management/session.test.ts +++ b/x-pack/plugins/security/server/session_management/session.test.ts @@ -358,7 +358,7 @@ describe('Session', () => { session = new Session({ logger: loggingSystemMock.createLogger(), config: createConfig( - ConfigSchema.validate({ session: { idleTimeout: 123 } }), + ConfigSchema.validate({ session: { idleTimeout: 123, lifespan: null } }), loggingSystemMock.createLogger(), { isTLSEnabled: false } ), @@ -398,7 +398,7 @@ describe('Session', () => { session = new Session({ logger: loggingSystemMock.createLogger(), config: createConfig( - ConfigSchema.validate({ session: { lifespan } }), + ConfigSchema.validate({ session: { idleTimeout: null, lifespan } }), loggingSystemMock.createLogger(), { isTLSEnabled: false } ), @@ -472,9 +472,11 @@ describe('Session', () => { session = new Session({ logger: loggingSystemMock.createLogger(), - config: createConfig(ConfigSchema.validate({}), loggingSystemMock.createLogger(), { - isTLSEnabled: false, - }), + config: createConfig( + ConfigSchema.validate({ session: { idleTimeout: null, lifespan: null } }), + loggingSystemMock.createLogger(), + { isTLSEnabled: false } + ), sessionCookie: mockSessionCookie, sessionIndex: mockSessionIndex, }); @@ -527,7 +529,7 @@ describe('Session', () => { session = new Session({ logger: loggingSystemMock.createLogger(), config: createConfig( - ConfigSchema.validate({ session: { idleTimeout: 123 } }), + ConfigSchema.validate({ session: { idleTimeout: 123, lifespan: null } }), loggingSystemMock.createLogger(), { isTLSEnabled: false } ), @@ -718,7 +720,7 @@ describe('Session', () => { session = new Session({ logger: loggingSystemMock.createLogger(), config: createConfig( - ConfigSchema.validate({ session: { lifespan } }), + ConfigSchema.validate({ session: { idleTimeout: null, lifespan } }), loggingSystemMock.createLogger(), { isTLSEnabled: false } ), diff --git a/x-pack/plugins/security/server/session_management/session_index.test.ts b/x-pack/plugins/security/server/session_management/session_index.test.ts index 11fb4ca27f5902..bf99f4926b1d67 100644 --- a/x-pack/plugins/security/server/session_management/session_index.test.ts +++ b/x-pack/plugins/security/server/session_management/session_index.test.ts @@ -26,9 +26,11 @@ describe('Session index', () => { const sessionIndexOptions = { logger: loggingSystemMock.createLogger(), kibanaIndexName: '.kibana_some_tenant', - config: createConfig(ConfigSchema.validate({}), loggingSystemMock.createLogger(), { - isTLSEnabled: false, - }), + config: createConfig( + ConfigSchema.validate({ session: { idleTimeout: null, lifespan: null } }), + loggingSystemMock.createLogger(), + { isTLSEnabled: false } + ), elasticsearchClient: mockElasticsearchClient, }; @@ -239,7 +241,7 @@ describe('Session index', () => { logger: loggingSystemMock.createLogger(), kibanaIndexName: '.kibana_some_tenant', config: createConfig( - ConfigSchema.validate({ session: { lifespan: 456 } }), + ConfigSchema.validate({ session: { idleTimeout: null, lifespan: 456 } }), loggingSystemMock.createLogger(), { isTLSEnabled: false } ), @@ -315,7 +317,7 @@ describe('Session index', () => { logger: loggingSystemMock.createLogger(), kibanaIndexName: '.kibana_some_tenant', config: createConfig( - ConfigSchema.validate({ session: { idleTimeout } }), + ConfigSchema.validate({ session: { idleTimeout, lifespan: null } }), loggingSystemMock.createLogger(), { isTLSEnabled: false } ),