mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[kbn/es serverless] Enable security and transport ssl by default (#166023)
## Summary Due to most users needing security plugin enabled by default, this adjusts to that. Which requires transport SSL to be enabled as well. The `--ssl` flag now will enable HTTP SSL only. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com>
This commit is contained in:
parent
127d4dfce7
commit
d7573c77a3
10 changed files with 101 additions and 99 deletions
|
@ -86,6 +86,8 @@ function createServerlessES() {
|
|||
clean: true,
|
||||
kill: true,
|
||||
waitForReady: true,
|
||||
// security is enabled by default, if needed kibana requires serviceAccountToken
|
||||
esArgs: ['xpack.security.enabled=false'],
|
||||
});
|
||||
const client = getServerlessESClient({ port: esPort });
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ export const docker: Command = {
|
|||
--image Full path to image of ES to run, has precedence over tag. [default: ${DOCKER_IMG}]
|
||||
--password Sets password for elastic user [default: ${password}]
|
||||
--port The port to bind to on 127.0.0.1 [default: ${DEFAULT_PORT}]
|
||||
--ssl Sets up SSL and enables security plugin on Elasticsearch
|
||||
--ssl Sets up HTTP and Transport SSL and enables security plugin on Elasticsearch
|
||||
--kill Kill running ES nodes if detected
|
||||
-E Additional key=value settings to pass to Elasticsearch
|
||||
-D Override Docker command
|
||||
|
|
|
@ -26,7 +26,7 @@ export const serverless: Command = {
|
|||
--image Full path of ESS image to run, has precedence over tag. [default: ${SERVERLESS_IMG}]
|
||||
--clean Remove existing file system object store before running
|
||||
--port The port to bind to on 127.0.0.1 [default: ${DEFAULT_PORT}]
|
||||
--ssl Sets up SSL and enables security plugin on Elasticsearch
|
||||
--ssl Enable HTTP SSL on Elasticsearch
|
||||
--kill Kill running ESS nodes if detected
|
||||
--background Start ESS without attaching to the first node's logs
|
||||
-E Additional key=value settings to pass to Elasticsearch
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
"compatibility": "8.11.0"
|
||||
},
|
||||
"string_secrets": {
|
||||
"xpack.security.http.ssl.keystore.secure_password": "storepass",
|
||||
"xpack.security.transport.ssl.keystore.secure_password": "storepass",
|
||||
"xpack.security.authc.realms.jwt.jwt1.client_authentication.shared_secret": "my_super_secret"
|
||||
}
|
||||
|
|
11
packages/kbn-es/src/ess_resources/secrets_ssl.json
Normal file
11
packages/kbn-es/src/ess_resources/secrets_ssl.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"metadata": {
|
||||
"version": "1",
|
||||
"compatibility": "8.11.0"
|
||||
},
|
||||
"string_secrets": {
|
||||
"xpack.security.http.ssl.keystore.secure_password": "storepass",
|
||||
"xpack.security.transport.ssl.keystore.secure_password": "storepass",
|
||||
"xpack.security.authc.realms.jwt.jwt1.client_authentication.shared_secret": "my_super_secret"
|
||||
}
|
||||
}
|
|
@ -35,6 +35,8 @@ export const ESS_ROLE_MAPPING_PATH = resolve(__dirname, './ess_resources/role_ma
|
|||
|
||||
export const ESS_SECRETS_PATH = resolve(__dirname, './ess_resources/secrets.json');
|
||||
|
||||
export const ESS_SECRETS_SSL_PATH = resolve(__dirname, './ess_resources/secrets_ssl.json');
|
||||
|
||||
export const ESS_JWKS_PATH = resolve(__dirname, './ess_resources/jwks.json');
|
||||
|
||||
export const ESS_RESOURCES_PATHS = [
|
||||
|
|
|
@ -9,6 +9,7 @@ import mockFs from 'mock-fs';
|
|||
|
||||
import { existsSync } from 'fs';
|
||||
import { stat } from 'fs/promises';
|
||||
import { basename } from 'path';
|
||||
|
||||
import {
|
||||
DOCKER_IMG,
|
||||
|
@ -27,10 +28,11 @@ import {
|
|||
stopServerlessCluster,
|
||||
teardownServerlessClusterSync,
|
||||
verifyDockerInstalled,
|
||||
getESp12Volume,
|
||||
} from './docker';
|
||||
import { ToolingLog, ToolingLogCollectingWriter } from '@kbn/tooling-log';
|
||||
import { ES_P12_PATH } from '@kbn/dev-utils';
|
||||
import { ESS_RESOURCES_PATHS } from '../paths';
|
||||
import { ESS_CONFIG_PATH, ESS_RESOURCES_PATHS, ESS_SECRETS_PATH, ESS_JWKS_PATH } from '../paths';
|
||||
|
||||
jest.mock('execa');
|
||||
const execa = jest.requireMock('execa');
|
||||
|
@ -68,9 +70,23 @@ afterEach(() => {
|
|||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
const essResources = ESS_RESOURCES_PATHS.reduce<string[]>((acc, path) => {
|
||||
acc.push(`${path}:${ESS_CONFIG_PATH}${basename(path)}`);
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const volumeCmdTest = async (volumeCmd: string[]) => {
|
||||
expect(volumeCmd).toHaveLength(2);
|
||||
expect(volumeCmd).toEqual(expect.arrayContaining(['--volume', `${baseEsPath}:/objectstore:z`]));
|
||||
expect(volumeCmd).toHaveLength(20);
|
||||
expect(volumeCmd).toEqual(
|
||||
expect.arrayContaining([
|
||||
...getESp12Volume(),
|
||||
...essResources,
|
||||
`${baseEsPath}:/objectstore:z`,
|
||||
`${ESS_SECRETS_PATH}:${ESS_CONFIG_PATH}secrets/secrets.json:z`,
|
||||
`${ESS_JWKS_PATH}:${ESS_CONFIG_PATH}secrets/jwks.json:z`,
|
||||
])
|
||||
);
|
||||
|
||||
// extract only permission from mode
|
||||
// eslint-disable-next-line no-bitwise
|
||||
|
@ -341,13 +357,10 @@ describe('resolveEsArgs()', () => {
|
|||
`);
|
||||
});
|
||||
|
||||
test('should add SSL args and enable security when SSL is passed', () => {
|
||||
const esArgs = resolveEsArgs([...defaultEsArgs, ['xpack.security.enabled', 'false']], {
|
||||
ssl: true,
|
||||
});
|
||||
test('should add SSL args when SSL is passed', () => {
|
||||
const esArgs = resolveEsArgs(defaultEsArgs, { ssl: true });
|
||||
|
||||
expect(esArgs).toHaveLength(20);
|
||||
expect(esArgs).not.toEqual(expect.arrayContaining(['xpack.security.enabled=false']));
|
||||
expect(esArgs).toHaveLength(10);
|
||||
expect(esArgs).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"--env",
|
||||
|
@ -355,21 +368,11 @@ describe('resolveEsArgs()', () => {
|
|||
"--env",
|
||||
"qux=zip",
|
||||
"--env",
|
||||
"xpack.security.enabled=true",
|
||||
"--env",
|
||||
"xpack.security.http.ssl.enabled=true",
|
||||
"--env",
|
||||
"xpack.security.http.ssl.keystore.path=/usr/share/elasticsearch/config/certs/elasticsearch.p12",
|
||||
"--env",
|
||||
"xpack.security.http.ssl.verification_mode=certificate",
|
||||
"--env",
|
||||
"xpack.security.transport.ssl.enabled=true",
|
||||
"--env",
|
||||
"xpack.security.transport.ssl.keystore.path=/usr/share/elasticsearch/config/certs/elasticsearch.p12",
|
||||
"--env",
|
||||
"xpack.security.transport.ssl.verification_mode=certificate",
|
||||
"--env",
|
||||
"xpack.security.operator_privileges.enabled=true",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
|
|
@ -29,6 +29,7 @@ import {
|
|||
ESS_JWKS_PATH,
|
||||
ESS_CONFIG_PATH,
|
||||
ESS_FILES_PATH,
|
||||
ESS_SECRETS_SSL_PATH,
|
||||
} from '../paths';
|
||||
import {
|
||||
ELASTIC_SERVERLESS_SUPERUSER,
|
||||
|
@ -153,44 +154,49 @@ const DEFAULT_SERVERLESS_ESARGS: Array<[string, string]> = [
|
|||
|
||||
['xpack.ml.enabled', 'true'],
|
||||
|
||||
['xpack.security.enabled', 'false'],
|
||||
];
|
||||
|
||||
const DEFAULT_SSL_ESARGS: Array<[string, string]> = [
|
||||
['xpack.security.enabled', 'true'],
|
||||
|
||||
['xpack.security.http.ssl.enabled', 'true'],
|
||||
// JWT realm settings are to closer emulate a real ES serverless env
|
||||
['xpack.security.authc.realms.jwt.jwt1.allowed_audiences', 'elasticsearch'],
|
||||
|
||||
['xpack.security.http.ssl.keystore.path', `${ESS_CONFIG_PATH}certs/elasticsearch.p12`],
|
||||
['xpack.security.authc.realms.jwt.jwt1.allowed_issuer', 'https://kibana.elastic.co/jwt/'],
|
||||
|
||||
['xpack.security.http.ssl.verification_mode', 'certificate'],
|
||||
['xpack.security.authc.realms.jwt.jwt1.claims.principal', 'sub'],
|
||||
|
||||
['xpack.security.authc.realms.jwt.jwt1.client_authentication.type', 'shared_secret'],
|
||||
|
||||
['xpack.security.authc.realms.jwt.jwt1.order', '-98'],
|
||||
|
||||
['xpack.security.authc.realms.jwt.jwt1.pkc_jwkset_path', `${ESS_CONFIG_PATH}secrets/jwks.json`],
|
||||
|
||||
['xpack.security.operator_privileges.enabled', 'true'],
|
||||
|
||||
['xpack.security.transport.ssl.enabled', 'true'],
|
||||
|
||||
['xpack.security.transport.ssl.keystore.path', `${ESS_CONFIG_PATH}certs/elasticsearch.p12`],
|
||||
|
||||
['xpack.security.transport.ssl.verification_mode', 'certificate'],
|
||||
|
||||
['xpack.security.operator_privileges.enabled', 'true'],
|
||||
];
|
||||
|
||||
const SERVERLESS_SSL_ESARGS: Array<[string, string]> = [
|
||||
['xpack.security.authc.realms.jwt.jwt1.client_authentication.type', 'shared_secret'],
|
||||
const DEFAULT_SSL_ESARGS: Array<[string, string]> = [
|
||||
['xpack.security.http.ssl.enabled', 'true'],
|
||||
|
||||
['xpack.security.authc.realms.jwt.jwt1.order', '-98'],
|
||||
['xpack.security.http.ssl.keystore.path', `${ESS_CONFIG_PATH}certs/elasticsearch.p12`],
|
||||
|
||||
['xpack.security.authc.realms.jwt.jwt1.allowed_issuer', 'https://kibana.elastic.co/jwt/'],
|
||||
|
||||
['xpack.security.authc.realms.jwt.jwt1.allowed_audiences', 'elasticsearch'],
|
||||
|
||||
['xpack.security.authc.realms.jwt.jwt1.pkc_jwkset_path', `${ESS_CONFIG_PATH}secrets/jwks.json`],
|
||||
|
||||
['xpack.security.authc.realms.jwt.jwt1.claims.principal', 'sub'],
|
||||
['xpack.security.http.ssl.verification_mode', 'certificate'],
|
||||
];
|
||||
|
||||
const DOCKER_SSL_ESARGS: Array<[string, string]> = [
|
||||
['xpack.security.enabled', 'true'],
|
||||
|
||||
['xpack.security.http.ssl.keystore.password', ES_P12_PASSWORD],
|
||||
|
||||
['xpack.security.transport.ssl.enabled', 'true'],
|
||||
|
||||
['xpack.security.transport.ssl.keystore.path', `${ESS_CONFIG_PATH}certs/elasticsearch.p12`],
|
||||
|
||||
['xpack.security.transport.ssl.verification_mode', 'certificate'],
|
||||
|
||||
['xpack.security.transport.ssl.keystore.password', ES_P12_PASSWORD],
|
||||
];
|
||||
|
||||
|
@ -418,13 +424,6 @@ export function resolveEsArgs(
|
|||
args.forEach((arg) => {
|
||||
const [key, ...value] = arg.split('=');
|
||||
|
||||
// Guide the user to use SSL flag instead of manual setup
|
||||
if (key === 'xpack.security.enabled' && value?.[0] === 'true') {
|
||||
throw createCliError(
|
||||
'Use the --ssl flag to automatically enable and set up the security plugin.'
|
||||
);
|
||||
}
|
||||
|
||||
esArgs.set(key.trim(), value.join('=').trim());
|
||||
});
|
||||
}
|
||||
|
@ -436,7 +435,7 @@ export function resolveEsArgs(
|
|||
return Array.from(esArgs).flatMap((e) => ['--env', e.join('=')]);
|
||||
}
|
||||
|
||||
function getESp12Volume() {
|
||||
export function getESp12Volume() {
|
||||
return ['--volume', `${ES_P12_PATH}:${ESS_CONFIG_PATH}certs/elasticsearch.p12`];
|
||||
}
|
||||
|
||||
|
@ -492,24 +491,22 @@ export async function setupServerlessVolumes(log: ToolingLog, options: Serverles
|
|||
volumeCmds.push(...fileCmds);
|
||||
}
|
||||
|
||||
if (ssl) {
|
||||
const essResources = ESS_RESOURCES_PATHS.reduce<string[]>((acc, path) => {
|
||||
acc.push('--volume', `${path}:${ESS_CONFIG_PATH}${basename(path)}`);
|
||||
const essResources = ESS_RESOURCES_PATHS.reduce<string[]>((acc, path) => {
|
||||
acc.push('--volume', `${path}:${ESS_CONFIG_PATH}${basename(path)}`);
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
volumeCmds.push(
|
||||
...getESp12Volume(),
|
||||
...essResources,
|
||||
volumeCmds.push(
|
||||
...getESp12Volume(),
|
||||
...essResources,
|
||||
|
||||
'--volume',
|
||||
`${ESS_SECRETS_PATH}:${ESS_CONFIG_PATH}secrets/secrets.json:z`,
|
||||
'--volume',
|
||||
`${ssl ? ESS_SECRETS_SSL_PATH : ESS_SECRETS_PATH}:${ESS_CONFIG_PATH}secrets/secrets.json:z`,
|
||||
|
||||
'--volume',
|
||||
`${ESS_JWKS_PATH}:${ESS_CONFIG_PATH}secrets/jwks.json:z`
|
||||
);
|
||||
}
|
||||
'--volume',
|
||||
`${ESS_JWKS_PATH}:${ESS_CONFIG_PATH}secrets/jwks.json:z`
|
||||
);
|
||||
|
||||
return volumeCmds;
|
||||
}
|
||||
|
@ -577,13 +574,7 @@ export async function runServerlessCluster(log: ToolingLog, options: ServerlessO
|
|||
...node,
|
||||
image,
|
||||
params: node.params.concat(
|
||||
resolveEsArgs(
|
||||
DEFAULT_SERVERLESS_ESARGS.concat(
|
||||
node.esArgs ?? [],
|
||||
options.ssl ? SERVERLESS_SSL_ESARGS : []
|
||||
),
|
||||
options
|
||||
),
|
||||
resolveEsArgs(DEFAULT_SERVERLESS_ESARGS.concat(node.esArgs ?? []), options),
|
||||
i === 0 ? portCmd : [],
|
||||
volumeCmd
|
||||
),
|
||||
|
@ -593,20 +584,15 @@ export async function runServerlessCluster(log: ToolingLog, options: ServerlessO
|
|||
);
|
||||
|
||||
log.success(`Serverless ES cluster running.
|
||||
Stop the cluster: ${chalk.bold(`docker container stop ${nodeNames.join(' ')}`)}
|
||||
Login with username ${chalk.bold.cyan(ELASTIC_SERVERLESS_SUPERUSER)} or ${chalk.bold.cyan(
|
||||
SYSTEM_INDICES_SUPERUSER
|
||||
)} and password ${chalk.bold.magenta(ELASTIC_SERVERLESS_SUPERUSER_PASSWORD)}
|
||||
Stop the cluster: ${chalk.bold(`docker container stop ${nodeNames.join(' ')}`)}
|
||||
`);
|
||||
|
||||
if (options.ssl) {
|
||||
log.success(`SSL and Security have been enabled for ES.
|
||||
Login through your browser with username ${chalk.bold.cyan(
|
||||
ELASTIC_SERVERLESS_SUPERUSER
|
||||
)} or ${chalk.bold.cyan(SYSTEM_INDICES_SUPERUSER)} and password ${chalk.bold.magenta(
|
||||
ELASTIC_SERVERLESS_SUPERUSER_PASSWORD
|
||||
)}.
|
||||
`);
|
||||
|
||||
log.warning(`Kibana should be started with the SSL flag so that it can authenticate with ES.
|
||||
See packages/kbn-es/src/ess_resources/README.md for additional information on authentication.
|
||||
log.warning(`SSL has been enabled for ES. Kibana should be started with the SSL flag so that it can authenticate with ES.
|
||||
See packages/kbn-es/src/ess_resources/README.md for additional information on authentication.
|
||||
`);
|
||||
}
|
||||
|
||||
|
@ -620,9 +606,9 @@ export async function runServerlessCluster(log: ToolingLog, options: ServerlessO
|
|||
|
||||
const client = getESClient({
|
||||
node: esNodeUrl,
|
||||
auth: { bearer: kibanaDevServiceAccount.token },
|
||||
...(options.ssl
|
||||
? {
|
||||
auth: { bearer: kibanaDevServiceAccount.token },
|
||||
tls: {
|
||||
ca: [fs.readFileSync(CA_CERT_PATH)],
|
||||
// NOTE: Even though we've added ca into the tls options, we are using 127.0.0.1 instead of localhost
|
||||
|
|
|
@ -234,9 +234,9 @@ export function createTestEsCluster<
|
|||
port,
|
||||
clean: true,
|
||||
teardown: true,
|
||||
ssl: true,
|
||||
background: true,
|
||||
files,
|
||||
ssl,
|
||||
kill: true, // likely don't need this but avoids any issues where the ESS cluster wasn't cleaned up
|
||||
waitForReady: true,
|
||||
});
|
||||
|
|
|
@ -52,31 +52,30 @@ export default async () => {
|
|||
files: [idpPath, jwksPath],
|
||||
serverArgs: [
|
||||
'xpack.security.authc.realms.file.file1.order=-100',
|
||||
|
||||
'xpack.security.authc.realms.jwt.jwt1.order=-98',
|
||||
`xpack.security.authc.realms.jwt.jwt1.token_type=access_token`,
|
||||
'xpack.security.authc.realms.jwt.jwt1.client_authentication.type=shared_secret',
|
||||
`xpack.security.authc.realms.jwt.jwt1.allowed_issuer=https://kibana.elastic.co/jwt/`,
|
||||
`xpack.security.authc.realms.jwt.jwt1.allowed_subjects=elastic-agent`,
|
||||
'xpack.security.authc.realms.jwt.jwt1.allowed_audiences=elasticsearch',
|
||||
`xpack.security.authc.realms.jwt.jwt1.allowed_signature_algorithms=[RS256]`,
|
||||
`xpack.security.authc.realms.jwt.jwt1.claims.principal=sub`,
|
||||
`xpack.security.authc.realms.jwt.jwt1.pkc_jwkset_path=${getDockerFileMountPath(jwksPath)}`,
|
||||
|
||||
`xpack.security.authc.realms.native.native1.enabled=false`,
|
||||
`xpack.security.authc.realms.native.native1.order=-97`,
|
||||
'xpack.security.authc.token.enabled=true',
|
||||
|
||||
'xpack.security.authc.realms.jwt.jwt1.allowed_audiences=elasticsearch',
|
||||
`xpack.security.authc.realms.jwt.jwt1.allowed_issuer=https://kibana.elastic.co/jwt/`,
|
||||
`xpack.security.authc.realms.jwt.jwt1.allowed_signature_algorithms=[RS256]`,
|
||||
`xpack.security.authc.realms.jwt.jwt1.allowed_subjects=elastic-agent`,
|
||||
`xpack.security.authc.realms.jwt.jwt1.claims.principal=sub`,
|
||||
'xpack.security.authc.realms.jwt.jwt1.client_authentication.type=shared_secret',
|
||||
'xpack.security.authc.realms.jwt.jwt1.order=-98',
|
||||
`xpack.security.authc.realms.jwt.jwt1.pkc_jwkset_path=${getDockerFileMountPath(jwksPath)}`,
|
||||
`xpack.security.authc.realms.jwt.jwt1.token_type=access_token`,
|
||||
|
||||
'xpack.security.authc.realms.saml.cloud-saml-kibana.attributes.principal=urn:oid:0.0.7',
|
||||
'xpack.security.authc.realms.saml.cloud-saml-kibana.idp.entity_id=http://www.elastic.co/saml1',
|
||||
'xpack.security.authc.realms.saml.cloud-saml-kibana.order=101',
|
||||
`xpack.security.authc.realms.saml.cloud-saml-kibana.idp.metadata.path=${getDockerFileMountPath(
|
||||
idpPath
|
||||
)}`,
|
||||
'xpack.security.authc.realms.saml.cloud-saml-kibana.idp.entity_id=http://www.elastic.co/saml1',
|
||||
`xpack.security.authc.realms.saml.cloud-saml-kibana.sp.acs=http://localhost:${servers.kibana.port}/api/security/saml/callback`,
|
||||
`xpack.security.authc.realms.saml.cloud-saml-kibana.sp.entity_id=http://localhost:${servers.kibana.port}`,
|
||||
`xpack.security.authc.realms.saml.cloud-saml-kibana.sp.logout=http://localhost:${servers.kibana.port}/logout`,
|
||||
`xpack.security.authc.realms.saml.cloud-saml-kibana.sp.acs=http://localhost:${servers.kibana.port}/api/security/saml/callback`,
|
||||
'xpack.security.authc.realms.saml.cloud-saml-kibana.attributes.principal=urn:oid:0.0.7',
|
||||
],
|
||||
ssl: true, // not needed as for serverless ssl is always on but added it anyway
|
||||
ssl: true, // SSL is required for SAML realm
|
||||
},
|
||||
|
||||
kbnTestServer: {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue