[Fleet] Configure fleet default output on prem with ES host and CA fingerprint (#120276)

This commit is contained in:
Nicolas Chaulet 2021-12-03 08:33:09 -05:00 committed by GitHub
parent 4436b267b7
commit 265c8dcd43
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 78 additions and 1 deletions

View file

@ -7,6 +7,7 @@
*/
jest.mock('fs/promises');
jest.mock('crypto');
import { constants } from 'fs';
import { loggingSystemMock } from 'src/core/server/mocks';
@ -28,6 +29,16 @@ describe('KibanaConfigWriter', () => {
mockReadFile.mockResolvedValue('');
const mockCrypto = jest.requireMock('crypto');
mockCrypto.X509Certificate = function (cert: string) {
if (cert === 'invalid-cert') {
throw new Error('Invalid certificate');
}
return {
fingerprint256: 'fingerprint256',
};
};
kibanaConfigWriter = new KibanaConfigWriter(
'/some/path/kibana.yml',
'/data',
@ -120,6 +131,7 @@ describe('KibanaConfigWriter', () => {
elasticsearch.hosts: [some-host]
elasticsearch.serviceAccountToken: some-value
elasticsearch.ssl.certificateAuthorities: [/data/ca_1234.crt]
xpack.fleet.outputs: [{id: fleet-default-output, name: default, is_default: true, is_default_monitoring: true, type: elasticsearch, hosts: [some-host], ca_sha256: fingerprint256}]
",
],
@ -186,6 +198,7 @@ describe('KibanaConfigWriter', () => {
elasticsearch.username: username
elasticsearch.password: password
elasticsearch.ssl.certificateAuthorities: [/data/ca_1234.crt]
xpack.fleet.outputs: [{id: fleet-default-output, name: default, is_default: true, is_default_monitoring: true, type: elasticsearch, hosts: [some-host], ca_sha256: fingerprint256}]
",
],
@ -193,6 +206,18 @@ describe('KibanaConfigWriter', () => {
`);
});
it('throws if it cannot parse CA certificate', async () => {
await expect(
kibanaConfigWriter.writeConfig({
caCert: 'invalid-cert',
host: 'some-host',
serviceAccountToken: { name: 'some-token', value: 'some-value' },
})
).rejects.toMatchInlineSnapshot(`[Error: Invalid certificate]`);
expect(mockWriteFile).not.toHaveBeenCalled();
});
it('can successfully write elasticsearch config without CA certificate', async () => {
await expect(
kibanaConfigWriter.writeConfig({
@ -250,6 +275,7 @@ describe('KibanaConfigWriter', () => {
elasticsearch.hosts: [some-host]
elasticsearch.serviceAccountToken: some-value
elasticsearch.ssl.certificateAuthorities: [/data/ca_1234.crt]
xpack.fleet.outputs: [{id: fleet-default-output, name: default, is_default: true, is_default_monitoring: true, type: elasticsearch, hosts: [some-host], ca_sha256: fingerprint256}]
",
],
@ -303,6 +329,7 @@ describe('KibanaConfigWriter', () => {
monitoring.ui.container.elasticsearch.enabled: true
elasticsearch.serviceAccountToken: some-value
elasticsearch.ssl.certificateAuthorities: [/data/ca_1234.crt]
xpack.fleet.outputs: [{id: fleet-default-output, name: default, is_default: true, is_default_monitoring: true, type: elasticsearch, hosts: [some-host], ca_sha256: fingerprint256}]
",
],

View file

@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
import { X509Certificate } from 'crypto';
import { constants } from 'fs';
import fs from 'fs/promises';
import yaml from 'js-yaml';
@ -30,6 +31,16 @@ export type WriteConfigParameters = {
| {}
);
interface FleetOutputConfig {
id: string;
name: string;
is_default: boolean;
is_default_monitoring: boolean;
type: 'elasticsearch';
hosts: string[];
ca_sha256: string;
}
export class KibanaConfigWriter {
constructor(
private readonly configPath: string,
@ -61,7 +72,9 @@ export class KibanaConfigWriter {
*/
public async writeConfig(params: WriteConfigParameters) {
const caPath = path.join(this.dataDirectoryPath, `ca_${Date.now()}.crt`);
const config: Record<string, string | string[]> = { 'elasticsearch.hosts': [params.host] };
const config: Record<string, string | string[] | FleetOutputConfig[]> = {
'elasticsearch.hosts': [params.host],
};
if ('serviceAccountToken' in params && params.serviceAccountToken) {
config['elasticsearch.serviceAccountToken'] = params.serviceAccountToken.value;
} else if ('username' in params && params.username) {
@ -72,6 +85,21 @@ export class KibanaConfigWriter {
config['elasticsearch.ssl.certificateAuthorities'] = [caPath];
}
// If a certificate is passed configure Fleet default output
if (params.caCert) {
try {
config['xpack.fleet.outputs'] = KibanaConfigWriter.getFleetDefaultOutputConfig(
params.caCert,
params.host
);
} catch (err) {
this.logger.error(
`Failed to generate Fleet default output: ${getDetailedErrorMessage(err)}.`
);
throw err;
}
}
// Load and parse existing configuration file to check if it already has values for the config
// entries we want to write.
const existingConfig = await this.loadAndParseKibanaConfig();
@ -152,6 +180,28 @@ export class KibanaConfigWriter {
return { raw: rawConfig, parsed: parsedConfig };
}
/**
* Build config for Fleet outputs
* @param caCert
* @param host
*/
private static getFleetDefaultOutputConfig(caCert: string, host: string): FleetOutputConfig[] {
const cert = new X509Certificate(caCert);
const certFingerprint = cert.fingerprint256;
return [
{
id: 'fleet-default-output',
name: 'default',
is_default: true,
is_default_monitoring: true,
type: 'elasticsearch',
hosts: [host],
ca_sha256: certFingerprint,
},
];
}
/**
* Comments out all non-commented entries in the Kibana configuration file.
* @param rawConfig Content of the Kibana configuration file.