Skip to content

Commit

Permalink
[Synthetics] Omit or include ssl keys when appropriate for project …
Browse files Browse the repository at this point in the history
…monitors and private locations (elastic#149298)

## Summary

Resolves elastic#149083

1. [Prevents tls fields from being
saved](https://github.com/elastic/kibana/pull/149298/files#diff-56296f634bf379eb71629f426c670cd030d2a15263a59964847c0d10af09a767R14)
on the Synthetics Integration policy when `is_tls_enabled` is false
2. Ensures `is_tls_enabled` is set properly for project monitors
([http](https://github.com/elastic/kibana/pull/149298/files#diff-0f42bb3b11a6ab864dee3488d5e9f7282adc009a261b3caee743a880b825c766R73)
and
[tcp](https://github.com/elastic/kibana/pull/149298/files#diff-3ad87e629abc6f17c395e8435c94f0f1a6274c9efea7d24ab81b7635ef0e43dfR69)).
This ensures that when a monitor is sent to a public location or a
private location, the `ssl` fields are sent or stripped appropriately.

### Testing

1. Create a private location
2. Create 2 lightweight project monitors using the following
configuration
```
- type: tcp
  id: 'tls-enabled'
  name: 'TLS-Enabled'
  hosts: ["8.8.8.8:80"]
  ssl:
     verification_mode: 'strict'
```
```
- type: tcp
  id: 'tls-disabled'
  name: 'TLS-Disabled'
  hosts: ["8.8.8.8:80"]
```
3. Set these monitors to execute from both a private and public location
via the `monitor` key in your `synthetics.config.ts` file.
```
    monitor: {
      schedule: 3,
      privateLocations: ["YOUR PRIVATE LOCATION"],
      locations: ["us_central"], // to test against dev environment
    },
```
4. Navigate to the agent policy for the private location and inspect the
full policy. Ensure the Synthetics policy on the agent package policy
does not have `ssl` fields set for ssl disabled monitor. Ensure the
`ssl` fields are set for the ssl enabled monitor.
  • Loading branch information
dominiqueclarke authored Jan 24, 2023
1 parent bd940e8 commit 0592abd
Show file tree
Hide file tree
Showing 15 changed files with 531 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { DataStream } from '../runtime_types';
import { ConfigKey, DataStream } from '../runtime_types';
import { formatSyntheticsPolicy } from './format_synthetics_policy';

describe('formatSyntheticsPolicy', () => {
Expand Down Expand Up @@ -499,8 +499,11 @@ describe('formatSyntheticsPolicy', () => {
});
});

it('formats http policy', () => {
const { formattedPolicy } = formatSyntheticsPolicy(testNewPolicy, DataStream.HTTP, httpPolicy);
it.each([true, false])('formats http policy', (isTLSEnabled) => {
const { formattedPolicy } = formatSyntheticsPolicy(testNewPolicy, DataStream.HTTP, {
...httpPolicy,
[ConfigKey.METADATA]: { is_tls_enabled: isTLSEnabled },
});

expect(formattedPolicy).toEqual({
enabled: true,
Expand All @@ -518,7 +521,7 @@ describe('formatSyntheticsPolicy', () => {
vars: {
__ui: {
type: 'yaml',
value: '{"is_tls_enabled":false,"is_zip_url_tls_enabled":false}',
value: `{"is_tls_enabled":${isTLSEnabled}}`,
},
'check.request.body': {
type: 'yaml',
Expand Down Expand Up @@ -628,11 +631,11 @@ describe('formatSyntheticsPolicy', () => {
},
'ssl.supported_protocols': {
type: 'yaml',
value: '["TLSv1.1","TLSv1.2","TLSv1.3"]',
value: isTLSEnabled ? '["TLSv1.1","TLSv1.2","TLSv1.3"]' : null,
},
'ssl.verification_mode': {
type: 'text',
value: 'full',
value: isTLSEnabled ? 'full' : null,
},
tags: {
type: 'yaml',
Expand Down Expand Up @@ -1287,7 +1290,7 @@ const httpPolicy: any = {
journey_id: '',
hash: '',
id: '51ccd9d9-fc3f-4718-ba9d-b6ef80e73fc5',
__ui: { is_tls_enabled: false, is_zip_url_tls_enabled: false },
__ui: { is_tls_enabled: false },
urls: 'https://www.google.com',
max_redirects: '0',
'url.port': null,
Expand Down
36 changes: 25 additions & 11 deletions x-pack/plugins/synthetics/common/formatters/tls/formatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,43 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { TLSFields, ConfigKey } from '../../runtime_types/monitor_management';
import { TLSFields, TLSVersion, ConfigKey } from '../../runtime_types/monitor_management';
import { Formatter } from '../common/formatters';

type TLSFormatMap = Record<keyof TLSFields, Formatter>;

export const tlsFormatters: TLSFormatMap = {
[ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: (fields) =>
tlsValueToYamlFormatter(fields[ConfigKey.TLS_CERTIFICATE_AUTHORITIES]),
fields[ConfigKey.METADATA]?.is_tls_enabled
? tlsValueToYamlFormatter(fields[ConfigKey.TLS_CERTIFICATE_AUTHORITIES])
: null,
[ConfigKey.TLS_CERTIFICATE]: (fields) =>
tlsValueToYamlFormatter(fields[ConfigKey.TLS_CERTIFICATE]),
[ConfigKey.TLS_KEY]: (fields) => tlsValueToYamlFormatter(fields[ConfigKey.TLS_KEY]),
fields[ConfigKey.METADATA]?.is_tls_enabled
? tlsValueToYamlFormatter(fields[ConfigKey.TLS_CERTIFICATE])
: null,
[ConfigKey.TLS_KEY]: (fields) =>
fields[ConfigKey.METADATA]?.is_tls_enabled
? tlsValueToYamlFormatter(fields[ConfigKey.TLS_KEY])
: null,
[ConfigKey.TLS_KEY_PASSPHRASE]: (fields) =>
tlsValueToStringFormatter(fields[ConfigKey.TLS_KEY_PASSPHRASE]),
fields[ConfigKey.METADATA]?.is_tls_enabled
? tlsValueToStringFormatter(fields[ConfigKey.TLS_KEY_PASSPHRASE])
: null,
[ConfigKey.TLS_VERIFICATION_MODE]: (fields) =>
tlsValueToStringFormatter(fields[ConfigKey.TLS_VERIFICATION_MODE]),
[ConfigKey.TLS_VERSION]: (fields) => tlsArrayToYamlFormatter(fields[ConfigKey.TLS_VERSION]),
fields[ConfigKey.METADATA]?.is_tls_enabled
? tlsValueToStringFormatter(fields[ConfigKey.TLS_VERIFICATION_MODE])
: null,
[ConfigKey.TLS_VERSION]: (fields) =>
fields[ConfigKey.METADATA]?.is_tls_enabled
? tlsArrayToYamlFormatter(fields[ConfigKey.TLS_VERSION])
: null,
};

// only add tls settings if they are enabled by the user and isEnabled is true
export const tlsValueToYamlFormatter = (tlsValue: string = '') =>
export const tlsValueToYamlFormatter = (tlsValue: string | null = '') =>
tlsValue ? JSON.stringify(tlsValue) : null;

export const tlsValueToStringFormatter = (tlsValue: string = '') => tlsValue || null;
export const tlsValueToStringFormatter = (tlsValue: string | null = '') => tlsValue || null;

export const tlsArrayToYamlFormatter = (tlsValue: string[] = []) =>
tlsValue.length ? JSON.stringify(tlsValue) : null;
export const tlsArrayToYamlFormatter = (tlsValue: TLSVersion[] | null = []) =>
tlsValue?.length ? JSON.stringify(tlsValue) : null;
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
} from '../types';
import { defaultConfig } from '../synthetics_policy_create_extension';

describe('useBarChartsHooks', () => {
describe('useUpdatePolicy', () => {
const newPolicy: NewPackagePolicy = {
name: '',
description: '',
Expand Down Expand Up @@ -433,6 +433,9 @@ describe('useBarChartsHooks', () => {
...initialProps,
config: {
...defaultConfig[DataStream.HTTP],
[ConfigKey.METADATA]: {
is_tls_enabled: true,
},
[ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: ['test'],
[ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: ['test'],
[ConfigKey.RESPONSE_STATUS_CHECK]: ['test'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ export const getNormalizeCommonFields = ({
namespace,
}: NormalizedProjectProps): Partial<CommonFields> => {
const defaultFields = DEFAULT_COMMON_FIELDS;

const normalizedFields = {
[ConfigKey.JOURNEY_ID]: monitor.id || defaultFields[ConfigKey.JOURNEY_ID],
[ConfigKey.MONITOR_SOURCE_TYPE]: SourceType.PROJECT,
Expand Down Expand Up @@ -252,3 +251,7 @@ export const normalizeYamlConfig = (monitor: NormalizedProjectProps['monitor'])
unsupportedKeys,
};
};

// returns true when any ssl fields are defined
export const getHasTLSFields = (monitor: ProjectMonitor) =>
Object.keys(monitor).some((key) => key.includes('ssl'));
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { omit } from 'lodash';
import {
DataStream,
Locations,
Expand Down Expand Up @@ -144,7 +144,7 @@ describe('http normalizers', () => {
normalizedFields: {
...DEFAULT_FIELDS[DataStream.HTTP],
__ui: {
is_tls_enabled: false,
is_tls_enabled: true,
},
'check.request.body': {
type: 'json',
Expand Down Expand Up @@ -201,7 +201,7 @@ describe('http normalizers', () => {
normalizedFields: {
...DEFAULT_FIELDS[DataStream.HTTP],
__ui: {
is_tls_enabled: false,
is_tls_enabled: true,
},
'check.request.body': {
type: 'text',
Expand Down Expand Up @@ -255,5 +255,145 @@ describe('http normalizers', () => {
},
]);
});

it('sets is_tls_enabled appropriately', () => {
const actual = normalizeProjectMonitors({
locations,
privateLocations,
monitors: [monitors[0], { ...omit(monitors[1], ['ssl.supported_protocols']) }],
projectId,
namespace: 'test-space',
version: '8.5.0',
});
expect(actual).toEqual([
{
errors: [
{
details:
'`http` project monitors must have exactly one value for field `urls` in version `8.5.0`. Your monitor was not created or updated.',
id: 'my-monitor-2',
reason: 'Invalid Heartbeat configuration',
},
{
details:
'The following Heartbeat options are not supported for http project monitors in 8.5.0: check.response.body|unsupportedKey.nestedUnsupportedKey. You monitor was not created or updated.',
id: 'my-monitor-2',
reason: 'Unsupported Heartbeat option',
},
],
normalizedFields: {
...DEFAULT_FIELDS[DataStream.HTTP],
__ui: {
is_tls_enabled: true,
},
'check.request.body': {
type: 'json',
value: '{"json":"body"}',
},
'check.request.headers': {
'a-header': 'a-header-value',
},
'check.request.method': 'POST',
'check.response.body.negative': [],
'check.response.body.positive': [],
'check.response.headers': {},
'check.response.status': ['200'],
config_id: '',
custom_heartbeat_id: 'my-monitor-2-test-project-id-test-space',
enabled: false,
form_monitor_type: 'http',
journey_id: 'my-monitor-2',
locations: [],
max_redirects: '0',
name: 'My Monitor 2',
namespace: 'test_space',
origin: 'project',
original_space: 'test-space',
password: '',
project_id: 'test-project-id',
proxy_url: '',
'response.include_body': 'always',
'response.include_headers': false,
schedule: {
number: '60',
unit: 'm',
},
'service.name': 'test service',
'ssl.certificate': '',
'ssl.certificate_authorities': '',
'ssl.key': '',
'ssl.key_passphrase': '',
'ssl.supported_protocols': ['TLSv1.2', 'TLSv1.3'],
'ssl.verification_mode': 'full',
tags: [],
timeout: '80',
type: 'http',
urls: 'http://localhost:9200',
'url.port': null,
username: '',
id: '',
hash: testHash,
},
unsupportedKeys: ['check.response.body', 'unsupportedKey.nestedUnsupportedKey'],
},
{
errors: [],
normalizedFields: {
...DEFAULT_FIELDS[DataStream.HTTP],
__ui: {
is_tls_enabled: false,
},
'check.request.body': {
type: 'text',
value: 'sometextbody',
},
'check.request.headers': {
'a-header': 'a-header-value',
},
'check.request.method': 'POST',
'check.response.body.negative': [],
'check.response.body.positive': ['Saved', 'saved'],
'check.response.headers': {},
'check.response.status': ['200'],
config_id: '',
custom_heartbeat_id: 'my-monitor-3-test-project-id-test-space',
enabled: false,
form_monitor_type: 'http',
journey_id: 'my-monitor-3',
locations: [],
max_redirects: '0',
name: 'My Monitor 3',
namespace: 'test_space',
origin: 'project',
original_space: 'test-space',
password: '',
project_id: 'test-project-id',
proxy_url: '',
'response.include_body': 'always',
'response.include_headers': false,
schedule: {
number: '60',
unit: 'm',
},
'service.name': 'test service',
'ssl.certificate': '',
'ssl.certificate_authorities': '',
'ssl.key': '',
'ssl.key_passphrase': '',
'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'],
'ssl.verification_mode': 'full',
tags: ['tag2', 'tag2'],
timeout: '80',
type: 'http',
urls: 'http://localhost:9200',
'url.port': null,
username: '',
id: '',
hash: testHash,
},
unsupportedKeys: [],
},
]);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
getOptionalArrayField,
getUnsupportedKeysError,
getInvalidUrlsOrHostsError,
getHasTLSFields,
} from './common_fields';

export const getNormalizeHTTPFields = ({
Expand Down Expand Up @@ -69,7 +70,12 @@ export const getNormalizeHTTPFields = ({
[ConfigKey.TLS_VERSION]: get(monitor, ConfigKey.TLS_VERSION)
? (getOptionalListField(get(monitor, ConfigKey.TLS_VERSION)) as TLSVersion[])
: defaultFields[ConfigKey.TLS_VERSION],
[ConfigKey.METADATA]: {
...DEFAULT_FIELDS[DataStream.HTTP][ConfigKey.METADATA],
is_tls_enabled: getHasTLSFields(monitor),
},
};

return {
normalizedFields: {
...defaultFields,
Expand Down
Loading

0 comments on commit 0592abd

Please sign in to comment.