Skip to content

Commit

Permalink
[Fleet] Configure ca trusted fingerprint for on prem users (#120549)
Browse files Browse the repository at this point in the history
  • Loading branch information
nchaulet committed Dec 9, 2021
1 parent cc65713 commit e46e082
Show file tree
Hide file tree
Showing 15 changed files with 148 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ describe('KibanaConfigWriter', () => {
throw new Error('Invalid certificate');
}
return {
fingerprint256: 'fingerprint256',
fingerprint256:
'D4:86:CE:00:AC:71:E4:1D:2B:70:D0:87:A5:55:FA:5D:D1:93:6C:DB:45:80:79:53:7B:A3:AC:13:3E:48:34:D6',
};
};

Expand Down Expand Up @@ -131,7 +132,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}]
xpack.fleet.outputs: [{id: fleet-default-output, name: default, is_default: true, is_default_monitoring: true, type: elasticsearch, hosts: [some-host], ca_trusted_fingerprint: d486ce00ac71e41d2b70d087a555fa5dd1936cdb458079537ba3ac133e4834d6}]
",
],
Expand Down Expand Up @@ -198,7 +199,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}]
xpack.fleet.outputs: [{id: fleet-default-output, name: default, is_default: true, is_default_monitoring: true, type: elasticsearch, hosts: [some-host], ca_trusted_fingerprint: d486ce00ac71e41d2b70d087a555fa5dd1936cdb458079537ba3ac133e4834d6}]
",
],
Expand Down Expand Up @@ -275,7 +276,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}]
xpack.fleet.outputs: [{id: fleet-default-output, name: default, is_default: true, is_default_monitoring: true, type: elasticsearch, hosts: [some-host], ca_trusted_fingerprint: d486ce00ac71e41d2b70d087a555fa5dd1936cdb458079537ba3ac133e4834d6}]
",
],
Expand Down Expand Up @@ -329,7 +330,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}]
xpack.fleet.outputs: [{id: fleet-default-output, name: default, is_default: true, is_default_monitoring: true, type: elasticsearch, hosts: [some-host], ca_trusted_fingerprint: d486ce00ac71e41d2b70d087a555fa5dd1936cdb458079537ba3ac133e4834d6}]
",
],
Expand Down
7 changes: 4 additions & 3 deletions src/plugins/interactive_setup/server/kibana_config_writer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ interface FleetOutputConfig {
is_default_monitoring: boolean;
type: 'elasticsearch';
hosts: string[];
ca_sha256: string;
ca_trusted_fingerprint: string;
}

export class KibanaConfigWriter {
Expand Down Expand Up @@ -187,7 +187,8 @@ export class KibanaConfigWriter {
*/
private static getFleetDefaultOutputConfig(caCert: string, host: string): FleetOutputConfig[] {
const cert = new X509Certificate(caCert);
const certFingerprint = cert.fingerprint256;
// fingerprint256 is a ":" separated uppercase hexadecimal string
const certFingerprint = cert.fingerprint256.split(':').join('').toLowerCase();

return [
{
Expand All @@ -197,7 +198,7 @@ export class KibanaConfigWriter {
is_default_monitoring: true,
type: 'elasticsearch',
hosts: [host],
ca_sha256: certFingerprint,
ca_trusted_fingerprint: certFingerprint,
},
];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ properties:
type: string
ca_sha256:
type: string
ca_trusted_fingerprint:
type: string
api_key:
type: string
config:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ put:
type: string
ca_sha256:
type: string
ca_trusted_fingerprint:
type: string
config_yaml:
type: string
required:
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/common/types/models/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface NewOutput {
type: ValueOf<OutputType>;
hosts?: string[];
ca_sha256?: string;
ca_trusted_fingerprint?: string;
api_key?: string;
config_yaml?: string;
is_preconfigured?: boolean;
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/fleet/common/types/rest_spec/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface PutOutputRequest {
name?: string;
hosts?: string[];
ca_sha256?: string;
ca_trusted_fingerprint?: string;
config_yaml?: string;
is_default?: boolean;
is_default_monitoring?: boolean;
Expand All @@ -45,6 +46,7 @@ export interface PostOutputRequest {
name: string;
hosts?: string[];
ca_sha256?: string;
ca_trusted_fingerprint?: string;
is_default?: boolean;
is_default_monitoring?: boolean;
config_yaml?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ export const useFleetServerInstructions = (policyId?: string) => {
const { data: settings, resendRequest: refreshSettings } = useGetSettings();
const fleetServerHost = settings?.item.fleet_server_hosts?.[0];
const esHost = output?.hosts?.[0];
const sslCATrustedFingerprint: string | undefined = output?.ca_trusted_fingerprint;

const installCommand = useMemo((): string => {
if (!serviceToken || !esHost) {
Expand All @@ -257,9 +258,18 @@ export const useFleetServerInstructions = (policyId?: string) => {
serviceToken,
policyId,
fleetServerHost,
deploymentMode === 'production'
deploymentMode === 'production',
sslCATrustedFingerprint
);
}, [serviceToken, esHost, platform, policyId, fleetServerHost, deploymentMode]);
}, [
serviceToken,
esHost,
platform,
policyId,
fleetServerHost,
deploymentMode,
sslCATrustedFingerprint,
]);

const getServiceToken = useCallback(async () => {
setIsLoadingServiceToken(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('getInstallCommandForPlatform', () => {
);

expect(res).toMatchInlineSnapshot(`
"sudo ./elastic-agent install \\\\
"sudo ./elastic-agent install \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1"
`);
Expand All @@ -31,7 +31,7 @@ describe('getInstallCommandForPlatform', () => {
);

expect(res).toMatchInlineSnapshot(`
".\\\\elastic-agent.exe install \`
".\\\\elastic-agent.exe install \`
--fleet-server-es=http://elasticsearch:9200 \`
--fleet-server-service-token=service-token-1"
`);
Expand All @@ -45,11 +45,30 @@ describe('getInstallCommandForPlatform', () => {
);

expect(res).toMatchInlineSnapshot(`
"sudo elastic-agent enroll \\\\
"sudo elastic-agent enroll \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1"
`);
});

it('should return the correct command sslCATrustedFingerprint option is passed', () => {
const res = getInstallCommandForPlatform(
'linux-mac',
'http://elasticsearch:9200',
'service-token-1',
undefined,
undefined,
false,
'fingerprint123456'
);

expect(res).toMatchInlineSnapshot(`
"sudo ./elastic-agent install \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1 \\\\
--fleet-server-es-ca-trusted-fingerprint=fingerprint123456"
`);
});
});

describe('with policy id', () => {
Expand All @@ -62,7 +81,7 @@ describe('getInstallCommandForPlatform', () => {
);

expect(res).toMatchInlineSnapshot(`
"sudo ./elastic-agent install \\\\
"sudo ./elastic-agent install \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1 \\\\
--fleet-server-policy=policy-1"
Expand All @@ -78,7 +97,7 @@ describe('getInstallCommandForPlatform', () => {
);

expect(res).toMatchInlineSnapshot(`
".\\\\elastic-agent.exe install \`
".\\\\elastic-agent.exe install \`
--fleet-server-es=http://elasticsearch:9200 \`
--fleet-server-service-token=service-token-1 \`
--fleet-server-policy=policy-1"
Expand All @@ -94,7 +113,7 @@ describe('getInstallCommandForPlatform', () => {
);

expect(res).toMatchInlineSnapshot(`
"sudo elastic-agent enroll \\\\
"sudo elastic-agent enroll \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1 \\\\
--fleet-server-policy=policy-1"
Expand Down Expand Up @@ -178,7 +197,7 @@ describe('getInstallCommandForPlatform', () => {
);

expect(res).toMatchInlineSnapshot(`
"sudo elastic-agent enroll \\\\
"sudo elastic-agent enroll \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1"
`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,37 +13,49 @@ export function getInstallCommandForPlatform(
serviceToken: string,
policyId?: string,
fleetServerHost?: string,
isProductionDeployment?: boolean
isProductionDeployment?: boolean,
sslCATrustedFingerprint?: string
) {
let commandArguments = '';
const newLineSeparator = platform === 'windows' ? '`' : '\\';
const commandArguments = [];
const newLineSeparator = platform === 'windows' ? '`\n' : '\\\n';

if (isProductionDeployment && fleetServerHost) {
commandArguments += `--url=${fleetServerHost} ${newLineSeparator}\n`;
} else {
commandArguments += ` ${newLineSeparator}\n`;
commandArguments.push(['url', fleetServerHost]);
}

commandArguments += ` --fleet-server-es=${esHost}`;
commandArguments += ` ${newLineSeparator}\n --fleet-server-service-token=${serviceToken}`;
commandArguments.push(['fleet-server-es', esHost]);
commandArguments.push(['fleet-server-service-token', serviceToken]);
if (policyId) {
commandArguments += ` ${newLineSeparator}\n --fleet-server-policy=${policyId}`;
commandArguments.push(['fleet-server-policy', policyId]);
}

if (sslCATrustedFingerprint) {
commandArguments.push(['fleet-server-es-ca-trusted-fingerprint', sslCATrustedFingerprint]);
}

if (isProductionDeployment) {
commandArguments += ` ${newLineSeparator}\n --certificate-authorities=<PATH_TO_CA>`;
commandArguments += ` ${newLineSeparator}\n --fleet-server-es-ca=<PATH_TO_ES_CERT>`;
commandArguments += ` ${newLineSeparator}\n --fleet-server-cert=<PATH_TO_FLEET_SERVER_CERT>`;
commandArguments += ` ${newLineSeparator}\n --fleet-server-cert-key=<PATH_TO_FLEET_SERVER_CERT_KEY>`;
commandArguments.push(['certificate-authorities', '<PATH_TO_CA>']);
if (!sslCATrustedFingerprint) {
commandArguments.push(['fleet-server-es-ca', '<PATH_TO_ES_CERT>']);
}
commandArguments.push(['fleet-server-cert', '<PATH_TO_FLEET_SERVER_CERT>']);
commandArguments.push(['fleet-server-cert-key', '<PATH_TO_FLEET_SERVER_CERT_KEY>']);
}

const commandArgumentsStr = commandArguments.reduce((acc, [key, val]) => {
if (acc === '' && key === 'url') {
return `--${key}=${val}`;
}
return (acc += ` ${newLineSeparator} --${key}=${val}`);
}, '');

switch (platform) {
case 'linux-mac':
return `sudo ./elastic-agent install ${commandArguments}`;
return `sudo ./elastic-agent install ${commandArgumentsStr}`;
case 'windows':
return `.\\elastic-agent.exe install ${commandArguments}`;
return `.\\elastic-agent.exe install ${commandArgumentsStr}`;
case 'rpm-deb':
return `sudo elastic-agent enroll ${commandArguments}`;
return `sudo elastic-agent enroll ${commandArgumentsStr}`;
default:
return '';
}
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/server/saved_objects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ const getSavedObjectTypes = (
is_default_monitoring: { type: 'boolean' },
hosts: { type: 'keyword' },
ca_sha256: { type: 'keyword', index: false },
ca_trusted_fingerprint: { type: 'keyword', index: false },
config: { type: 'flattened' },
config_yaml: { type: 'text' },
is_preconfigured: { type: 'boolean', index: false },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { AgentPolicy, Output } from '../../types';
import { agentPolicyService } from '../agent_policy';
import { agentPolicyUpdateEventHandler } from '../agent_policy_update';

import { getFullAgentPolicy } from './full_agent_policy';
import { getFullAgentPolicy, transformOutputToFullPolicyOutput } from './full_agent_policy';
import { getMonitoringPermissions } from './monitoring_permissions';

const mockedGetElasticAgentMonitoringPermissions = getMonitoringPermissions as jest.Mock<
Expand Down Expand Up @@ -305,3 +305,58 @@ describe('getFullAgentPolicy', () => {
expect(agentPolicy?.outputs.default).toBeDefined();
});
});

describe('transformOutputToFullPolicyOutput', () => {
it('should works with only required field on a output', () => {
const policyOutput = transformOutputToFullPolicyOutput({
id: 'id123',
hosts: ['http://host.fr'],
is_default: false,
is_default_monitoring: false,
name: 'test output',
type: 'elasticsearch',
api_key: 'apikey123',
});

expect(policyOutput).toMatchInlineSnapshot(`
Object {
"api_key": "apikey123",
"ca_sha256": undefined,
"hosts": Array [
"http://host.fr",
],
"type": "elasticsearch",
}
`);
});
it('should support ca_trusted_fingerprint field on a output', () => {
const policyOutput = transformOutputToFullPolicyOutput({
id: 'id123',
hosts: ['http://host.fr'],
is_default: false,
is_default_monitoring: false,
name: 'test output',
type: 'elasticsearch',
api_key: 'apikey123',
ca_trusted_fingerprint: 'fingerprint123',
config_yaml: `
test: 1234
ssl.test: 123
`,
});

expect(policyOutput).toMatchInlineSnapshot(`
Object {
"api_key": "apikey123",
"ca_sha256": undefined,
"hosts": Array [
"http://host.fr",
],
"ssl.ca_trusted_fingerprint": "fingerprint123",
"ssl.test": 123,
"test": 1234,
"type": "elasticsearch",
}
`);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -168,19 +168,20 @@ export async function getFullAgentPolicy(
return fullAgentPolicy;
}

function transformOutputToFullPolicyOutput(
export function transformOutputToFullPolicyOutput(
output: Output,
standalone = false
): FullAgentPolicyOutput {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { config_yaml, type, hosts, ca_sha256, api_key } = output;
const { config_yaml, type, hosts, ca_sha256, ca_trusted_fingerprint, api_key } = output;
const configJs = config_yaml ? safeLoad(config_yaml) : {};
const newOutput: FullAgentPolicyOutput = {
...configJs,
type,
hosts,
ca_sha256,
api_key,
...configJs,
...(ca_trusted_fingerprint ? { 'ssl.ca_trusted_fingerprint': ca_trusted_fingerprint } : {}),
};

if (standalone) {
Expand Down
Loading

0 comments on commit e46e082

Please sign in to comment.