[Cloud Security] add agentless ux creation flow (#189932)

## Summary
This PR add UX updates to agentless creation flow which include the
following:
 * Disabling Agent-based popups when newly created policy is Agentless
 * Adding Cloud Credentials Instructions 
* Minor UX adjustments such as adding Readme doc link on AWS credentials
form and spacing between Azure Credentials form.
 
<img width="985" alt="image"
src="https://github.com/user-attachments/assets/47b8dcaa-b63d-478d-b12e-ab6ba1d075eb">


https://github.com/user-attachments/assets/48d3a49e-043d-48f6-ac0c-8e131d47e976

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Lola 2024-08-12 17:26:46 -04:00 committed by GitHub
parent 012be928f8
commit a23f63db0e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 326 additions and 56 deletions

View file

@ -6,7 +6,7 @@
*/
import React from 'react';
import { EuiButton, EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui';
import { EuiButton, EuiCallOut, EuiLink, EuiSpacer, EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import semverCompare from 'semver/functions/compare';
import semverValid from 'semver/functions/valid';
@ -19,6 +19,7 @@ import {
import { cspIntegrationDocsNavigation } from '../../../common/navigation/constants';
import {
CLOUD_CREDENTIALS_PACKAGE_VERSION,
ORGANIZATION_ACCOUNT,
SINGLE_ACCOUNT,
TEMPLATE_URL_ACCOUNT_TYPE_ENV_VAR,
} from '../../../../common/constants';
@ -34,8 +35,145 @@ import {
AwsFormProps,
AWSSetupInfoContent,
AwsCredentialTypeSelector,
ReadDocumentation,
} from './aws_credentials_form';
const CLOUD_FORMATION_EXTERNAL_DOC_URL =
'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-whatis-howdoesitwork.html';
export const CloudFormationCloudCredentialsGuide = ({
isOrganization,
}: {
isOrganization?: boolean;
}) => {
return (
<EuiText size="s" color="subdued">
<FormattedMessage
id="xpack.csp.agentlessForm.cloudFormation.guide.description"
defaultMessage="Access keys are long-term credentials for an IAM user or the AWS account root user.
Utilize AWS CloudFormation (a built-in AWS tool) or a series of manual steps to set up access. {learnMore}."
values={{
learnMore: (
<EuiLink
href={CLOUD_FORMATION_EXTERNAL_DOC_URL}
target="_blank"
rel="noopener nofollow noreferrer"
data-test-subj="externalLink"
>
<FormattedMessage
id="xpack.csp.agentlessForm.cloudFormation.guide.learnMoreLinkText"
defaultMessage="Learn more about CloudFormation"
/>
</EuiLink>
),
}}
/>
<EuiSpacer size="m" />
<EuiText size="s" color="subdued">
<ol>
{isOrganization ? (
<li>
<FormattedMessage
id="xpack.csp.agentlessForm.cloudFormation.guide.steps.organizationLogin"
defaultMessage="Log in as an {admin} in the management account of the AWS Organization you want to onboard"
values={{
admin: <strong>admin</strong>,
}}
/>
</li>
) : (
<li>
<FormattedMessage
id="xpack.csp.agentlessForm.cloudFormation.guide.steps.singleLogin"
defaultMessage="Log in as an {admin} in the AWS account you want to onboard"
values={{
admin: <strong>admin</strong>,
}}
/>
</li>
)}
<EuiSpacer size="xs" />
<li>
<FormattedMessage
id="xpack.csp.agentlessForm.cloudFormation.guide.steps.launch"
defaultMessage="Click the {launchCloudFormation} button below."
values={{
launchCloudFormation: <strong>Launch CloudFormation</strong>,
}}
/>
</li>
<EuiSpacer size="xs" />
<li>
<FormattedMessage
id="xpack.csp.agentlessForm.cloudFormation.steps.region"
defaultMessage="(Optional) Change the {amazonRegion} in the upper right corner to the region you want to deploy your stack to"
values={{
amazonRegion: <strong>AWS region</strong>,
}}
/>
</li>
<EuiSpacer size="xs" />
<li>
<FormattedMessage
id="xpack.csp.agentlessForm.cloudFormation.steps.accept"
defaultMessage="Tick the checkbox under {capabilities} in the opened CloudFormation stack review form: {acknowledge}"
values={{
acknowledge: (
<em>
<FormattedMessage
id="xpack.csp.agentlessForm.cloudFormation.steps.accept.acknowledge"
defaultMessage="I acknowledge that AWS CloudFormation might create IAM resources."
/>
</em>
),
capabilities: (
<strong>
<FormattedMessage
id="xpack.csp.agentlessForm.cloudFormation.steps.accept.capabilties"
defaultMessage="capabilities"
/>
</strong>
),
}}
/>
</li>
<EuiSpacer size="xs" />
<li>
<FormattedMessage
id="xpack.csp.agentlessForm.cloudFormation.steps.create"
defaultMessage="Click {createStack}."
values={{
createStack: <strong>Create stack</strong>,
}}
/>
</li>
<EuiSpacer size="xs" />
<li>
<FormattedMessage
id="xpack.csp.agentlessForm.cloudFormation.steps.stackStatus"
defaultMessage="Once stack status is {createComplete} then click the Outputs tab"
values={{
createComplete: <strong>CREATE_COMPLETE</strong>,
}}
/>
</li>
<EuiSpacer size="xs" />
<li>
<FormattedMessage
id="xpack.csp.agentlessForm.cloudFormation.steps.credentials"
defaultMessage="Copy {accessKeyId} and {secretAccessKey} then paste the credentials below"
values={{
accessKeyId: <strong>Access Key Id</strong>,
secretAccessKey: <strong>Secret Access Key</strong>,
}}
/>
</li>
</ol>
</EuiText>
</EuiText>
);
};
export const AwsCredentialsFormAgentless = ({
input,
newPolicy,
@ -60,6 +198,8 @@ export const AwsCredentialsFormAgentless = ({
SUPPORTED_TEMPLATES_URL_FROM_PACKAGE_INFO_INPUT_VARS.CLOUD_FORMATION_CREDENTIALS
)?.replace(TEMPLATE_URL_ACCOUNT_TYPE_ENV_VAR, accountType);
const isOrganization = accountType === ORGANIZATION_ACCOUNT;
return (
<>
<AWSSetupInfoContent
@ -96,20 +236,22 @@ export const AwsCredentialsFormAgentless = ({
}}
/>
<EuiSpacer size="m" />
{!showCloudCredentialsButton && (
<>
<EuiCallOut color="warning">
<FormattedMessage
id="xpack.csp.fleetIntegration.awsCloudCredentials.cloudFormationSupportedMessage"
defaultMessage="Launch Cloud Formation for Automated Credentials not supported in current integration version. Please upgrade to the latest version to enable Launch CloudFormation for automated credentials."
/>
</EuiCallOut>
<EuiSpacer size="m" />
</>
)}
{awsCredentialsType === DEFAULT_AGENTLESS_AWS_CREDENTIALS_TYPE &&
!showCloudCredentialsButton && (
<>
<EuiCallOut color="warning">
<FormattedMessage
id="xpack.csp.fleetIntegration.awsCloudCredentials.cloudFormationSupportedMessage"
defaultMessage="Launch Cloud Formation for Automated Credentials not supported in current integration version. Please upgrade to the latest version to enable Launch CloudFormation for automated credentials."
/>
</EuiCallOut>
<EuiSpacer size="m" />
</>
)}
{awsCredentialsType === DEFAULT_AGENTLESS_AWS_CREDENTIALS_TYPE &&
showCloudCredentialsButton && (
<>
<CloudFormationCloudCredentialsGuide isOrganization={isOrganization} />
<EuiSpacer size="m" />
<EuiButton
data-test-subj="launchCloudFormationAgentlessButton"
@ -133,6 +275,7 @@ export const AwsCredentialsFormAgentless = ({
updatePolicy(getPosturePolicy(newPolicy, input.type, { [key]: { value } }));
}}
/>
<ReadDocumentation url={integrationLink} />
</>
);
};

View file

@ -316,21 +316,24 @@ export const AzureInputVarFields = ({
</>
)}
{field.type === 'text' && (
<EuiFormRow
key={field.id}
label={field.label}
fullWidth
hasChildLabel={true}
id={field.id}
>
<EuiFieldText
id={field.id}
<>
<EuiFormRow
key={field.id}
label={field.label}
fullWidth
value={field.value || ''}
onChange={(event) => onChange(field.id, event.target.value)}
data-test-subj={field.testSubj}
/>
</EuiFormRow>
hasChildLabel={true}
id={field.id}
>
<EuiFieldText
id={field.id}
fullWidth
value={field.value || ''}
onChange={(event) => onChange(field.id, event.target.value)}
data-test-subj={field.testSubj}
/>
</EuiFormRow>
<EuiSpacer size="s" />
</>
)}
</div>
))}

View file

@ -57,7 +57,7 @@ export const GCP_CREDENTIALS_TYPE = {
type SetupFormatGCP = typeof GCP_SETUP_ACCESS.CLOUD_SHELL | typeof GCP_SETUP_ACCESS.MANUAL;
export const GCPSetupInfoContent = () => (
export const GCPSetupInfoContent = ({ isAgentless }: { isAgentless: boolean }) => (
<>
<EuiHorizontalRule margin="xl" />
<EuiTitle size="xs">
@ -70,12 +70,20 @@ export const GCPSetupInfoContent = () => (
</EuiTitle>
<EuiSpacer size="l" />
<EuiText color={'subdued'} size="s">
<FormattedMessage
id="xpack.csp.gcpIntegration.setupInfoContent"
defaultMessage="The integration will need elevated access to run some CIS benchmark rules. Select your preferred
method of providing the GCP credentials this integration will use. You can follow these
{isAgentless ? (
<FormattedMessage
id="xpack.csp.gcpIntegration.agentlessSetupInfoContent"
defaultMessage="The integration will need elevated access to run some CIS benchmark rules.You can follow these
step-by-step instructions to generate the necessary credentials."
/>
/>
) : (
<FormattedMessage
id="xpack.csp.gcpIntegration.setupInfoContent"
defaultMessage="The integration will need elevated access to run some CIS benchmark rules. Select your preferred
method of providing the GCP credentials this integration will use. You can follow these
step-by-step instructions to generate the necessary credentials."
/>
)}
</EuiText>
</>
);
@ -467,7 +475,7 @@ export const GcpCredentialsForm = ({
}
return (
<>
<GCPSetupInfoContent />
<GCPSetupInfoContent isAgentless={false} />
<EuiSpacer size="l" />
<RadioGroup
disabled={disabled}

View file

@ -6,7 +6,7 @@
*/
import React from 'react';
import { EuiButton, EuiCallOut, EuiSpacer } from '@elastic/eui';
import { EuiButton, EuiCallOut, EuiCodeBlock, EuiLink, EuiSpacer, EuiText } from '@elastic/eui';
import semverCompare from 'semver/functions/compare';
import semverValid from 'semver/functions/valid';
import { FormattedMessage } from '@kbn/i18n-react';
@ -20,6 +20,7 @@ import {
TEMPLATE_URL_ACCOUNT_TYPE_ENV_VAR,
} from '../../../../common/constants';
import {
GCPSetupInfoContent,
GcpFormProps,
GcpInputVarFields,
gcpField,
@ -29,6 +30,126 @@ import { getPosturePolicy } from '../utils';
import { ReadDocumentation } from '../aws_credentials_form/aws_credentials_form';
import { cspIntegrationDocsNavigation } from '../../../common/navigation/constants';
const GoogleCloudShellCredentialsGuide = (props: {
commandText: string;
isOrganization?: boolean;
}) => {
const GOOGLE_CLOUD_SHELL_EXTERNAL_DOC_URL = 'https://cloud.google.com/shell/docs';
const Link = ({ children, url }: { children: React.ReactNode; url: string }) => (
<EuiLink
href={url}
target="_blank"
rel="noopener nofollow noreferrer"
data-test-subj="externalLink"
>
{children}
</EuiLink>
);
return (
<>
<EuiSpacer size="xs" />
<EuiText size="s" color="subdued">
<FormattedMessage
id="xpack.csp.googleCloudShellCredentials.guide.description"
defaultMessage="The Google Cloud Shell Command below will generate a Service Account Credentials JSON key to set up access for assessing your GCP environment's security posture. Learn more about {learnMore}."
values={{
learnMore: (
<Link url={GOOGLE_CLOUD_SHELL_EXTERNAL_DOC_URL}>
<FormattedMessage
id="xpack.csp.googleCloudShellCredentials.guide.learnMoreLinkText"
defaultMessage="Google Cloud Shell"
/>
</Link>
),
}}
/>
<EuiSpacer size="m" />
<EuiText size="s" color="subdued">
<ol>
<li>
<FormattedMessage
id="xpack.csp.googleCloudShellCredentials.guide.steps.launch"
defaultMessage="Log into your {googleCloudConsole}"
values={{
googleCloudConsole: <strong>Google Cloud Console</strong>,
}}
/>
</li>
<EuiSpacer size="xs" />
<li>
<>
{props?.isOrganization ? (
<FormattedMessage
id="xpack.csp.googleCloudShellCredentials.guide.steps.copyWithOrgId"
defaultMessage="Replace <PROJECT_ID> and <ORG_ID_VALUE> in the following command with your project ID and organization ID then copy the command"
ignoreTag
/>
) : (
<FormattedMessage
id="xpack.csp.googleCloudShellCredentials.guide.steps.copyWithProjectId"
defaultMessage="Replace <PROJECT_ID> in the following command with your project ID then copy the command"
ignoreTag
/>
)}
<EuiSpacer size="m" />
<EuiCodeBlock language="bash" isCopyable contentEditable="true">
{props.commandText}
</EuiCodeBlock>
</>
</li>
<EuiSpacer size="xs" />
<li>
<FormattedMessage
id="xpack.csp.googleCloudShellCredentials.guide.steps.cloudShellButton"
defaultMessage="Click the {cloudShellButton} button above and login into your account"
values={{
cloudShellButton: <strong>Launch Google Cloud Shell</strong>,
}}
ignoreTag
/>
</li>
<EuiSpacer size="xs" />
<li>
<FormattedMessage
id="xpack.csp.googleCloudShellCredentials.guide.steps.confirmation"
defaultMessage="Check {trustRepo} and click {confirmButton}"
values={{
confirmButton: <strong>Confirm</strong>,
trustRepo: <em>Trust Repo</em>,
}}
ignoreTag
/>
</li>
<EuiSpacer size="xs" />
<li>
<FormattedMessage
id="xpack.csp.googleCloudShellCredentials.guide.steps.runCloudShellScript"
defaultMessage="Paste and run command in the {googleCloudShell} terminal"
values={{
googleCloudShell: <strong>Google Cloud Shell</strong>,
}}
ignoreTag
/>
</li>
<EuiSpacer size="xs" />
<li>
<FormattedMessage
id="xpack.csp.googleCloudShellCredentials.guide.steps.copyJsonServiceKey"
defaultMessage="Run {catCommand} to view the service account key. Copy and paste Credentials JSON below"
values={{
catCommand: <code>cat KEY_FILE.json</code>,
}}
ignoreTag
/>
</li>
</ol>
</EuiText>
</EuiText>
</>
);
};
export const GcpCredentialsFormAgentless = ({
input,
newPolicy,
@ -64,14 +185,19 @@ export const GcpCredentialsFormAgentless = ({
SUPPORTED_TEMPLATES_URL_FROM_PACKAGE_INFO_INPUT_VARS.CLOUD_SHELL_URL
)?.replace(TEMPLATE_URL_ACCOUNT_TYPE_ENV_VAR, accountType);
const commandText = `gcloud config set project ${
isOrganization ? `<PROJECT_ID> && ORD_ID=<ORG_ID_VALUE>` : `<PROJECT_ID>`
} && ./deploy_service_account.sh`;
return (
<>
<GCPSetupInfoContent isAgentless={true} />
<EuiSpacer size="m" />
{!showCloudCredentialsButton && (
<>
<EuiCallOut color="warning">
<FormattedMessage
id="xpack.csp.fleetIntegration.gcpCloudCredentials.cloudFormationSupportedMessage"
id="xpack.csp.cspIntegration.gcpCloudCredentials.cloudFormationSupportedMessage"
defaultMessage="Launch Cloud Shell for automated credentials not supported in current integration version. Please upgrade to the latest version to enable Launch Cloud Shell for automated credentials."
/>
</EuiCallOut>
@ -80,6 +206,11 @@ export const GcpCredentialsFormAgentless = ({
)}
{showCloudCredentialsButton && (
<>
<GoogleCloudShellCredentialsGuide
isOrganization={isOrganization}
commandText={commandText}
/>
<EuiSpacer size="m" />
<EuiButton
data-test-subj="launchGoogleCloudShellAgentlessButton"
target="_blank"

View file

@ -378,31 +378,19 @@ export function useOnSubmit({
const hasGoogleCloudShell = data?.item ? getCloudShellUrlFromPackagePolicy(data.item) : false;
if (hasFleetAddAgentsPrivileges) {
if (hasAzureArmTemplate) {
setFormState(agentCount ? 'SUBMITTED' : 'SUBMITTED_AZURE_ARM_TEMPLATE');
} else {
setFormState(agentCount ? 'SUBMITTED' : 'SUBMITTED_NO_AGENTS');
}
if (hasCloudFormation) {
setFormState(agentCount ? 'SUBMITTED' : 'SUBMITTED_CLOUD_FORMATION');
} else {
setFormState(agentCount ? 'SUBMITTED' : 'SUBMITTED_NO_AGENTS');
}
if (hasGoogleCloudShell) {
setFormState(agentCount ? 'SUBMITTED' : 'SUBMITTED_GOOGLE_CLOUD_SHELL');
} else {
setFormState(agentCount ? 'SUBMITTED' : 'SUBMITTED_NO_AGENTS');
}
} else {
if (agentCount > 0) {
setFormState('SUBMITTED');
return;
}
if (!error) {
setSavedPackagePolicy(data!.item);
const promptForAgentEnrollment =
!(agentCount && agentPolicies.length > 0) && hasFleetAddAgentsPrivileges;
!(agentCount && agentPolicies.length > 0) &&
!isAgentlessPackagePolicy(data!.item) &&
hasFleetAddAgentsPrivileges;
if (promptForAgentEnrollment && hasAzureArmTemplate) {
setFormState('SUBMITTED_AZURE_ARM_TEMPLATE');
return;

View file

@ -14438,7 +14438,6 @@
"xpack.csp.fleetIntegration.gcpAccountType.gcpSingleAccountDescription": "Un déploiement vers un seul projet suffit pour une démonstration de faisabilité initiale. Pour garantir une couverture complète, il est fortement recommandé de déployer la gestion du niveau de sécurité du cloud au niveau de l'organisation, ce qui connecte automatiquement tous les projets (actuels et futurs).",
"xpack.csp.fleetIntegration.gcpAccountType.gcpSingleAccountLabel": "Compte unique",
"xpack.csp.fleetIntegration.gcpAccountTypeDescriptionLabel": "Sélectionnez un compte unique ou une organisation, puis saisissez un nom et une description pour permettre l'identification de cette intégration.",
"xpack.csp.fleetIntegration.gcpCloudCredentials.cloudFormationSupportedMessage": "La fonctionnalité Lancer Cloud Shell pour obtenir les informations d'identification de façon automatisée nest pas pris en charge dans la version d'intégration actuelle. Veuillez effectuer une mise à niveau vers la dernière version pour activer Lancer Cloud Shell pour les informations d'identification automatisées.",
"xpack.csp.fleetIntegration.integrationDescriptionLabel": "Description",
"xpack.csp.fleetIntegration.integrationNameLabel": "Nom",
"xpack.csp.fleetIntegration.integrationSettingsTitle": "Paramètres de l'intégration",

View file

@ -14427,7 +14427,6 @@
"xpack.csp.fleetIntegration.gcpAccountType.gcpSingleAccountDescription": "1つのプロジェクトへのデプロイは、最初のPOCに適しています。完全に対応するためには、組織レベルでCSPMをデプロイし、すべてのプロジェクト現在と将来の両方を自動的に接続することを強くお勧めします。",
"xpack.csp.fleetIntegration.gcpAccountType.gcpSingleAccountLabel": "1つのアカウント",
"xpack.csp.fleetIntegration.gcpAccountTypeDescriptionLabel": "1つのアカウントまたは組織のいずれかを選択し、この統合を識別するための名前と説明を入力します。",
"xpack.csp.fleetIntegration.gcpCloudCredentials.cloudFormationSupportedMessage": "Launch Cloud ShellLaunch Cloud Formation for Automated Credentialsは、現在の統合バージョンではサポートされていません。Launch Cloud Shell for Automated Credentialsを有効化するには、最新バージョンにアップグレードしてください。",
"xpack.csp.fleetIntegration.integrationDescriptionLabel": "説明",
"xpack.csp.fleetIntegration.integrationNameLabel": "名前",
"xpack.csp.fleetIntegration.integrationSettingsTitle": "統合設定",

View file

@ -14449,7 +14449,6 @@
"xpack.csp.fleetIntegration.gcpAccountType.gcpSingleAccountDescription": "部署到单个项目适用于初始 POC。为确保全面覆盖强烈建议在组织级别部署 CSPM这会自动连接所有项目当前和未来。",
"xpack.csp.fleetIntegration.gcpAccountType.gcpSingleAccountLabel": "单个帐户",
"xpack.csp.fleetIntegration.gcpAccountTypeDescriptionLabel": "选择单个帐户或组织,然后填写名称和描述以帮助标识此集成。",
"xpack.csp.fleetIntegration.gcpCloudCredentials.cloudFormationSupportedMessage": "当前集成版本不支持为自动化凭据启动 Cloud Shell。请升级到最新版本以启用为自动化凭据启动 Cloud Shell。",
"xpack.csp.fleetIntegration.integrationDescriptionLabel": "描述",
"xpack.csp.fleetIntegration.integrationNameLabel": "名称",
"xpack.csp.fleetIntegration.integrationSettingsTitle": "集成设置",