mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Revert "[Fleet] Support preconfigured output secrets (#170259)"
This reverts commit 1f7a527494
.
This commit is contained in:
parent
e7c793e0d7
commit
78f7b80e33
7 changed files with 61 additions and 511 deletions
|
@ -865,7 +865,6 @@
|
|||
"ansi-regex": "^5.0.1",
|
||||
"antlr4ts": "^0.5.0-alpha.3",
|
||||
"archiver": "^5.3.1",
|
||||
"argon2": "0.31.1",
|
||||
"async": "^3.2.3",
|
||||
"aws4": "^1.12.0",
|
||||
"axios": "^1.6.0",
|
||||
|
|
|
@ -23,12 +23,7 @@ export type KafkaPartitionType = typeof kafkaPartitionType;
|
|||
export type KafkaTopicWhenType = typeof kafkaTopicWhenType;
|
||||
export type KafkaAcknowledgeReliabilityLevel = typeof kafkaAcknowledgeReliabilityLevel;
|
||||
export type KafkaVerificationMode = typeof kafkaVerificationModes;
|
||||
export type OutputSecret =
|
||||
| string
|
||||
| {
|
||||
id: string;
|
||||
hash?: string;
|
||||
};
|
||||
|
||||
interface NewBaseOutput {
|
||||
is_default: boolean;
|
||||
is_default_monitoring: boolean;
|
||||
|
@ -50,7 +45,11 @@ interface NewBaseOutput {
|
|||
allow_edit?: string[];
|
||||
secrets?: {
|
||||
ssl?: {
|
||||
key?: OutputSecret;
|
||||
key?:
|
||||
| string
|
||||
| {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -132,9 +131,17 @@ export interface KafkaOutput extends NewBaseOutput {
|
|||
broker_timeout?: number;
|
||||
required_acks?: ValueOf<KafkaAcknowledgeReliabilityLevel>;
|
||||
secrets?: {
|
||||
password?: OutputSecret;
|
||||
password?:
|
||||
| string
|
||||
| {
|
||||
id: string;
|
||||
};
|
||||
ssl?: {
|
||||
key?: OutputSecret;
|
||||
key?:
|
||||
| string
|
||||
| {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -419,12 +419,7 @@ class OutputService {
|
|||
soClient: SavedObjectsClientContract,
|
||||
esClient: ElasticsearchClient,
|
||||
output: NewOutput,
|
||||
options?: {
|
||||
id?: string;
|
||||
fromPreconfiguration?: boolean;
|
||||
overwrite?: boolean;
|
||||
secretHashes?: Record<string, any>;
|
||||
}
|
||||
options?: { id?: string; fromPreconfiguration?: boolean; overwrite?: boolean }
|
||||
): Promise<Output> {
|
||||
const data: OutputSOAttributes = { ...omit(output, ['ssl', 'secrets']) };
|
||||
if (output.type === outputType.RemoteElasticsearch) {
|
||||
|
@ -560,7 +555,6 @@ class OutputService {
|
|||
const { output: outputWithSecrets } = await extractAndWriteOutputSecrets({
|
||||
output,
|
||||
esClient,
|
||||
secretHashes: output.is_preconfigured ? options?.secretHashes : undefined,
|
||||
});
|
||||
|
||||
if (outputWithSecrets.secrets) data.secrets = outputWithSecrets.secrets;
|
||||
|
@ -722,10 +716,7 @@ class OutputService {
|
|||
esClient: ElasticsearchClient,
|
||||
id: string,
|
||||
data: Partial<Output>,
|
||||
{
|
||||
fromPreconfiguration = false,
|
||||
secretHashes,
|
||||
}: { fromPreconfiguration: boolean; secretHashes?: Record<string, any> } = {
|
||||
{ fromPreconfiguration = false }: { fromPreconfiguration: boolean } = {
|
||||
fromPreconfiguration: false,
|
||||
}
|
||||
) {
|
||||
|
@ -756,7 +747,6 @@ class OutputService {
|
|||
oldOutput: originalOutput,
|
||||
outputUpdate: data,
|
||||
esClient,
|
||||
secretHashes: data.is_preconfigured ? secretHashes : undefined,
|
||||
});
|
||||
|
||||
updateData.secrets = secretsRes.outputUpdate.secrets;
|
||||
|
|
|
@ -17,7 +17,6 @@ import {
|
|||
createOrUpdatePreconfiguredOutputs,
|
||||
cleanPreconfiguredOutputs,
|
||||
getPreconfiguredOutputFromConfig,
|
||||
hash,
|
||||
} from './outputs';
|
||||
|
||||
jest.mock('../agent_policy_update');
|
||||
|
@ -47,18 +46,16 @@ const spyAgentPolicyServicBumpAllAgentPoliciesForOutput = jest.spyOn(
|
|||
);
|
||||
|
||||
describe('output preconfiguration', () => {
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
mockedOutputService.create.mockReset();
|
||||
mockedOutputService.update.mockReset();
|
||||
mockedOutputService.delete.mockReset();
|
||||
mockedOutputService.getDefaultDataOutputId.mockReset();
|
||||
mockedOutputService.getDefaultESHosts.mockReturnValue(['http://default-es:9200']);
|
||||
const keyHash = await hash('secretKey');
|
||||
const passwordHash = await hash('secretPassword');
|
||||
mockedOutputService.bulkGet.mockImplementation(async (soClient, id): Promise<Output[]> => {
|
||||
return [
|
||||
{
|
||||
id: 'existing-es-output-1',
|
||||
id: 'existing-output-1',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Output 1',
|
||||
|
@ -77,76 +74,8 @@ describe('output preconfiguration', () => {
|
|||
hosts: ['kafka.co:80'],
|
||||
is_preconfigured: true,
|
||||
},
|
||||
{
|
||||
id: 'existing-logstash-output-with-secrets-1',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Logstash Output With Secrets 1',
|
||||
type: 'logstash',
|
||||
hosts: ['test:4343'],
|
||||
is_preconfigured: true,
|
||||
secrets: {
|
||||
ssl: {
|
||||
key: {
|
||||
id: '123',
|
||||
hash: keyHash,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'existing-logstash-output-with-secrets-2',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Logstash Output With Secrets 2',
|
||||
type: 'logstash',
|
||||
hosts: ['test:4343'],
|
||||
is_preconfigured: true,
|
||||
secrets: {
|
||||
ssl: {
|
||||
key: 'secretKey',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'existing-kafka-output-with-secrets-1',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Kafka Output With Secrets 1',
|
||||
type: 'kafka',
|
||||
hosts: ['kafka.co:80'],
|
||||
is_preconfigured: true,
|
||||
secrets: {
|
||||
password: {
|
||||
id: '456',
|
||||
hash: passwordHash,
|
||||
},
|
||||
ssl: {
|
||||
key: {
|
||||
id: '789',
|
||||
hash: keyHash,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'existing-kafka-output-with-secrets-2',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Kafka Output With Secrets 2',
|
||||
type: 'kafka',
|
||||
hosts: ['kafka.co:80'],
|
||||
is_preconfigured: true,
|
||||
secrets: {
|
||||
password: 'secretPassword',
|
||||
ssl: {
|
||||
key: 'secretKey',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
});
|
||||
spyAgentPolicyServicBumpAllAgentPoliciesForOutput.mockClear();
|
||||
});
|
||||
|
||||
it('should generate a preconfigured output if elasticsearch.hosts is set in the config', async () => {
|
||||
|
@ -175,7 +104,7 @@ describe('output preconfiguration', () => {
|
|||
`);
|
||||
});
|
||||
|
||||
it('should create preconfigured output that does not exist', async () => {
|
||||
it('should create preconfigured output that does not exists', async () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
await createOrUpdatePreconfiguredOutputs(soClient, esClient, [
|
||||
|
@ -194,7 +123,7 @@ describe('output preconfiguration', () => {
|
|||
expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should create preconfigured kafka output that does not exist', async () => {
|
||||
it('should create preconfigured kafka output that does not exists', async () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
await createOrUpdatePreconfiguredOutputs(soClient, esClient, [
|
||||
|
@ -213,7 +142,7 @@ describe('output preconfiguration', () => {
|
|||
expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should create a preconfigured output with ca_trusted_fingerprint that does not exist', async () => {
|
||||
it('should create a preconfigured output with ca_trusted_fingerprint that does not exists', async () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
await createOrUpdatePreconfiguredOutputs(soClient, esClient, [
|
||||
|
@ -241,7 +170,7 @@ describe('output preconfiguration', () => {
|
|||
expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should create a preconfigured logstash output that does not exist', async () => {
|
||||
it('should create preconfigured logstash output that does not exist', async () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
await createOrUpdatePreconfiguredOutputs(soClient, esClient, [
|
||||
|
@ -261,66 +190,7 @@ describe('output preconfiguration', () => {
|
|||
expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should create a preconfigured logstash output with secrets that does not exist', async () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
await createOrUpdatePreconfiguredOutputs(soClient, esClient, [
|
||||
{
|
||||
id: 'non-existing-logstash-output-with-secrets-1',
|
||||
name: 'Logstash Output With Secrets 2',
|
||||
type: 'logstash',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
secrets: {
|
||||
ssl: {
|
||||
key: 'secretKey',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mockedOutputService.create).toBeCalled();
|
||||
expect(mockedOutputService.create).toBeCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({
|
||||
secrets: {
|
||||
ssl: {
|
||||
key: 'secretKey',
|
||||
},
|
||||
},
|
||||
}),
|
||||
expect.anything()
|
||||
);
|
||||
expect(mockedOutputService.update).not.toBeCalled();
|
||||
expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should create a preconfigured kafka output with secrets that does not exist', async () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
await createOrUpdatePreconfiguredOutputs(soClient, esClient, [
|
||||
{
|
||||
id: 'non-existing-kafka-output-with-secrets-1',
|
||||
name: 'Kafka Output With Secrets 2',
|
||||
type: 'kafka',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
secrets: {
|
||||
password: 'secretPassword',
|
||||
ssl: {
|
||||
key: 'secretKey',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mockedOutputService.create).toBeCalled();
|
||||
expect(mockedOutputService.update).not.toBeCalled();
|
||||
expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should set default hosts if hosts is not set output that does not exist', async () => {
|
||||
it('should set default hosts if hosts is not set output that does not exists', async () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
await createOrUpdatePreconfiguredOutputs(soClient, esClient, [
|
||||
|
@ -343,7 +213,7 @@ describe('output preconfiguration', () => {
|
|||
soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 });
|
||||
mockedOutputService.bulkGet.mockResolvedValue([
|
||||
{
|
||||
id: 'existing-es-output-1',
|
||||
id: 'existing-output-1',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Output 1',
|
||||
|
@ -355,7 +225,7 @@ describe('output preconfiguration', () => {
|
|||
]);
|
||||
await createOrUpdatePreconfiguredOutputs(soClient, esClient, [
|
||||
{
|
||||
id: 'existing-es-output-1',
|
||||
id: 'existing-output-1',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Output 1',
|
||||
|
@ -369,7 +239,7 @@ describe('output preconfiguration', () => {
|
|||
expect(mockedOutputService.update).toBeCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
'existing-es-output-1',
|
||||
'existing-output-1',
|
||||
expect.objectContaining({
|
||||
is_preconfigured: true,
|
||||
}),
|
||||
|
@ -384,7 +254,7 @@ describe('output preconfiguration', () => {
|
|||
soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 });
|
||||
await createOrUpdatePreconfiguredOutputs(soClient, esClient, [
|
||||
{
|
||||
id: 'existing-es-output-1',
|
||||
id: 'existing-output-1',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Output 1',
|
||||
|
@ -398,30 +268,6 @@ describe('output preconfiguration', () => {
|
|||
expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled();
|
||||
});
|
||||
|
||||
it('should update output if a preconfigured logstash ouput with secrets exists and has changed', async () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 });
|
||||
await createOrUpdatePreconfiguredOutputs(soClient, esClient, [
|
||||
{
|
||||
id: 'existing-logstash-output-with-secrets-1',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Logstash Output With Secrets 1',
|
||||
type: 'logstash',
|
||||
secrets: {
|
||||
ssl: {
|
||||
key: 'secretKey2', // field that changed
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mockedOutputService.create).not.toBeCalled();
|
||||
expect(mockedOutputService.update).toBeCalled();
|
||||
expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled();
|
||||
});
|
||||
|
||||
it('should update output if preconfigured kafka output exists and changed', async () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
|
@ -433,7 +279,7 @@ describe('output preconfiguration', () => {
|
|||
is_default_monitoring: false,
|
||||
name: 'Kafka Output 1',
|
||||
type: 'kafka',
|
||||
hosts: ['kafka.co:8080'], // field that changed
|
||||
hosts: ['kafka.co:8080'],
|
||||
},
|
||||
]);
|
||||
|
||||
|
@ -442,49 +288,24 @@ describe('output preconfiguration', () => {
|
|||
expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled();
|
||||
});
|
||||
|
||||
it('should update ouput if a preconfigured kafka with secrets exists and has changed', async () => {
|
||||
it('should not update output if preconfigured output exists and did not changed', async () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 });
|
||||
await createOrUpdatePreconfiguredOutputs(soClient, esClient, [
|
||||
{
|
||||
id: 'existing-kafka-output-with-secrets-1',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Kafka Output With Secrets 1',
|
||||
type: 'kafka',
|
||||
secrets: {
|
||||
password: 'secretPassword2', // field that changed
|
||||
ssl: {
|
||||
key: 'secretKey2',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mockedOutputService.create).not.toBeCalled();
|
||||
expect(mockedOutputService.update).toBeCalled();
|
||||
expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled();
|
||||
});
|
||||
|
||||
it('should not update output if preconfigured output exists and did not change', async () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 });
|
||||
await createOrUpdatePreconfiguredOutputs(soClient, esClient, [
|
||||
{
|
||||
id: 'existing-es-output-1',
|
||||
id: 'existing-output-1',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Output 1',
|
||||
type: 'elasticsearch',
|
||||
hosts: ['http://es.co:80'],
|
||||
hosts: ['http://newhostichanged.co:9201'], // field that changed
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mockedOutputService.create).not.toBeCalled();
|
||||
expect(mockedOutputService.update).not.toBeCalled();
|
||||
expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled();
|
||||
expect(mockedOutputService.update).toBeCalled();
|
||||
expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled();
|
||||
});
|
||||
|
||||
it('should not update output if preconfigured kafka output exists and did not change', async () => {
|
||||
|
@ -498,109 +319,7 @@ describe('output preconfiguration', () => {
|
|||
is_default_monitoring: false,
|
||||
name: 'Kafka Output 1',
|
||||
type: 'kafka',
|
||||
hosts: ['kafka.co:80'],
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mockedOutputService.create).not.toBeCalled();
|
||||
expect(mockedOutputService.update).not.toBeCalled();
|
||||
expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should not update output if a preconfigured logstash output with secrets exists and did not change', async () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 });
|
||||
await createOrUpdatePreconfiguredOutputs(soClient, esClient, [
|
||||
{
|
||||
id: 'existing-logstash-output-with-secrets-1',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Logstash Output With Secrets 1',
|
||||
type: 'logstash',
|
||||
hosts: ['test:4343'],
|
||||
secrets: {
|
||||
ssl: {
|
||||
key: 'secretKey',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mockedOutputService.create).not.toBeCalled();
|
||||
expect(mockedOutputService.update).not.toBeCalled();
|
||||
expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should not update output if a preconfigured kafka output with secrets exists and did not change', async () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 });
|
||||
await createOrUpdatePreconfiguredOutputs(soClient, esClient, [
|
||||
{
|
||||
id: 'existing-kafka-output-with-secrets-1',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Kafka Output With Secrets 1',
|
||||
type: 'kafka',
|
||||
hosts: ['kafka.co:80'],
|
||||
secrets: {
|
||||
password: 'secretPassword',
|
||||
ssl: {
|
||||
key: 'secretKey',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mockedOutputService.create).not.toBeCalled();
|
||||
expect(mockedOutputService.update).not.toBeCalled();
|
||||
expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should update output if a preconfigured logstash output with plain value secrets exists and did not change', async () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 });
|
||||
await createOrUpdatePreconfiguredOutputs(soClient, esClient, [
|
||||
{
|
||||
id: 'existing-logstash-output-with-secrets-2',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Logstash Output With Secrets 2',
|
||||
type: 'logstash',
|
||||
hosts: ['test:4343'],
|
||||
secrets: {
|
||||
ssl: {
|
||||
key: 'secretKey', // no change
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mockedOutputService.create).not.toBeCalled();
|
||||
expect(mockedOutputService.update).toBeCalled();
|
||||
expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled();
|
||||
});
|
||||
|
||||
it('should update output if a preconfigured kafka output with plain value secrets exists and did not change', async () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 });
|
||||
await createOrUpdatePreconfiguredOutputs(soClient, esClient, [
|
||||
{
|
||||
id: 'existing-kafka-output-with-secrets-2',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Kafka Output With Secrets 2',
|
||||
type: 'kafka',
|
||||
hosts: ['kafka.co:80'],
|
||||
secrets: {
|
||||
password: 'secretPassword', // no change
|
||||
ssl: {
|
||||
key: 'secretKey', // no change
|
||||
},
|
||||
},
|
||||
hosts: ['kafka.co:8080'],
|
||||
},
|
||||
]);
|
||||
|
||||
|
@ -613,7 +332,7 @@ describe('output preconfiguration', () => {
|
|||
{
|
||||
name: 'no changes',
|
||||
data: {
|
||||
id: 'existing-es-output-1',
|
||||
id: 'existing-output-1',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Output 1',
|
||||
|
@ -624,7 +343,7 @@ describe('output preconfiguration', () => {
|
|||
{
|
||||
name: 'hosts without port',
|
||||
data: {
|
||||
id: 'existing-es-output-1',
|
||||
id: 'existing-output-1',
|
||||
is_default: false,
|
||||
is_default_monitoring: false,
|
||||
name: 'Output 1',
|
||||
|
|
|
@ -8,16 +8,8 @@
|
|||
import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server';
|
||||
import { isEqual } from 'lodash';
|
||||
import { safeDump } from 'js-yaml';
|
||||
import argon2 from 'argon2';
|
||||
|
||||
import type {
|
||||
PreconfiguredOutput,
|
||||
Output,
|
||||
NewOutput,
|
||||
OutputSecret,
|
||||
KafkaOutput,
|
||||
NewLogstashOutput,
|
||||
} from '../../../common/types';
|
||||
import type { PreconfiguredOutput, Output, NewOutput } from '../../../common/types';
|
||||
import { normalizeHostsForAgents } from '../../../common/services';
|
||||
import type { FleetConfigType } from '../../config';
|
||||
import { DEFAULT_OUTPUT_ID, DEFAULT_OUTPUT } from '../../constants';
|
||||
|
@ -107,79 +99,25 @@ export async function createOrUpdatePreconfiguredOutputs(
|
|||
}
|
||||
|
||||
const isUpdateWithNewData =
|
||||
existingOutput && (await isPreconfiguredOutputDifferentFromCurrent(existingOutput, data));
|
||||
existingOutput && isPreconfiguredOutputDifferentFromCurrent(existingOutput, data);
|
||||
|
||||
if (isCreate || isUpdateWithNewData) {
|
||||
const secretHashes = await hashSecrets(output);
|
||||
|
||||
if (isCreate) {
|
||||
logger.debug(`Creating preconfigured output ${output.id}`);
|
||||
await outputService.create(soClient, esClient, data, {
|
||||
id,
|
||||
fromPreconfiguration: true,
|
||||
secretHashes,
|
||||
});
|
||||
} else if (isUpdateWithNewData) {
|
||||
logger.debug(`Updating preconfigured output ${output.id}`);
|
||||
await outputService.update(soClient, esClient, id, data, {
|
||||
fromPreconfiguration: true,
|
||||
secretHashes,
|
||||
});
|
||||
// Bump revision of all policies using that output
|
||||
if (outputData.is_default || outputData.is_default_monitoring) {
|
||||
await agentPolicyService.bumpAllAgentPolicies(soClient, esClient);
|
||||
} else {
|
||||
await agentPolicyService.bumpAllAgentPoliciesForOutput(soClient, esClient, id);
|
||||
}
|
||||
if (isCreate) {
|
||||
logger.debug(`Creating output ${output.id}`);
|
||||
await outputService.create(soClient, esClient, data, { id, fromPreconfiguration: true });
|
||||
} else if (isUpdateWithNewData) {
|
||||
logger.debug(`Updating output ${output.id}`);
|
||||
await outputService.update(soClient, esClient, id, data, { fromPreconfiguration: true });
|
||||
// Bump revision of all policies using that output
|
||||
if (outputData.is_default || outputData.is_default_monitoring) {
|
||||
await agentPolicyService.bumpAllAgentPolicies(soClient, esClient);
|
||||
} else {
|
||||
await agentPolicyService.bumpAllAgentPoliciesForOutput(soClient, esClient, id);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export async function hash(str: string) {
|
||||
return argon2.hash(str, {
|
||||
type: argon2.argon2id,
|
||||
memoryCost: 19456,
|
||||
timeCost: 2,
|
||||
parallelism: 1,
|
||||
});
|
||||
}
|
||||
|
||||
async function hashSecrets(output: PreconfiguredOutput) {
|
||||
if (output.type === 'kafka') {
|
||||
const kafkaOutput = output as KafkaOutput;
|
||||
if (typeof kafkaOutput.secrets?.password === 'string') {
|
||||
const password = await hash(kafkaOutput.secrets?.password);
|
||||
return {
|
||||
password,
|
||||
};
|
||||
}
|
||||
if (typeof kafkaOutput.secrets?.ssl?.key === 'string') {
|
||||
const key = await hash(kafkaOutput.secrets?.ssl?.key);
|
||||
return {
|
||||
ssl: {
|
||||
key,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
if (output.type === 'logstash') {
|
||||
const logstashOutput = output as NewLogstashOutput;
|
||||
|
||||
if (typeof logstashOutput.secrets?.ssl?.key === 'string') {
|
||||
const key = await hash(logstashOutput.secrets?.ssl?.key);
|
||||
return {
|
||||
ssl: {
|
||||
key,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function cleanPreconfiguredOutputs(
|
||||
soClient: SavedObjectsClientContract,
|
||||
esClient: ElasticsearchClient,
|
||||
|
@ -227,56 +165,15 @@ export async function cleanPreconfiguredOutputs(
|
|||
}
|
||||
}
|
||||
|
||||
const hasHash = (secret?: OutputSecret): secret is { id: string; hash: string } => {
|
||||
return !!secret && typeof secret !== 'string' && !!secret.hash;
|
||||
};
|
||||
|
||||
async function isSecretDifferent(
|
||||
preconfiguredValue: OutputSecret | undefined,
|
||||
existingSecret: OutputSecret | undefined
|
||||
): Promise<boolean> {
|
||||
if (!existingSecret && preconfiguredValue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!preconfiguredValue && existingSecret) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!preconfiguredValue && !existingSecret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasHash(existingSecret) && typeof preconfiguredValue === 'string') {
|
||||
// verifying the has tells us if the value has changed
|
||||
const hashIsVerified = await argon2.verify(existingSecret.hash, preconfiguredValue!);
|
||||
|
||||
return !hashIsVerified;
|
||||
} else {
|
||||
// if there is no hash then the safest thing to do is assume the value has changed
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
async function isPreconfiguredOutputDifferentFromCurrent(
|
||||
function isPreconfiguredOutputDifferentFromCurrent(
|
||||
existingOutput: Output,
|
||||
preconfiguredOutput: Partial<Output>
|
||||
): Promise<boolean> {
|
||||
const kafkaFieldsAreDifferent = async (): Promise<boolean> => {
|
||||
): boolean {
|
||||
const kafkaFieldsAreDifferent = (): boolean => {
|
||||
if (existingOutput.type !== 'kafka' || preconfiguredOutput.type !== 'kafka') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const passwordHashIsDifferent = await isSecretDifferent(
|
||||
preconfiguredOutput.secrets?.password,
|
||||
existingOutput.secrets?.password
|
||||
);
|
||||
|
||||
const sslKeyHashIsDifferent = await isSecretDifferent(
|
||||
preconfiguredOutput.secrets?.ssl?.key,
|
||||
existingOutput.secrets?.ssl?.key
|
||||
);
|
||||
|
||||
return (
|
||||
isDifferent(existingOutput.client_id, preconfiguredOutput.client_id) ||
|
||||
isDifferent(existingOutput.version, preconfiguredOutput.version) ||
|
||||
|
@ -296,24 +193,10 @@ async function isPreconfiguredOutputDifferentFromCurrent(
|
|||
isDifferent(existingOutput.headers, preconfiguredOutput.headers) ||
|
||||
isDifferent(existingOutput.timeout, preconfiguredOutput.timeout) ||
|
||||
isDifferent(existingOutput.broker_timeout, preconfiguredOutput.broker_timeout) ||
|
||||
isDifferent(existingOutput.required_acks, preconfiguredOutput.required_acks) ||
|
||||
passwordHashIsDifferent ||
|
||||
sslKeyHashIsDifferent
|
||||
isDifferent(existingOutput.required_acks, preconfiguredOutput.required_acks)
|
||||
);
|
||||
};
|
||||
|
||||
const logstashFieldsAreDifferent = async (): Promise<boolean> => {
|
||||
if (existingOutput.type !== 'logstash' || preconfiguredOutput.type !== 'logstash') {
|
||||
return false;
|
||||
}
|
||||
const sslKeyHashIsDifferent = await isSecretDifferent(
|
||||
preconfiguredOutput.secrets?.ssl?.key,
|
||||
existingOutput.secrets?.ssl?.key
|
||||
);
|
||||
|
||||
return sslKeyHashIsDifferent;
|
||||
};
|
||||
|
||||
return (
|
||||
!existingOutput.is_preconfigured ||
|
||||
isDifferent(existingOutput.is_default, preconfiguredOutput.is_default) ||
|
||||
|
@ -338,7 +221,6 @@ async function isPreconfiguredOutputDifferentFromCurrent(
|
|||
isDifferent(existingOutput.config_yaml, preconfiguredOutput.config_yaml) ||
|
||||
isDifferent(existingOutput.proxy_id, preconfiguredOutput.proxy_id) ||
|
||||
isDifferent(existingOutput.allow_edit ?? [], preconfiguredOutput.allow_edit ?? []) ||
|
||||
(await kafkaFieldsAreDifferent()) ||
|
||||
(await logstashFieldsAreDifferent())
|
||||
kafkaFieldsAreDifferent()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server';
|
||||
|
||||
import { get, keyBy } from 'lodash';
|
||||
import { keyBy } from 'lodash';
|
||||
import { set } from '@kbn/safer-lodash-set';
|
||||
|
||||
import type { KafkaOutput, Output, OutputSecretPath } from '../../common/types';
|
||||
|
@ -247,9 +247,8 @@ export async function extractAndWriteSecrets(opts: {
|
|||
export async function extractAndWriteOutputSecrets(opts: {
|
||||
output: NewOutput;
|
||||
esClient: ElasticsearchClient;
|
||||
secretHashes?: Record<string, any>;
|
||||
}): Promise<{ output: NewOutput; secretReferences: PolicySecretReference[] }> {
|
||||
const { output, esClient, secretHashes = {} } = opts;
|
||||
const { output, esClient } = opts;
|
||||
|
||||
const secretPaths = getOutputSecretPaths(output.type, output).filter(
|
||||
(path) => typeof path.value === 'string'
|
||||
|
@ -266,12 +265,7 @@ export async function extractAndWriteOutputSecrets(opts: {
|
|||
|
||||
const outputWithSecretRefs = JSON.parse(JSON.stringify(output));
|
||||
secretPaths.forEach((secretPath, i) => {
|
||||
const pathWithoutPrefix = secretPath.path.replace('secrets.', '');
|
||||
const maybeHash = get(secretHashes, pathWithoutPrefix);
|
||||
set(outputWithSecretRefs, secretPath.path, {
|
||||
id: secrets[i].id,
|
||||
...(typeof maybeHash === 'string' && { hash: maybeHash }),
|
||||
});
|
||||
set(outputWithSecretRefs, secretPath.path, { id: secrets[i].id });
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -405,13 +399,12 @@ export async function extractAndUpdateOutputSecrets(opts: {
|
|||
oldOutput: Output;
|
||||
outputUpdate: Partial<Output>;
|
||||
esClient: ElasticsearchClient;
|
||||
secretHashes?: Record<string, any>;
|
||||
}): Promise<{
|
||||
outputUpdate: Partial<Output>;
|
||||
secretReferences: PolicySecretReference[];
|
||||
secretsToDelete: PolicySecretReference[];
|
||||
}> {
|
||||
const { oldOutput, outputUpdate, esClient, secretHashes } = opts;
|
||||
const { oldOutput, outputUpdate, esClient } = opts;
|
||||
const outputType = outputUpdate.type || oldOutput.type;
|
||||
const oldSecretPaths = getOutputSecretPaths(outputType, oldOutput);
|
||||
const updatedSecretPaths = getOutputSecretPaths(outputType, outputUpdate);
|
||||
|
@ -432,13 +425,7 @@ export async function extractAndUpdateOutputSecrets(opts: {
|
|||
|
||||
const outputWithSecretRefs = JSON.parse(JSON.stringify(outputUpdate));
|
||||
toCreate.forEach((secretPath, i) => {
|
||||
const pathWithoutPrefix = secretPath.path.replace('secrets.', '');
|
||||
const maybeHash = get(secretHashes, pathWithoutPrefix);
|
||||
|
||||
set(outputWithSecretRefs, secretPath.path, {
|
||||
id: createdSecrets[i].id,
|
||||
...(typeof maybeHash === 'string' && { hash: maybeHash }),
|
||||
});
|
||||
set(outputWithSecretRefs, secretPath.path, { id: createdSecrets[i].id });
|
||||
});
|
||||
|
||||
const secretReferences = [
|
||||
|
|
34
yarn.lock
34
yarn.lock
|
@ -6477,21 +6477,6 @@
|
|||
resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz#c15367178d8bfe4765e6b47b542fe821ce259c7b"
|
||||
integrity sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ==
|
||||
|
||||
"@mapbox/node-pre-gyp@^1.0.11":
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa"
|
||||
integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==
|
||||
dependencies:
|
||||
detect-libc "^2.0.0"
|
||||
https-proxy-agent "^5.0.0"
|
||||
make-dir "^3.1.0"
|
||||
node-fetch "^2.6.7"
|
||||
nopt "^5.0.0"
|
||||
npmlog "^5.0.1"
|
||||
rimraf "^3.0.2"
|
||||
semver "^7.3.5"
|
||||
tar "^6.1.11"
|
||||
|
||||
"@mapbox/point-geometry@0.1.0", "@mapbox/point-geometry@^0.1.0", "@mapbox/point-geometry@~0.1.0":
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz#8a83f9335c7860effa2eeeca254332aa0aeed8f2"
|
||||
|
@ -7140,11 +7125,6 @@
|
|||
node-addon-api "^3.2.1"
|
||||
node-gyp-build "^4.3.0"
|
||||
|
||||
"@phc/format@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@phc/format/-/format-1.0.0.tgz#b5627003b3216dc4362125b13f48a4daa76680e4"
|
||||
integrity sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==
|
||||
|
||||
"@pkgjs/parseargs@^0.11.0":
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
||||
|
@ -11169,15 +11149,6 @@ arg@^5.0.1, arg@^5.0.2:
|
|||
resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c"
|
||||
integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==
|
||||
|
||||
argon2@0.31.1:
|
||||
version "0.31.1"
|
||||
resolved "https://registry.yarnpkg.com/argon2/-/argon2-0.31.1.tgz#c8560bc76b12681afea13e28f3417aaa4b84c466"
|
||||
integrity sha512-ik2xnJrLXazya7m4Nz1XfBSRjXj8Koq8qF9PsQC8059p20ifWc9zx/hgU3ItZh/3TnwXkv0RbhvjodPkmFf0bg==
|
||||
dependencies:
|
||||
"@mapbox/node-pre-gyp" "^1.0.11"
|
||||
"@phc/format" "^1.0.0"
|
||||
node-addon-api "^7.0.0"
|
||||
|
||||
argparse@^1.0.7:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
|
||||
|
@ -22796,11 +22767,6 @@ node-addon-api@^6.1.0:
|
|||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76"
|
||||
integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==
|
||||
|
||||
node-addon-api@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.0.0.tgz#8136add2f510997b3b94814f4af1cce0b0e3962e"
|
||||
integrity sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==
|
||||
|
||||
node-cache@^5.1.0:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue