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

[Cloud Security] limit setup options for Agentless CSPM #172562

Merged
merged 39 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
d1a4e79
agentless/agent-based selector
maxcold Nov 20, 2023
da03bc8
i18n for the SetupType selector
maxcold Nov 21, 2023
b7327f8
use enum in the check to add hosts step
maxcold Nov 21, 2023
ba16765
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 22, 2023
8239c60
move setup type logic in a hook
maxcold Nov 22, 2023
102e93a
rename 'setup type' to 'setup technology'
maxcold Nov 22, 2023
dac9aac
move selector component to a separate file
maxcold Nov 22, 2023
5865d38
move setup technology logic to a custom hook
maxcold Nov 22, 2023
1eee824
fix imports from fleet-plugin
maxcold Nov 23, 2023
5bbb771
memoize isAgentlessAvailable
maxcold Nov 23, 2023
ada7955
move useSetupTechnology hook out
maxcold Nov 23, 2023
1716fc8
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 23, 2023
db5d71c
handle case of coming from a specific agent policy
maxcold Nov 23, 2023
79715dd
add tests for useSetupTechnology hook
maxcold Nov 24, 2023
771139a
add unit tests to useSetupTechnology in csp
maxcold Nov 24, 2023
528d472
switch feature flag to test on serverless
maxcold Nov 24, 2023
422f360
update agentless policy id
maxcold Nov 24, 2023
50bb376
Merge branch 'main' into csp-agentless-selector
maxcold Nov 27, 2023
8975fd0
default to false for agentless settings
maxcold Nov 27, 2023
8b26fbd
fetch agentless policy only in serverless
maxcold Nov 29, 2023
dc55d19
Merge branch 'main' into csp-agentless-selector
maxcold Nov 29, 2023
c7a0279
implement direct access keys setup method
maxcold Dec 1, 2023
b5bdfcc
add temporary credentials
maxcold Dec 1, 2023
a28c337
create shared components to be used for both agent-based and agentless
maxcold Dec 5, 2023
2e6cd1b
rename getAwsCredentialsFormAgentlessOptions
maxcold Dec 5, 2023
f9b552d
cover edit/upgrade flow
maxcold Dec 5, 2023
7ed7b22
Merge branch 'main' into csp-agentless-limit-setup-options
maxcold Dec 7, 2023
b8850a6
add temp setup guide for agentless options
maxcold Dec 8, 2023
b23ca23
remove todo
maxcold Dec 8, 2023
d357b62
unit tests for getDefaultAwsCredentialsType
maxcold Dec 8, 2023
cccd4ee
fix policy_template_form tests
maxcold Dec 8, 2023
f6a54a2
unit tests for use_setup_technology
maxcold Dec 8, 2023
3adc0fb
fix linting error
maxcold Dec 8, 2023
3a37864
Merge branch 'main' of github.com:elastic/kibana into csp-agentless-l…
maxcold Dec 8, 2023
b9123ac
add unit test for PolicyTemplateForm component
maxcold Dec 11, 2023
b5e4582
move test subj to const
maxcold Dec 11, 2023
51dbd53
Merge branch 'main' into csp-agentless-limit-setup-options
maxcold Dec 11, 2023
81e0208
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Dec 11, 2023
8eae17e
Merge branch 'main' into csp-agentless-limit-setup-options
maxcold Dec 14, 2023
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 @@ -4,44 +4,41 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import React, { ReactNode } from 'react';
import {
EuiFieldText,
EuiFieldPassword,
EuiCallOut,
EuiFormRow,
EuiHorizontalRule,
EuiLink,
EuiSelect,
EuiSpacer,
EuiText,
EuiTitle,
EuiSelect,
EuiCallOut,
EuiHorizontalRule,
} from '@elastic/eui';
import type { NewPackagePolicy } from '@kbn/fleet-plugin/public';
import { NewPackagePolicyInput, PackageInfo } from '@kbn/fleet-plugin/common';
import { FormattedMessage } from '@kbn/i18n-react';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import {
AwsCredentialsTypeOptions,
getAwsCredentialsFormManualOptions,
AwsOptions,
DEFAULT_MANUAL_AWS_CREDENTIALS_TYPE,
} from './get_aws_credentials_form_options';
import { RadioGroup } from '../csp_boxed_radio_group';
import {
getCspmCloudFormationDefaultValue,
getPosturePolicy,
NewPackagePolicyPostureInput,
} from '../utils';
import { CspRadioOption, RadioGroup } from '../csp_boxed_radio_group';
import { getPosturePolicy, NewPackagePolicyPostureInput } from '../utils';
import { SetupFormat, useAwsCredentialsForm } from './hooks';
import { AWS_ORGANIZATION_ACCOUNT } from '../policy_template_form';
import { AwsCredentialsType } from '../../../../common/types_old';
import { AwsInputVarFields } from './aws_input_var_fields';
import {
AWS_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ,
AWS_CREDENTIALS_TYPE_SELECTOR_TEST_SUBJ,
} from '../../test_subjects';

interface AWSSetupInfoContentProps {
integrationLink: string;
info: ReactNode;
}

const AWSSetupInfoContent = ({ integrationLink }: AWSSetupInfoContentProps) => {
export const AWSSetupInfoContent = ({ info }: AWSSetupInfoContentProps) => {
return (
<>
<EuiHorizontalRule margin="xl" />
Expand All @@ -55,48 +52,28 @@ const AWSSetupInfoContent = ({ integrationLink }: AWSSetupInfoContentProps) => {
</EuiTitle>
<EuiSpacer size="l" />
<EuiText color="subdued" size="s">
<FormattedMessage
id="xpack.csp.awsIntegration.gettingStarted.setupInfoContent"
defaultMessage="Utilize AWS CloudFormation (a built-in AWS tool) or a series of manual steps to set up and deploy CSPM for assessing your AWS environment's security posture. Refer to our {gettingStartedLink} guide for details."
values={{
gettingStartedLink: (
<EuiLink href={integrationLink} target="_blank">
<FormattedMessage
id="xpack.csp.awsIntegration.gettingStarted.setupInfoContentLink"
defaultMessage="Getting Started"
/>
</EuiLink>
),
}}
/>
{info}
</EuiText>
</>
);
};

const getSetupFormatOptions = (): Array<{ id: SetupFormat; label: string }> => [
const getSetupFormatOptions = (): CspRadioOption[] => [
{
id: 'cloud_formation',
label: 'CloudFormation',
testId: AWS_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ.CLOUDFORMATION,
},
{
id: 'manual',
label: i18n.translate('xpack.csp.awsIntegration.setupFormatOptions.manual', {
defaultMessage: 'Manual',
}),
testId: AWS_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ.MANUAL,
},
];

export const getDefaultAwsVarsGroup = (packageInfo: PackageInfo): AwsCredentialsType => {
const hasCloudFormationTemplate = !!getCspmCloudFormationDefaultValue(packageInfo);
if (hasCloudFormationTemplate) {
return 'cloud_formation';
}

return DEFAULT_MANUAL_AWS_CREDENTIALS_TYPE;
};

interface Props {
export interface AwsFormProps {
newPolicy: NewPackagePolicy;
input: Extract<NewPackagePolicyPostureInput, { type: 'cloudbeat/cis_aws' }>;
updatePolicy(updatedPolicy: NewPackagePolicy): void;
Expand Down Expand Up @@ -217,7 +194,7 @@ export const AwsCredentialsForm = ({
onChange,
setIsValid,
disabled,
}: Props) => {
}: AwsFormProps) => {
const {
awsCredentialsType,
setupFormat,
Expand All @@ -237,7 +214,24 @@ export const AwsCredentialsForm = ({

return (
<>
<AWSSetupInfoContent integrationLink={integrationLink} />
<AWSSetupInfoContent
info={
<FormattedMessage
id="xpack.csp.awsIntegration.gettingStarted.setupInfoContent"
defaultMessage="Utilize AWS CloudFormation (a built-in AWS tool) or a series of manual steps to set up and deploy CSPM for assessing your AWS environment's security posture. Refer to our {gettingStartedLink} guide for details."
values={{
gettingStartedLink: (
<EuiLink href={integrationLink} target="_blank">
<FormattedMessage
id="xpack.csp.awsIntegration.gettingStarted.setupInfoContentLink"
defaultMessage="Getting Started"
/>
</EuiLink>
),
}}
/>
}
/>
<EuiSpacer size="l" />
<RadioGroup
disabled={disabled}
Expand All @@ -255,6 +249,10 @@ export const AwsCredentialsForm = ({
{setupFormat === 'manual' && (
<>
<AwsCredentialTypeSelector
label={i18n.translate('xpack.csp.awsIntegration.awsCredentialTypeSelectorLabel', {
defaultMessage: 'Preferred manual method',
})}
options={getAwsCredentialsFormManualOptions()}
type={awsCredentialsType}
onChange={(optionId) => {
updatePolicy(
Expand All @@ -281,60 +279,26 @@ export const AwsCredentialsForm = ({
</>
);
};
const AwsCredentialTypeSelector = ({
export const AwsCredentialTypeSelector = ({
type,
onChange,
label,
options,
}: {
onChange(type: AwsCredentialsType): void;
type: AwsCredentialsType;
label: string;
options: AwsCredentialsTypeOptions;
}) => (
<EuiFormRow
fullWidth
label={i18n.translate('xpack.csp.awsIntegration.awsCredentialTypeSelectorLabel', {
defaultMessage: 'Preferred manual method',
})}
>
<EuiFormRow fullWidth label={label}>
<EuiSelect
fullWidth
options={getAwsCredentialsFormManualOptions()}
options={options}
value={type}
onChange={(optionElem) => {
onChange(optionElem.target.value as AwsCredentialsType);
}}
data-test-subj={AWS_CREDENTIALS_TYPE_SELECTOR_TEST_SUBJ}
/>
</EuiFormRow>
);

const AwsInputVarFields = ({
fields,
onChange,
}: {
fields: Array<AwsOptions[keyof AwsOptions]['fields'][number] & { value: string; id: string }>;
onChange: (key: string, value: string) => void;
}) => (
<div>
{fields.map((field) => (
<EuiFormRow key={field.id} label={field.label} fullWidth hasChildLabel={true} id={field.id}>
<>
{field.type === 'password' && (
<EuiFieldPassword
id={field.id}
type="dual"
fullWidth
value={field.value || ''}
onChange={(event) => onChange(field.id, event.target.value)}
/>
)}
{field.type === 'text' && (
<EuiFieldText
id={field.id}
fullWidth
value={field.value || ''}
onChange={(event) => onChange(field.id, event.target.value)}
/>
)}
</>
</EuiFormRow>
))}
</div>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { EuiLink, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { cspIntegrationDocsNavigation } from '../../../common/navigation/constants';
import {
DEFAULT_AGENTLESS_AWS_CREDENTIALS_TYPE,
getAwsCredentialsFormAgentlessOptions,
getAwsCredentialsFormOptions,
getInputVarsFields,
} from './get_aws_credentials_form_options';
import { getAwsCredentialsType, getPosturePolicy } from '../utils';
import { AwsInputVarFields } from './aws_input_var_fields';
import {
AwsFormProps,
ReadDocumentation,
AWSSetupInfoContent,
AwsCredentialTypeSelector,
} from './aws_credentials_form';

export const AwsCredentialsFormAgentless = ({ input, newPolicy, updatePolicy }: AwsFormProps) => {
const awsCredentialsType = getAwsCredentialsType(input) || DEFAULT_AGENTLESS_AWS_CREDENTIALS_TYPE;
const options = getAwsCredentialsFormOptions();
const group = options[awsCredentialsType];
const fields = getInputVarsFields(input, group.fields);
const integrationLink = cspIntegrationDocsNavigation.cspm.getStartedPath;

return (
<>
<AWSSetupInfoContent
info={
<FormattedMessage
id="xpack.csp.awsIntegration.gettingStarted.setupInfoContentAgentless"
defaultMessage="Utilize AWS Access Keys to set up and deploy CSPM for assessing your AWS environment's security posture. Refer to our {gettingStartedLink} guide for details."
values={{
gettingStartedLink: (
<EuiLink href={integrationLink} target="_blank">
<FormattedMessage
id="xpack.csp.awsIntegration.gettingStarted.setupInfoContentLink"
defaultMessage="Getting Started"
/>
</EuiLink>
),
}}
/>
}
/>
<EuiSpacer size="l" />
<AwsCredentialTypeSelector
label={i18n.translate('xpack.csp.awsIntegration.awsCredentialTypeSelectorLabelAgentless', {
defaultMessage: 'Preferred method',
})}
type={awsCredentialsType}
options={getAwsCredentialsFormAgentlessOptions()}
onChange={(optionId) => {
updatePolicy(
getPosturePolicy(newPolicy, input.type, {
'aws.credentials.type': { value: optionId },
})
);
}}
/>
<EuiSpacer size="m" />
{group.info}
<EuiSpacer size="m" />
<ReadDocumentation url={integrationLink} />
<EuiSpacer size="l" />
<AwsInputVarFields
fields={fields}
onChange={(key, value) => {
updatePolicy(getPosturePolicy(newPolicy, input.type, { [key]: { value } }));
}}
/>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { EuiFieldPassword, EuiFieldText, EuiFormRow } from '@elastic/eui';
import { AwsOptions } from './get_aws_credentials_form_options';

export const AwsInputVarFields = ({
fields,
onChange,
}: {
fields: Array<AwsOptions[keyof AwsOptions]['fields'][number] & { value: string; id: string }>;
onChange: (key: string, value: string) => void;
}) => (
<div>
{fields.map((field) => (
<EuiFormRow key={field.id} label={field.label} fullWidth hasChildLabel={true} id={field.id}>
<>
{field.type === 'password' && (
<EuiFieldPassword
id={field.id}
type="dual"
fullWidth
value={field.value || ''}
onChange={(event) => onChange(field.id, event.target.value)}
/>
)}
{field.type === 'text' && (
<EuiFieldText
id={field.id}
fullWidth
value={field.value || ''}
onChange={(event) => onChange(field.id, event.target.value)}
/>
)}
</>
</EuiFormRow>
))}
</div>
);
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,33 @@ export const getInputVarsFields = (input: NewPackagePolicyInput, fields: AwsCred
});

export type AwsOptions = Record<AwsCredentialsType, AwsOptionValue>;

export const getAwsCredentialsFormManualOptions = (): Array<{
export type AwsCredentialsTypeOptions = Array<{
value: AwsCredentialsType;
text: string;
}> => {
}>;

const getAwsCredentialsTypeSelectorOptions = (
filterFn: ({ value }: { value: AwsCredentialsType }) => boolean
): AwsCredentialsTypeOptions => {
return Object.entries(getAwsCredentialsFormOptions())
.map(([key, value]) => ({
value: key as AwsCredentialsType,
text: value.label,
}))
.filter(({ value }) => value !== 'cloud_formation');
.filter(filterFn);
};

export const getAwsCredentialsFormManualOptions = (): AwsCredentialsTypeOptions =>
getAwsCredentialsTypeSelectorOptions(({ value }) => value !== 'cloud_formation');

export const getAwsCredentialsFormAgentlessOptions = (): AwsCredentialsTypeOptions =>
getAwsCredentialsTypeSelectorOptions(
({ value }) => value === 'direct_access_keys' || value === 'temporary_keys'
);

export const DEFAULT_AWS_CREDENTIALS_TYPE = 'cloud_formation';
export const DEFAULT_MANUAL_AWS_CREDENTIALS_TYPE = 'assume_role';
export const DEFAULT_AGENTLESS_AWS_CREDENTIALS_TYPE = 'direct_access_keys';

export const getAwsCredentialsFormOptions = (): AwsOptions => ({
assume_role: {
Expand Down
Loading
Loading