Skip to content

Commit

Permalink
[Cloud Security][Onboarding]GCP Onboarding - Manual (elastic#161913)
Browse files Browse the repository at this point in the history
## Summary

Added basic form for GCP onboarding



https://github.com/elastic/kibana/assets/8703149/e99f0eac-02f4-4f37-a41c-b78825e7e230

<img width="924" alt="Screenshot 2023-07-20 at 3 51 25 PM"
src="https://github.com/elastic/kibana/assets/8703149/7f5783fa-6e29-4cb8-837a-3095c6fb292a">
<img width="849" alt="Screenshot 2023-07-20 at 3 53 25 PM"
src="https://github.com/elastic/kibana/assets/8703149/2ef71e15-3597-4dc8-a97d-40990462d7ce">
<img width="934" alt="Screenshot 2023-07-20 at 3 53 33 PM"
src="https://github.com/elastic/kibana/assets/8703149/a8b14e54-079a-4c23-900a-f0d237568bd2">
  • Loading branch information
animehart authored Jul 24, 2023
1 parent cbe2a09 commit cb04b55
Show file tree
Hide file tree
Showing 7 changed files with 318 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,7 @@ export const cloudPostureIntegrations: CloudPostureIntegrations = {
benchmark: i18n.translate('xpack.csp.cspmIntegration.gcpOption.benchmarkTitle', {
defaultMessage: 'CIS GCP',
}),
disabled: true,
icon: 'logoGCP',
tooltip: i18n.translate('xpack.csp.cspmIntegration.gcpOption.tooltipContent', {
defaultMessage: 'Coming soon',
}),
},
{
type: CLOUDBEAT_AZURE,
Expand Down Expand Up @@ -214,3 +210,4 @@ export const cloudPostureIntegrations: CloudPostureIntegrations = {
},
};
export const FINDINGS_DOCS_URL = 'https://ela.st/findings';
export const MIN_VERSION_GCP_CIS = '1.5.0';
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
/*
* 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, { useEffect, useState } from 'react';
import {
EuiFieldText,
EuiFormRow,
EuiLink,
EuiSpacer,
EuiText,
EuiTitle,
EuiSelect,
EuiForm,
EuiCallOut,
EuiTextArea,
} 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 { i18n } from '@kbn/i18n';
import { RadioGroup } from './csp_boxed_radio_group';
import { getPosturePolicy, NewPackagePolicyPostureInput } from './utils';
import { MIN_VERSION_GCP_CIS } from '../../common/constants';

type SetupFormatGCP = 'google_cloud_shell' | 'manual';
const GCPSetupInfoContent = () => (
<>
<EuiSpacer size="l" />
<EuiTitle size="s">
<h2>
<FormattedMessage
id="xpack.csp.gcpIntegration.setupInfoContentTitle"
defaultMessage="Setup Access"
/>
</h2>
</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
step-by-step instructions to generate the necessary credentials."
/>
</EuiText>
</>
);

/* NEED TO FIND THE REAL URL HERE LATER*/
const DocsLink = (
<EuiText color={'subdued'} size="s">
<FormattedMessage
id="xpack.csp.gcpIntegration.docsLink"
defaultMessage="Read the {docs} for more details"
values={{
docs: (
<EuiLink href="https://cloud.google.com/docs/authentication" external>
documentation
</EuiLink>
),
}}
/>
</EuiText>
);

const CredentialFileText = i18n.translate(
'xpack.csp.findings.gcpIntegration.gcpInputText.credentialFileText',
{ defaultMessage: 'Path to JSON file containing the credentials and key used to subscribe' }
);
const CredentialJSONText = i18n.translate(
'xpack.csp.findings.gcpIntegration.gcpInputText.credentialJSONText',
{ defaultMessage: 'JSON blob containing the credentials and key used to subscribe' }
);

type GcpCredentialsType = 'credentials_file' | 'credentials_json';
type GcpFields = Record<string, { label: string; type?: 'password' | 'text' }>;
interface GcpInputFields {
fields: GcpFields;
}

const gcpField: GcpInputFields = {
fields: {
project_id: {
label: i18n.translate('xpack.csp.gcpIntegration.projectidFieldLabel', {
defaultMessage: 'Project ID',
}),
type: 'text',
},
credentials_file: {
label: i18n.translate('xpack.csp.gcpIntegration.credentialsFileFieldLabel', {
defaultMessage: 'Credentials File',
}),
type: 'text',
},
credentials_json: {
label: i18n.translate('xpack.csp.gcpIntegration.credentialsJSONFieldLabel', {
defaultMessage: 'Credentials JSON',
}),
type: 'text',
},
},
};

const credentialOptionsList = [
{
label: i18n.translate('xpack.csp.gcpIntegration.credentialsFileOption', {
defaultMessage: 'Credentials File',
}),
text: 'Credentials File',
},
{
label: i18n.translate('xpack.csp.gcpIntegration.credentialsjsonOption', {
defaultMessage: 'Credentials JSON',
}),
text: 'Credentials JSON',
},
];

const getSetupFormatOptions = (): Array<{
id: SetupFormatGCP;
label: string;
disabled: boolean;
}> => [
{
id: 'google_cloud_shell',
label: i18n.translate('xpack.csp.gcpIntegration.setupFormatOptions.googleCloudShell', {
defaultMessage: 'Google Cloud Shell',
}),
disabled: true,
},
{
id: 'manual',
label: i18n.translate('xpack.csp.gcpIntegration.setupFormatOptions.manual', {
defaultMessage: 'Manual',
}),
disabled: false,
},
];

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

const getInputVarsFields = (
input: NewPackagePolicyInput,
fields: GcpInputFields[keyof GcpInputFields]
) =>
Object.entries(input.streams[0].vars || {})
.filter(([id]) => id in fields)
.map(([id, inputVar]) => {
const field = fields[id];
return {
id,
label: field.label,
type: field.type || 'text',
value: inputVar.value,
} as const;
});

export const GcpCredentialsForm = ({
input,
newPolicy,
updatePolicy,
packageInfo,
setIsValid,
onChange,
}: Props) => {
const fields = getInputVarsFields(input, gcpField.fields);

useEffect(() => {
const isInvalid = packageInfo.version < MIN_VERSION_GCP_CIS;

setIsValid(!isInvalid);

onChange({
isValid: !isInvalid,
updatedPolicy: newPolicy,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [input, packageInfo]);

if (packageInfo.version < MIN_VERSION_GCP_CIS) {
return (
<>
<EuiSpacer size="l" />
<EuiCallOut color="warning">
<FormattedMessage
id="xpack.csp.gcpIntegration.gcpNotSupportedMessage"
defaultMessage="CIS GCP is not supported on the current Integration version, please upgrade your integration to the latest version to use CIS GCP"
/>
</EuiCallOut>
</>
);
}
return (
<>
<GCPSetupInfoContent />
<EuiSpacer size="l" />
<GcpSetupAccessSelector
onChange={(optionId) => updatePolicy(getPosturePolicy(newPolicy, input.type))}
/>
<EuiSpacer size="l" />
<GcpInputVarFields
fields={fields}
onChange={(key, value) =>
updatePolicy(getPosturePolicy(newPolicy, input.type, { [key]: { value } }))
}
/>
<EuiSpacer size="s" />
{DocsLink}
<EuiSpacer />
</>
);
};

const GcpSetupAccessSelector = ({ onChange }: { onChange(type: GcpCredentialsType): void }) => (
<RadioGroup
size="s"
options={getSetupFormatOptions()}
idSelected={'manual'}
onChange={(id: GcpCredentialsType) => onChange(id)}
/>
);

const GcpInputVarFields = ({
fields,
onChange,
}: {
fields: Array<GcpFields[keyof GcpFields] & { value: string; id: string }>;
onChange: (key: string, value: string) => void;
}) => {
const [credentialOption, setCredentialOption] = useState('Credentials File');
const targetFieldName = (id: string) => {
return fields.find((element) => element.id === id);
};
return (
<div>
<EuiForm component="form">
<EuiFormRow fullWidth label={gcpField.fields.project_id.label}>
<EuiFieldText
id={targetFieldName('project_id')!.id}
fullWidth
value={targetFieldName('project_id')!.value || ''}
onChange={(event) => onChange(targetFieldName('project_id')!.id, event.target.value)}
/>
</EuiFormRow>
<EuiFormRow fullWidth label={'Credentials'}>
<EuiSelect
fullWidth
options={credentialOptionsList}
value={credentialOption}
onChange={(optionElem) => {
setCredentialOption(optionElem.target.value);
}}
/>
</EuiFormRow>
{credentialOption === 'Credentials File' && (
<EuiFormRow fullWidth label={CredentialFileText}>
<EuiFieldText
id={targetFieldName('credentials_file')!.id}
fullWidth
value={targetFieldName('credentials_file')!.value || ''}
onChange={(event) =>
onChange(targetFieldName('credentials_file')!.id, event.target.value)
}
/>
</EuiFormRow>
)}
{credentialOption === 'Credentials JSON' && (
<EuiFormRow fullWidth label={CredentialJSONText}>
<EuiTextArea
id={targetFieldName('credentials_json')!.id}
fullWidth
value={targetFieldName('credentials_json')!.value || ''}
onChange={(event) =>
onChange(targetFieldName('credentials_json')!.id, event.target.value)
}
/>
</EuiFormRow>
)}
</EuiForm>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ describe('<CspPolicyTemplateForm />', () => {
expect(option2).toBeInTheDocument();
expect(option3).toBeInTheDocument();
expect(option1).toBeEnabled();
expect(option2).toBeDisabled();
expect(option2).toBeEnabled();
expect(option3).toBeDisabled();
expect(option1).toBeChecked();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { getPolicyTemplateInputOptions, type NewPackagePolicyPostureInput } from
import { RadioGroup } from './csp_boxed_radio_group';
import { AwsCredentialsForm } from './aws_credentials_form/aws_credentials_form';
import { EksCredentialsForm } from './eks_credentials_form';
import { GcpCredentialsForm } from './gcp_credential_form';

interface PolicyTemplateSelectorProps {
selectedTemplate: CloudSecurityPolicyTemplate;
Expand Down Expand Up @@ -79,6 +80,8 @@ export const PolicyTemplateVarsForm = ({ input, ...props }: PolicyTemplateVarsFo
return <AwsCredentialsForm {...props} input={input} />;
case 'cloudbeat/cis_eks':
return <EksCredentialsForm {...props} input={input} />;
case 'cloudbeat/cis_gcp':
return <GcpCredentialsForm {...props} input={input} />;
default:
return null;
}
Expand Down
8 changes: 6 additions & 2 deletions x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -2152,6 +2152,12 @@
"data.sessions.management.flyoutTitle": "Inspecter la session de recherche",
"data.triggers.applyFilterDescription": "Lorsque le filtre Kibana est appliqué. Peut être un filtre simple ou un filtre de plage.",
"data.triggers.applyFilterTitle": "Appliquer le filtre",
"savedSearch.kibana_context.filters.help": "Spécifier des filtres génériques Kibana",
"savedSearch.kibana_context.help": "Met à jour le contexte général de Kibana.",
"savedSearch.kibana_context.q.help": "Spécifier une recherche en texte libre Kibana",
"savedSearch.kibana_context.savedSearchId.help": "Spécifier l'ID de recherche enregistrée à utiliser pour les requêtes et les filtres",
"savedSearch.kibana_context.timeRange.help": "Spécifier le filtre de plage temporelle Kibana",
"savedSearch.legacyURLConflict.errorMessage": "Cette recherche a la même URL qu'un alias hérité. Désactiver l'alias pour résoudre cette erreur : {json}",
"dataViews.deprecations.scriptedFieldsMessage": "Vous avez {numberOfIndexPatternsWithScriptedFields} vues de données ({titlesPreview}…) qui utilisent des champs scriptés. Les champs scriptés sont déclassés et seront supprimés à l'avenir. Utilisez plutôt des champs d'exécution.",
"dataViews.fetchFieldErrorTitle": "Erreur lors de l'extraction des champs pour la vue de données {title} (ID : {id})",
"dataViews.aliasLabel": "Alias",
Expand Down Expand Up @@ -4991,7 +4997,6 @@
"savedObjectsManagement.view.savedObjectProblemErrorMessage": "Un problème est survenu avec cet objet enregistré.",
"savedObjectsManagement.view.savedSearchDoesNotExistErrorMessage": "La recherche enregistrée associée à cet objet n'existe plus.",
"savedSearch.legacyURLConflict.errorMessage": "Cette recherche a la même URL qu'un alias hérité. Désactiver l'alias pour résoudre cette erreur : {json}",
"savedSearch.contentManagementType": "Recherche enregistrée",
"securitySolutionPackages.dataTable.eventsTab.unit": "{totalCount, plural, =1 {alerte} one {alertes} many {alertes} other {alertes}}",
"securitySolutionPackages.dataTable.unit": "{totalCount, plural, =1 {alerte} one {alertes} many {alertes} other {alertes}}",
"securitySolutionPackages.ecsDataQualityDashboard.allTab.allFieldsTableTitle": "Tous les champs – {indexName}",
Expand Down Expand Up @@ -11442,7 +11447,6 @@
"xpack.csp.cspmIntegration.azureOption.tooltipContent": "Bientôt disponible",
"xpack.csp.cspmIntegration.gcpOption.benchmarkTitle": "CIS GCP",
"xpack.csp.cspmIntegration.gcpOption.nameTitle": "GCP",
"xpack.csp.cspmIntegration.gcpOption.tooltipContent": "Bientôt disponible",
"xpack.csp.cspmIntegration.integration.nameTitle": "Gestion du niveau de sécurité du cloud",
"xpack.csp.cspmIntegration.integration.shortNameTitle": "CSPM",
"xpack.csp.dashboard.benchmarkSection.clusterTitleTooltip.clusterPrefixTitle": "Afficher tous les résultats pour ",
Expand Down
7 changes: 6 additions & 1 deletion x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -2166,6 +2166,12 @@
"data.sessions.management.flyoutTitle": "検索セッションの検査",
"data.triggers.applyFilterDescription": "Kibanaフィルターが適用されるとき。単一の値または範囲フィルターにすることができます。",
"data.triggers.applyFilterTitle": "フィルターを適用",
"savedSearch.kibana_context.filters.help": "Kibana ジェネリックフィルターを指定します",
"savedSearch.kibana_context.help": "Kibana グローバルコンテキストを更新します",
"savedSearch.kibana_context.q.help": "自由形式の Kibana テキストクエリを指定します",
"savedSearch.kibana_context.savedSearchId.help": "クエリとフィルターに使用する保存検索ID を指定します。",
"savedSearch.kibana_context.timeRange.help": "Kibana 時間範囲フィルターを指定します",
"savedSearch.legacyURLConflict.errorMessage": "この検索にはレガシーエイリアスと同じURLがあります。このエラーを解決するには、エイリアスを無効にしてください:{json}",
"dataViews.deprecations.scriptedFieldsMessage": "スクリプト化されたフィールドを使用する{numberOfIndexPatternsWithScriptedFields}データビュー({titlesPreview}...)があります。スクリプト化されたフィールドは廃止予定であり、今後は削除されます。ランタイムフィールドを使用してください。",
"dataViews.fetchFieldErrorTitle": "データビューのフィールド取得中にエラーが発生 {title}(ID:{id})",
"dataViews.aliasLabel": "エイリアス",
Expand Down Expand Up @@ -11456,7 +11462,6 @@
"xpack.csp.cspmIntegration.azureOption.tooltipContent": "まもなくリリース",
"xpack.csp.cspmIntegration.gcpOption.benchmarkTitle": "CIS GCP",
"xpack.csp.cspmIntegration.gcpOption.nameTitle": "GCP",
"xpack.csp.cspmIntegration.gcpOption.tooltipContent": "まもなくリリース",
"xpack.csp.cspmIntegration.integration.nameTitle": "クラウドセキュリティ態勢管理",
"xpack.csp.cspmIntegration.integration.shortNameTitle": "CSPM",
"xpack.csp.dashboard.benchmarkSection.clusterTitleTooltip.clusterPrefixTitle": "すべての調査結果を表示 ",
Expand Down
Loading

0 comments on commit cb04b55

Please sign in to comment.