UNPKG

@cloud-copilot/iam-lens

Version:

Visibility in IAM in and across AWS accounts

136 lines 6.31 kB
import { convertAssumedRoleArnToRoleArn, splitArnParts } from '@cloud-copilot/iam-utils'; import { isArnPrincipal, isServicePrincipal } from '../principals.js'; export const knownContextKeys = [ 'aws:SecureTransport', 'aws:CurrentTime', 'aws:EpochTime', 'aws:PrincipalArn', 'aws:PrincipalAccount', 'aws:PrincipalOrgId', 'aws:PrincipalOrgPaths', 'aws:PrincipalType', 'aws:userid', 'aws:username', 'aws:ResourceAccount', 'aws:ResourceOrgID', 'aws:ResourceOrgPaths', 'aws:PrincipalIsAWSService', 'aws:PrincipalServiceName', 'aws:SourceAccount', 'aws:SourceOrgID', 'aws:SourceOrgPaths' ]; /** * Get the context keys for a simulation request. * * @param collectClient the collect client to use for fetching data * @param simulationRequest the simulation request to create context keys for * @param contextKeyOverrides the context key overrides to apply * @returns a promise that resolves to the context keys for the simulation request */ export async function createContextKeys(collectClient, simulationRequest, contextKeyOverrides) { const result = { 'aws:SecureTransport': 'true', 'aws:CurrentTime': new Date().toISOString(), 'aws:EpochTime': Math.floor(Date.now() / 1000).toString() }; if (isArnPrincipal(simulationRequest.principal)) { result['aws:PrincipalArn'] = simulationRequest.principal; const arnParts = splitArnParts(simulationRequest.principal); const principalAccountId = arnParts.accountId; result['aws:PrincipalAccount'] = arnParts.accountId || ''; const orgId = await collectClient.getOrgIdForAccount(principalAccountId); if (orgId) { result['aws:PrincipalOrgId'] = orgId; const orgStructure = await collectClient.getOrgUnitHierarchyForAccount(principalAccountId); result['aws:PrincipalOrgPaths'] = [`${orgId}/${orgStructure.join('/')}/`]; } const tags = await collectClient.getTagsForResource(simulationRequest.principal, principalAccountId); for (const [key, value] of Object.entries(tags)) { result[`aws:PrincipalTag/${key}`] = value; } result['aws:PrincipalIsAWSService'] = 'false'; if (simulationRequest.principal.endsWith(':root')) { result['aws:PrincipalType'] = 'Account'; result['aws:userid'] = principalAccountId; } else if (arnParts.resourceType === 'user') { result['aws:PrincipalType'] = 'User'; const userUniqueId = await collectClient.getUniqueIdForIamResource(simulationRequest.principal); result['aws:userid'] = userUniqueId || 'UNKNOWN'; const userName = arnParts.resourcePath?.split('/').at(-1); result['aws:username'] = userName; } else if (arnParts.resourceType === 'federated-user') { result['aws:PrincipalType'] = 'FederatedUser'; result['aws:userid'] = `${arnParts.accountId}:${arnParts.resourcePath}`; } else if (arnParts.resourceType === 'assumed-role') { result['aws:PrincipalType'] = 'AssumedRole'; const sessionName = arnParts.resourcePath?.split('/').at(-1); const roleArn = convertAssumedRoleArnToRoleArn(simulationRequest.principal); const roleUniqueId = await collectClient.getUniqueIdForIamResource(roleArn); result['aws:userid'] = `${roleUniqueId || 'UNKNOWN'}:${sessionName}`; } } //Resource context keys if (!isAwsResourceInfoExcludedAction(simulationRequest.action)) { result['aws:ResourceAccount'] = simulationRequest.resourceAccount; const resourceOrgId = await collectClient.getOrgIdForAccount(simulationRequest.resourceAccount); if (resourceOrgId) { result['aws:ResourceOrgID'] = resourceOrgId; const orgStructure = await collectClient.getOrgUnitHierarchyForAccount(simulationRequest.resourceAccount); result['aws:ResourceOrgPaths'] = [`${resourceOrgId}/${orgStructure.join('/')}/`]; } } if (simulationRequest.resourceArn) { const resourceTags = await collectClient.getTagsForResource(simulationRequest.resourceArn, simulationRequest.resourceAccount); for (const [key, value] of Object.entries(resourceTags)) { result[`aws:ResourceTag/${key}`] = value; } } //Service Principal context keys if (isServicePrincipal(simulationRequest.principal)) { result['aws:PrincipalIsAWSService'] = 'true'; result['aws:PrincipalServiceName'] = simulationRequest.principal; result['aws:SourceAccount'] = simulationRequest.resourceAccount; result['aws:SourceOrgID'] = result['aws:ResourceOrgID']; result['aws:SourceOrgPaths'] = result['aws:ResourceOrgPaths']; } //Apply any custom context key overrides for (const [key, value] of Object.entries(contextKeyOverrides)) { result[key] = value; } return result; } const awsResourceInfoExcludedActions = new Set([ 'auditmanager:updateassessmentframeworkshare', 'detective:acceptinvitation', 'ds:acceptshareddirectory', 'ec2:accepttransitgatewaypeeringattachment', 'ec2:acceptvpcendpointconnections', 'ec2:acceptvpcpeeringconnection', 'ec2:copysnapshot', 'ec2:createtransitgatewaypeeringattachment', 'ec2:createvpcendpoint', 'ec2:createvpcpeeringconnection', 'ec2:deletetransitgatewaypeeringattachment', 'ec2:deletevpcpeeringconnection', 'ec2:rejecttransitgatewaypeeringattachment', 'ec2:rejectvpcendpointconnections', 'ec2:rejectvpcpeeringconnection', 'guardduty:acceptadministratorinvitation', 'macie2:acceptinvitation', 'es:acceptinboundconnection', 'route53:associatevpcwithhostedzone', 'route53:createvpcassociationauthorization', 'route53:deletevpcassociationauthorization', 'route53:disassociatevpcfromhostedzone', 'route53:listhostedzonesbyvpc', 'securityhub:acceptadministratorinvitation' ]); function isAwsResourceInfoExcludedAction(action) { const lowerCaseAction = action.toLowerCase(); return lowerCaseAction.startsWith('ebs:') || awsResourceInfoExcludedActions.has(lowerCaseAction); } //# sourceMappingURL=contextKeys.js.map