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

[8.0] [Fleet] Configure ca trusted fingerprint for on prem users (#120549) #120984

Merged
merged 1 commit into from
Dec 9, 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
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