Skip to content
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

[7.x] Enable bearer scheme by default to support service token authorization (#112654) #115557

Merged
merged 2 commits into from
Oct 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/settings/security-settings.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ There is a very limited set of cases when you'd want to change these settings. F
| Determines if HTTP authentication schemes used by the enabled authentication providers should be automatically supported during HTTP authentication. By default, this setting is set to `true`.

| `xpack.security.authc.http.schemes[]`
| List of HTTP authentication schemes that {kib} HTTP authentication should support. By default, this setting is set to `['apikey']` to support HTTP authentication with <<api-keys, `ApiKey`>> scheme.
| List of HTTP authentication schemes that {kib} HTTP authentication should support. By default, this setting is set to `['apikey', 'bearer']` to support HTTP authentication with the <<api-keys, `ApiKey`>> and <<http-authentication, `Bearer`>> schemes.

|===

Expand Down
6 changes: 3 additions & 3 deletions docs/user/security/authentication/index.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -437,14 +437,14 @@ This type of authentication is usually useful for machine-to-machine interaction

By default {kib} supports <<api-keys, `ApiKey`>> authentication scheme _and_ any scheme supported by the currently enabled authentication provider. For example, `Basic` authentication scheme is automatically supported when basic authentication provider is enabled, or `Bearer` scheme when any of the token based authentication providers is enabled (Token, SAML, OpenID Connect, PKI or Kerberos). But it's also possible to add support for any other authentication scheme in the `kibana.yml` configuration file, as follows:

NOTE: Don't forget to explicitly specify default `apikey` scheme when you just want to add a new one to the list.
NOTE: Don't forget to explicitly specify the default `apikey` and `bearer` schemes when you just want to add a new one to the list.

[source,yaml]
--------------------------------------------------------------------------------
xpack.security.authc.http.schemes: [apikey, basic, something-custom]
xpack.security.authc.http.schemes: [apikey, bearer, basic, something-custom]
--------------------------------------------------------------------------------

With this configuration, you can send requests to {kib} with the `Authorization` header using `ApiKey`, `Basic` or `Something-Custom` HTTP schemes (case insensitive). Under the hood, {kib} relays this header to {es}, then {es} authenticates the request using the credentials in the header.
With this configuration, you can send requests to {kib} with the `Authorization` header using `ApiKey`, `Bearer`, `Basic` or `Something-Custom` HTTP schemes (case insensitive). Under the hood, {kib} relays this header to {es}, then {es} authenticates the request using the credentials in the header.

[float]
[[embedded-content-authentication]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ describe('Authenticator', () => {
expect(
jest.requireMock('./providers/http').HTTPAuthenticationProvider
).toHaveBeenCalledWith(expect.anything(), {
supportedSchemes: new Set(['apikey', 'basic']),
supportedSchemes: new Set(['apikey', 'bearer', 'basic']),
});
});

Expand Down Expand Up @@ -238,7 +238,9 @@ describe('Authenticator', () => {

expect(
jest.requireMock('./providers/http').HTTPAuthenticationProvider
).toHaveBeenCalledWith(expect.anything(), { supportedSchemes: new Set(['apikey']) });
).toHaveBeenCalledWith(expect.anything(), {
supportedSchemes: new Set(['apikey', 'bearer']),
});
});

it('disabled if explicitly disabled', () => {
Expand Down
9 changes: 9 additions & 0 deletions x-pack/plugins/security/server/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ describe('config schema', () => {
"enabled": true,
"schemes": Array [
"apikey",
"bearer",
],
},
"providers": Object {
Expand Down Expand Up @@ -78,6 +79,7 @@ describe('config schema', () => {
"enabled": true,
"schemes": Array [
"apikey",
"bearer",
],
},
"providers": Object {
Expand Down Expand Up @@ -129,6 +131,7 @@ describe('config schema', () => {
"enabled": true,
"schemes": Array [
"apikey",
"bearer",
],
},
"providers": Object {
Expand Down Expand Up @@ -305,6 +308,7 @@ describe('config schema', () => {
"enabled": true,
"schemes": Array [
"apikey",
"bearer",
],
},
"oidc": Object {
Expand Down Expand Up @@ -336,6 +340,7 @@ describe('config schema', () => {
"enabled": true,
"schemes": Array [
"apikey",
"bearer",
],
},
"oidc": Object {
Expand Down Expand Up @@ -367,6 +372,7 @@ describe('config schema', () => {
"enabled": true,
"schemes": Array [
"apikey",
"bearer",
],
},
"providers": Array [
Expand All @@ -385,6 +391,7 @@ describe('config schema', () => {
"enabled": true,
"schemes": Array [
"apikey",
"bearer",
],
},
"providers": Array [
Expand All @@ -406,6 +413,7 @@ describe('config schema', () => {
"enabled": true,
"schemes": Array [
"apikey",
"bearer",
],
},
"providers": Array [
Expand Down Expand Up @@ -1479,6 +1487,7 @@ describe('createConfig()', () => {
"enabled": true,
"schemes": Array [
"apikey",
"bearer",
],
},
"providers": Object {
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/security/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ export const ConfigSchema = schema.object({
http: schema.object({
enabled: schema.boolean({ defaultValue: true }),
autoSchemesEnabled: schema.boolean({ defaultValue: true }),
schemes: schema.arrayOf(schema.string(), { defaultValue: ['apikey'] }),
schemes: schema.arrayOf(schema.string(), { defaultValue: ['apikey', 'bearer'] }),
}),
}),
audit: schema.object(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('Security UsageCollector', () => {
authProviderCount: 1,
enabledAuthProviders: ['basic'],
loginSelectorEnabled: false,
httpAuthSchemes: ['apikey'],
httpAuthSchemes: ['apikey', 'bearer'],
sessionIdleTimeoutInMinutes: 0,
sessionLifespanInMinutes: 0,
sessionCleanupInMinutes: 60,
Expand Down
1 change: 1 addition & 0 deletions x-pack/scripts/functional_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ require('@kbn/test').runTestsCli([
require.resolve('../test/security_api_integration/session_lifespan.config.ts'),
require.resolve('../test/security_api_integration/login_selector.config.ts'),
require.resolve('../test/security_api_integration/audit.config.ts'),
require.resolve('../test/security_api_integration/http_bearer.config.ts'),
require.resolve('../test/security_api_integration/kerberos.config.ts'),
require.resolve('../test/security_api_integration/kerberos_anonymous_access.config.ts'),
require.resolve('../test/security_api_integration/pki.config.ts'),
Expand Down
36 changes: 36 additions & 0 deletions x-pack/test/security_api_integration/http_bearer.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { FtrConfigProviderContext } from '@kbn/test';
import { services } from './services';

export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts'));

return {
testFiles: [require.resolve('./tests/http_bearer')],
servers: xPackAPITestsConfig.get('servers'),
security: { disableTestUser: true },
services,
junit: {
reportName: 'X-Pack Security API Integration Tests (HTTP Bearer)',
},

esTestCluster: {
...xPackAPITestsConfig.get('esTestCluster'),
serverArgs: [
...xPackAPITestsConfig.get('esTestCluster.serverArgs'),
'xpack.security.authc.token.enabled=true',
'xpack.security.authc.token.timeout=15s',
],
},

kbnTestServer: {
...xPackAPITestsConfig.get('kbnTestServer'),
},
};
}
103 changes: 103 additions & 0 deletions x-pack/test/security_api_integration/tests/http_bearer/header.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import expect from '@kbn/expect';
import { adminTestUser } from '@kbn/test';
import { FtrProviderContext } from '../../ftr_provider_context';

export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertestWithoutAuth');
const es = getService('es');

async function createToken() {
const {
body: { access_token: accessToken, authentication },
} = await es.security.getToken({
body: {
grant_type: 'password',
...adminTestUser,
},
});

return {
accessToken,
expectedUser: {
...authentication,
authentication_provider: { name: '__http__', type: 'http' },
authentication_type: 'token',
},
};
}

describe('header', () => {
it('accepts valid access token via authorization Bearer header', async () => {
const { accessToken, expectedUser } = await createToken();

const response = await supertest
.get('/internal/security/me')
.set('kbn-xsrf', 'true')
.set('authorization', `Bearer ${accessToken}`)
.expect(200, expectedUser);

// Make sure we don't automatically create a session
expect(response.headers['set-cookie']).to.be(undefined);
});

it('accepts multiple requests for a single valid access token', async () => {
const { accessToken, expectedUser } = await createToken();

// try it once
await supertest
.get('/internal/security/me')
.set('kbn-xsrf', 'true')
.set('authorization', `Bearer ${accessToken}`)
.expect(200, expectedUser);

// try it again to verity it isn't invalidated after a single request
await supertest
.get('/internal/security/me')
.set('kbn-xsrf', 'true')
.set('authorization', `Bearer ${accessToken}`)
.expect(200, expectedUser);
});

it('rejects invalid access token via authorization Bearer header', async () => {
await supertest
.get('/internal/security/me')
.set('kbn-xsrf', 'true')
.set('authorization', 'Bearer notreal')
.expect(401);
});

it('rejects invalidated access token via authorization Bearer header', async () => {
const { accessToken } = await createToken();
await es.security.invalidateToken({ body: { token: accessToken } });

await supertest
.get('/internal/security/me')
.set('kbn-xsrf', 'true')
.set('authorization', `Bearer ${accessToken}`)
.expect(401);
});

it('rejects expired access token via authorization Bearer header', async function () {
this.timeout(40000);

const { accessToken } = await createToken();

// Access token expiration is set to 15s for API integration tests.
// Let's wait for 20s to make sure token expires.
await new Promise((resolve) => setTimeout(resolve, 20000));

await supertest
.get('/internal/security/me')
.set('kbn-xsrf', 'true')
.set('authorization', `Bearer ${accessToken}`)
.expect(401);
});
});
}
15 changes: 15 additions & 0 deletions x-pack/test/security_api_integration/tests/http_bearer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { FtrProviderContext } from '../../ftr_provider_context';

export default function ({ loadTestFile }: FtrProviderContext) {
describe('security APIs - HTTP Bearer', function () {
this.tags('ciGroup6');
loadTestFile(require.resolve('./header'));
});
}