Skip to content

Commit

Permalink
[CloudSecurity][Fleet] Add CloudFormation install method to CSPM (#15…
Browse files Browse the repository at this point in the history
  • Loading branch information
opauloh authored Jun 26, 2023
1 parent 05e97f7 commit 5728bfa
Show file tree
Hide file tree
Showing 29 changed files with 1,643 additions and 260 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,32 @@ interface PackagePolicyListData {

const PACKAGE_POLICY_LIST_QUERY_KEY = ['packagePolicyList'];

export const usePackagePolicyList = (packageInfoName: string) => {
export const usePackagePolicyList = (packageInfoName: string, { enabled = true }) => {
const { http } = useKibana<CoreStart>().services;

const query = useQuery<PackagePolicyListData, Error>(PACKAGE_POLICY_LIST_QUERY_KEY, async () => {
try {
const res = await http.get<PackagePolicyListData>(packagePolicyRouteService.getListPath(), {
query: {
perPage: SO_SEARCH_LIMIT,
page: 1,
kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${packageInfoName}`,
},
});
const query = useQuery<PackagePolicyListData, Error>(
PACKAGE_POLICY_LIST_QUERY_KEY,
async () => {
try {
const res = await http.get<PackagePolicyListData>(packagePolicyRouteService.getListPath(), {
query: {
perPage: SO_SEARCH_LIMIT,
page: 1,
kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${packageInfoName}`,
},
});

return res;
} catch (error: any) {
throw new Error(`Failed to fetch package policy list: ${error.message}`);
return res;
} catch (error: any) {
throw new Error(`Failed to fetch package policy list: ${error.message}`);
}
},
{
enabled,
refetchOnMount: false,
refetchOnWindowFocus: false,
}
});
);

return query;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
/*
* 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 {
EuiFieldText,
EuiFieldPassword,
EuiFormRow,
EuiLink,
EuiSpacer,
EuiText,
EuiTitle,
EuiSelect,
EuiCallOut,
} from '@elastic/eui';
import type { NewPackagePolicy } from '@kbn/fleet-plugin/public';
import { PackageInfo } from '@kbn/fleet-plugin/common';
import { FormattedMessage } from '@kbn/i18n-react';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import {
getAwsCredentialsFormManualOptions,
AwsCredentialsType,
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 { SetupFormat, useAwsCredentialsForm } from './hooks';

interface AWSSetupInfoContentProps {
integrationLink: string;
}

const AWSSetupInfoContent = ({ integrationLink }: AWSSetupInfoContentProps) => {
return (
<>
<EuiSpacer size="l" />
<EuiTitle size="s">
<h2>
<FormattedMessage
id="xpack.csp.awsIntegration.setupInfoContentTitle"
defaultMessage="Setup Access"
/>
</h2>
</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>
),
}}
/>
</EuiText>
</>
);
};

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

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

return DEFAULT_MANUAL_AWS_CREDENTIALS_TYPE;
};

interface Props {
newPolicy: NewPackagePolicy;
input: Extract<NewPackagePolicyPostureInput, { type: 'cloudbeat/cis_aws' }>;
updatePolicy(updatedPolicy: NewPackagePolicy): void;
packageInfo: PackageInfo;
onChange: any;
setIsValid: (isValid: boolean) => void;
}

const CloudFormationSetup = ({
hasCloudFormationTemplate,
}: {
hasCloudFormationTemplate: boolean;
}) => {
if (!hasCloudFormationTemplate) {
return (
<EuiCallOut color="warning">
<FormattedMessage
id="xpack.csp.awsIntegration.cloudFormationSetupStep.notSupported"
defaultMessage="CloudFormation is not supported on the current Integration version, please upgrade your integration to the latest version to use CloudFormation"
/>
</EuiCallOut>
);
}
return (
<>
<EuiText color="subdued" size="s">
<ol
css={css`
list-style: auto;
`}
>
<li>
<FormattedMessage
id="xpack.csp.awsIntegration.cloudFormationSetupStep.login"
defaultMessage="Log in as an admin to the AWS Account you want to onboard"
/>
</li>
<li>
<FormattedMessage
id="xpack.csp.awsIntegration.cloudFormationSetupStep.save"
defaultMessage="Click the Save and continue button on the bottom right of this page"
/>
</li>
<li>
<FormattedMessage
id="xpack.csp.awsIntegration.cloudFormationSetupStep.launch"
defaultMessage="On the subsequent pop-up modal, click the Launch CloudFormation button."
/>
</li>
</ol>
</EuiText>
<EuiSpacer size="l" />
<ReadDocumentation url={CLOUD_FORMATION_EXTERNAL_DOC_URL} />
</>
);
};

const CLOUD_FORMATION_EXTERNAL_DOC_URL =
'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-whatis-howdoesitwork.html';

const Link = ({ children, url }: { children: React.ReactNode; url: string }) => (
<EuiLink
href={url}
target="_blank"
rel="noopener nofollow noreferrer"
data-test-subj="externalLink"
>
{children}
</EuiLink>
);

const ReadDocumentation = ({ url }: { url: string }) => {
return (
<EuiText color="subdued" size="s">
<FormattedMessage
id="xpack.csp.awsIntegration.cloudFormationSetupNote"
defaultMessage="Read the {documentation} for more details"
values={{
documentation: (
<Link url={url}>
{i18n.translate('xpack.csp.awsIntegration.documentationLinkText', {
defaultMessage: 'documentation',
})}
</Link>
),
}}
/>
</EuiText>
);
};

export const AwsCredentialsForm = ({
input,
newPolicy,
updatePolicy,
packageInfo,
onChange,
setIsValid,
}: Props) => {
const {
awsCredentialsType,
setupFormat,
group,
fields,
integrationLink,
hasCloudFormationTemplate,
onSetupFormatChange,
} = useAwsCredentialsForm({
newPolicy,
input,
packageInfo,
onChange,
setIsValid,
updatePolicy,
});

return (
<>
<AWSSetupInfoContent integrationLink={integrationLink} />
<EuiSpacer size="l" />
<RadioGroup
size="m"
options={getSetupFormatOptions()}
idSelected={setupFormat}
onChange={onSetupFormatChange}
/>
<EuiSpacer size="l" />
{setupFormat === 'cloud_formation' && (
<CloudFormationSetup hasCloudFormationTemplate={hasCloudFormationTemplate} />
)}
{setupFormat === 'manual' && (
<>
<AwsCredentialTypeSelector
type={awsCredentialsType}
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 } }));
}}
/>
</>
)}
<EuiSpacer />
</>
);
};
const AwsCredentialTypeSelector = ({
type,
onChange,
}: {
onChange(type: AwsCredentialsType): void;
type: AwsCredentialsType;
}) => (
<EuiFormRow
fullWidth
label={i18n.translate('xpack.csp.awsIntegration.awsCredentialTypeSelectorLabel', {
defaultMessage: 'Preferred manual method',
})}
>
<EuiSelect
fullWidth
options={getAwsCredentialsFormManualOptions()}
value={type}
onChange={(optionElem) => {
onChange(optionElem.target.value as AwsCredentialsType);
}}
/>
</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>
);
Loading

0 comments on commit 5728bfa

Please sign in to comment.