mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Cloud Security] Aws credentials cleanup (#163528)
## Summary The various unused credential methods supported by both the KSPM->EKS and CSPM->AWS methods are not cleared out when a package is saved. As there are currently two components on the frontend which allow the user to specify their aws credential method, I've added hooks for both the 'packagePolicyCreate' and 'packagePolicyUpdate' methods in fleet to the CSP serverside plugin. Both these hooks will pass the policy to a cleanCredentials function which checks the 'aws.credentials.type' var to determine which fields should be cleared out. ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
This commit is contained in:
parent
eb3002eb65
commit
0651662159
8 changed files with 228 additions and 12 deletions
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { PostureTypes, VulnSeverity } from './types';
|
||||
import { PostureTypes, VulnSeverity, AwsCredentialsTypeFieldMap } from './types';
|
||||
|
||||
export const STATUS_ROUTE_PATH = '/internal/cloud_security_posture/status';
|
||||
export const STATUS_API_CURRENT_VERSION = '1';
|
||||
|
@ -125,5 +125,14 @@ export const VULNERABILITIES_SEVERITY: Record<VulnSeverity, VulnSeverity> = {
|
|||
};
|
||||
|
||||
export const VULNERABILITIES_ENUMERATION = 'CVE';
|
||||
|
||||
export const AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP: AwsCredentialsTypeFieldMap = {
|
||||
assume_role: ['role_arn'],
|
||||
direct_access_keys: ['access_key_id', 'secret_access_key'],
|
||||
temporary_keys: ['access_key_id', 'secret_access_key', 'session_token'],
|
||||
shared_credentials: ['shared_credential_file', 'credential_profile_name'],
|
||||
cloud_formation: [],
|
||||
};
|
||||
|
||||
export const SETUP_ACCESS_CLOUD_SHELL = 'google_cloud_shell';
|
||||
export const SETUP_ACCESS_MANUAL = 'manual';
|
||||
|
|
|
@ -13,6 +13,17 @@ import { CspRuleTemplate } from './schemas';
|
|||
import { findCspRuleTemplateRequest } from './schemas/csp_rule_template_api/get_csp_rule_template';
|
||||
import { getComplianceDashboardSchema } from './schemas/stats';
|
||||
|
||||
export type AwsCredentialsType =
|
||||
| 'assume_role'
|
||||
| 'direct_access_keys'
|
||||
| 'temporary_keys'
|
||||
| 'shared_credentials'
|
||||
| 'cloud_formation';
|
||||
|
||||
export type AwsCredentialsTypeFieldMap = {
|
||||
[key in AwsCredentialsType]: string[];
|
||||
};
|
||||
|
||||
export type Evaluation = 'passed' | 'failed' | 'NA';
|
||||
|
||||
export type PostureTypes = 'cspm' | 'kspm' | 'vuln_mgmt' | 'all';
|
||||
|
|
|
@ -6,7 +6,11 @@
|
|||
*/
|
||||
|
||||
import { createPackagePolicyMock } from '@kbn/fleet-plugin/common/mocks';
|
||||
import { getBenchmarkFromPackagePolicy, getBenchmarkTypeFilter } from './helpers';
|
||||
import {
|
||||
getBenchmarkFromPackagePolicy,
|
||||
getBenchmarkTypeFilter,
|
||||
cleanupCredentials,
|
||||
} from './helpers';
|
||||
|
||||
describe('test helper methods', () => {
|
||||
it('get default integration type from inputs with multiple enabled types', () => {
|
||||
|
@ -60,4 +64,126 @@ describe('test helper methods', () => {
|
|||
const typeFilter = getBenchmarkTypeFilter('cis_eks');
|
||||
expect(typeFilter).toMatch('csp-rule-template.attributes.metadata.benchmark.id: "cis_eks"');
|
||||
});
|
||||
|
||||
describe('cleanupCredentials', () => {
|
||||
it('cleans unused aws credential methods, except role_arn when using assume_role', () => {
|
||||
const mockPackagePolicy = createPackagePolicyMock();
|
||||
mockPackagePolicy.inputs = [
|
||||
{
|
||||
type: 'cloudbeat/cis_eks',
|
||||
enabled: true,
|
||||
streams: [
|
||||
{
|
||||
id: 'findings',
|
||||
enabled: true,
|
||||
data_stream: {
|
||||
dataset: 'cloud_security_posture.findings',
|
||||
type: 'logs',
|
||||
},
|
||||
vars: {
|
||||
'aws.credentials.type': { value: 'assume_role' },
|
||||
access_key_id: { value: 'unused', type: 'text' },
|
||||
credential_profile_name: { value: 'unused', type: 'text' },
|
||||
role_arn: { value: 'inuse' },
|
||||
secret_access_key: { value: 'unused', type: 'text' },
|
||||
session_token: { value: 'unused', type: 'text' },
|
||||
shared_credential_file: { value: 'unused', type: 'text' },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const cleanedPackage = cleanupCredentials(mockPackagePolicy);
|
||||
expect(cleanedPackage.inputs[0].streams[0].vars).toEqual({
|
||||
'aws.credentials.type': { value: 'assume_role' },
|
||||
access_key_id: { value: undefined, type: 'text' },
|
||||
credential_profile_name: { value: undefined, type: 'text' },
|
||||
role_arn: { value: 'inuse' },
|
||||
secret_access_key: { value: undefined, type: 'text' },
|
||||
session_token: { value: undefined, type: 'text' },
|
||||
shared_credential_file: { value: undefined, type: 'text' },
|
||||
});
|
||||
});
|
||||
|
||||
it('cleans unused aws credential methods, when using cloud formation', () => {
|
||||
const mockPackagePolicy = createPackagePolicyMock();
|
||||
mockPackagePolicy.inputs = [
|
||||
{
|
||||
type: 'cloudbeat/cis_eks',
|
||||
enabled: true,
|
||||
streams: [
|
||||
{
|
||||
id: 'findings',
|
||||
enabled: true,
|
||||
data_stream: {
|
||||
dataset: 'cloud_security_posture.findings',
|
||||
type: 'logs',
|
||||
},
|
||||
vars: {
|
||||
'aws.credentials.type': { value: 'cloud_formation' },
|
||||
access_key_id: { value: 'unused', type: 'text' },
|
||||
credential_profile_name: { value: 'unused', type: 'text' },
|
||||
role_arn: { value: 'unused' },
|
||||
secret_access_key: { value: 'unused', type: 'text' },
|
||||
session_token: { value: 'unused', type: 'text' },
|
||||
shared_credential_file: { value: 'unused', type: 'text' },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const cleanedPackage = cleanupCredentials(mockPackagePolicy);
|
||||
expect(cleanedPackage.inputs[0].streams[0].vars).toEqual({
|
||||
'aws.credentials.type': { value: 'cloud_formation' },
|
||||
access_key_id: { value: undefined, type: 'text' },
|
||||
credential_profile_name: { value: undefined, type: 'text' },
|
||||
role_arn: { value: undefined },
|
||||
secret_access_key: { value: undefined, type: 'text' },
|
||||
session_token: { value: undefined, type: 'text' },
|
||||
shared_credential_file: { value: undefined, type: 'text' },
|
||||
});
|
||||
});
|
||||
|
||||
it('cleans unused aws credential methods, when using direct_access_keys method ', () => {
|
||||
const mockPackagePolicy = createPackagePolicyMock();
|
||||
mockPackagePolicy.inputs = [
|
||||
{
|
||||
type: 'cloudbeat/cis_eks',
|
||||
enabled: true,
|
||||
streams: [
|
||||
{
|
||||
id: 'findings',
|
||||
enabled: true,
|
||||
data_stream: {
|
||||
dataset: 'cloud_security_posture.findings',
|
||||
type: 'logs',
|
||||
},
|
||||
vars: {
|
||||
'aws.credentials.type': { value: 'direct_access_keys' },
|
||||
access_key_id: { value: 'used', type: 'text' },
|
||||
credential_profile_name: { value: 'unused', type: 'text' },
|
||||
role_arn: { value: 'unused' },
|
||||
secret_access_key: { value: 'used', type: 'text' },
|
||||
session_token: { value: 'unused', type: 'text' },
|
||||
shared_credential_file: { value: 'unused', type: 'text' },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const cleanedPackage = cleanupCredentials(mockPackagePolicy);
|
||||
expect(cleanedPackage.inputs[0].streams[0].vars).toEqual({
|
||||
'aws.credentials.type': { value: 'direct_access_keys' },
|
||||
access_key_id: { value: 'used', type: 'text' },
|
||||
credential_profile_name: { value: undefined, type: 'text' },
|
||||
role_arn: { value: undefined },
|
||||
secret_access_key: { value: 'used', type: 'text' },
|
||||
session_token: { value: undefined, type: 'text' },
|
||||
shared_credential_file: { value: undefined, type: 'text' },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,13 +12,15 @@ import {
|
|||
PACKAGE_POLICY_SAVED_OBJECT_TYPE,
|
||||
PackagePolicy,
|
||||
PackagePolicyInput,
|
||||
UpdatePackagePolicy,
|
||||
} from '@kbn/fleet-plugin/common';
|
||||
import {
|
||||
CLOUD_SECURITY_POSTURE_PACKAGE_NAME,
|
||||
CLOUDBEAT_VANILLA,
|
||||
CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE,
|
||||
AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP,
|
||||
} from '../constants';
|
||||
import type { BenchmarkId, Score, BaseCspSetupStatus } from '../types';
|
||||
import type { BenchmarkId, Score, BaseCspSetupStatus, AwsCredentialsType } from '../types';
|
||||
|
||||
/**
|
||||
* @example
|
||||
|
@ -98,3 +100,47 @@ export const getStatusForIndexName = (indexName: string, status?: BaseCspSetupSt
|
|||
|
||||
return 'unknown';
|
||||
};
|
||||
|
||||
export const cleanupCredentials = (packagePolicy: NewPackagePolicy | UpdatePackagePolicy) => {
|
||||
const enabledInput = packagePolicy.inputs.find((i) => i.enabled);
|
||||
const credentialType: AwsCredentialsType | undefined =
|
||||
enabledInput?.streams?.[0].vars?.['aws.credentials.type'].value;
|
||||
|
||||
if (credentialType) {
|
||||
const credsToKeep = AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP[credentialType];
|
||||
const credFields = Object.values(AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP).flat();
|
||||
|
||||
if (credsToKeep) {
|
||||
// we need to return a copy of the policy with the unused
|
||||
// credentials set to undefined
|
||||
return {
|
||||
...packagePolicy,
|
||||
inputs: packagePolicy.inputs.map((input) => {
|
||||
if (input.enabled) {
|
||||
return {
|
||||
...input,
|
||||
streams: input.streams.map((stream) => {
|
||||
const vars = stream.vars;
|
||||
for (const field in vars) {
|
||||
if (!credsToKeep.includes(field) && credFields.includes(field)) {
|
||||
vars[field].value = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...stream,
|
||||
vars,
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
return input;
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// nothing to do, return unmutated policy
|
||||
return packagePolicy;
|
||||
};
|
||||
|
|
|
@ -24,7 +24,6 @@ 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';
|
||||
|
@ -35,6 +34,7 @@ import {
|
|||
NewPackagePolicyPostureInput,
|
||||
} from '../utils';
|
||||
import { SetupFormat, useAwsCredentialsForm } from './hooks';
|
||||
import { AwsCredentialsType } from '../../../../common/types';
|
||||
|
||||
interface AWSSetupInfoContentProps {
|
||||
integrationLink: string;
|
||||
|
|
|
@ -10,6 +10,7 @@ import { EuiText } from '@elastic/eui';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { NewPackagePolicyInput } from '@kbn/fleet-plugin/common';
|
||||
import { AwsCredentialsType } from '../../../../common/types';
|
||||
|
||||
const AssumeRoleDescription = (
|
||||
<div>
|
||||
|
@ -69,13 +70,6 @@ const AWS_FIELD_LABEL = {
|
|||
}),
|
||||
};
|
||||
|
||||
export type AwsCredentialsType =
|
||||
| 'assume_role'
|
||||
| 'direct_access_keys'
|
||||
| 'temporary_keys'
|
||||
| 'shared_credentials'
|
||||
| 'cloud_formation';
|
||||
|
||||
export type AwsCredentialsFields = Record<string, { label: string; type?: 'password' | 'text' }>;
|
||||
|
||||
export interface AwsOptionValue {
|
||||
|
|
|
@ -14,12 +14,12 @@ import {
|
|||
NewPackagePolicyPostureInput,
|
||||
} from '../utils';
|
||||
import {
|
||||
AwsCredentialsType,
|
||||
DEFAULT_MANUAL_AWS_CREDENTIALS_TYPE,
|
||||
getAwsCredentialsFormOptions,
|
||||
getInputVarsFields,
|
||||
} from './get_aws_credentials_form_options';
|
||||
import { CLOUDBEAT_AWS } from '../../../../common/constants';
|
||||
import { AwsCredentialsType } from '../../../../common/types';
|
||||
/**
|
||||
* Update CloudFormation template and stack name in the Agent Policy
|
||||
* based on the selected policy template
|
||||
|
|
|
@ -18,6 +18,7 @@ import type {
|
|||
PostDeletePackagePoliciesResponse,
|
||||
PackagePolicy,
|
||||
NewPackagePolicy,
|
||||
UpdatePackagePolicy,
|
||||
} from '@kbn/fleet-plugin/common';
|
||||
import type {
|
||||
TaskManagerSetupContract,
|
||||
|
@ -25,6 +26,7 @@ import type {
|
|||
} from '@kbn/task-manager-plugin/server';
|
||||
import { isCspPackage } from '../common/utils/helpers';
|
||||
import { isSubscriptionAllowed } from '../common/utils/subscription';
|
||||
import { cleanupCredentials } from '../common/utils/helpers';
|
||||
import type {
|
||||
CspServerPluginSetup,
|
||||
CspServerPluginStart,
|
||||
|
@ -124,6 +126,34 @@ export class CspPlugin
|
|||
}
|
||||
);
|
||||
|
||||
plugins.fleet.registerExternalCallback(
|
||||
'packagePolicyCreate',
|
||||
async (
|
||||
packagePolicy: NewPackagePolicy,
|
||||
soClient: SavedObjectsClientContract
|
||||
): Promise<NewPackagePolicy> => {
|
||||
if (isCspPackage(packagePolicy.package?.name)) {
|
||||
return cleanupCredentials(packagePolicy);
|
||||
}
|
||||
|
||||
return packagePolicy;
|
||||
}
|
||||
);
|
||||
|
||||
plugins.fleet.registerExternalCallback(
|
||||
'packagePolicyUpdate',
|
||||
async (
|
||||
packagePolicy: UpdatePackagePolicy,
|
||||
soClient: SavedObjectsClientContract
|
||||
): Promise<UpdatePackagePolicy> => {
|
||||
if (isCspPackage(packagePolicy.package?.name)) {
|
||||
return cleanupCredentials(packagePolicy);
|
||||
}
|
||||
|
||||
return packagePolicy;
|
||||
}
|
||||
);
|
||||
|
||||
plugins.fleet.registerExternalCallback(
|
||||
'packagePolicyPostCreate',
|
||||
async (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue