Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi
| iot | |
| kinesisFirehose | kinesisStream, s3, iamRole |
| kinesisStream | kinesisFirehose |
| kms | cloudtrail, cloudwatchLog, codebuild, ecsCluster, efs, eksCluster, elastiCacheReplicationGroup, elasticSearchDomain, emrCluster, lambda, rdsClusterSnapshot, sns, sageMakerNotebookInstance, dmsReplicationInstance, redshiftCluster, rdsCluster |
| lambda | appSync, cognitoUserPool, kms, securityGroup, subnet, vpc, iamRole |
| kms | cloudtrail, cloudwatchLog, codebuild, ecsCluster, efs, eksCluster, elastiCacheReplicationGroup, elasticSearchDomain, emrCluster, lambda, rdsClusterSnapshot, sns, sageMakerNotebookInstance, secretsManager, dmsReplicationInstance, redshiftCluster, rdsCluster |
| lambda | appSync, cognitoUserPool, kms, secretsManager, securityGroup, subnet, vpc, iamRole |
| managedAirflow | iamRole, securityGroups, subnet, s3 |
| nacl | vpc |
| natGateway | networkInterface, subnet, vpc |
Expand All @@ -148,7 +148,7 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi
| sageMakerNotebookInstance | iamRole, kms, networkInterface, subnet, securityGroup |
| sageMakerProject | |
| s3 | cloudfront, cloudtrail, ecsCluster, kinesisFirehose, managedAirflow |
| secretsManager | |
| secretsManager | kms, lambda |
| securityGroup | alb, asg, clientVpnEndpoint, codebuild, dmsReplicationInstance, ecsService, lambda, ec2, elasticSearchDomain, elb, rdsCluster, rdsDbInstance, eksCluster, elastiCacheCluster, managedAirflow, sageMakerNotebookInstance |
| ses | |
| sns | kms, cloudtrail, cloudwatch |
Expand Down
1 change: 1 addition & 0 deletions src/properties/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ export default {
gettingRotationStatus: 'Checking rotation status for each key...',
gettingPolicies: 'Fetching default Policy for each key...',
gettingTags: 'Fetching Tags for each key...',
gettingAliases: 'Fetching Aliases for each key...',

/**
* EKS
Expand Down
47 changes: 47 additions & 0 deletions src/services/kms/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import isEmpty from 'lodash/isEmpty'
import { AWSError, Request } from 'aws-sdk'
import { Config } from 'aws-sdk/lib/config'
import KMS, {
AliasListEntry,
KeyListEntry,
KeyMetadata,
ListKeysRequest,
Expand Down Expand Up @@ -33,6 +34,7 @@ export type AwsKms = KeyListEntry &
policy: string
Tags: TagMap
keyRotationEnabled: boolean
Aliases?: AliasListEntry[]
}

export default async ({
Expand All @@ -50,6 +52,7 @@ export default async ({
const regionPromises = []
const policyPromises = []
const tagPromises = []
const aliasesPromises = []

/**
* Step 1) for all regions, list the kms keys
Expand Down Expand Up @@ -331,6 +334,50 @@ export default async ({
})

await Promise.all(tagPromises)

/**
* Step 6) get aliases for each key
*/

logger.debug(lt.gettingAliases)

kmsData.map(({ region, KeyId }, idx) => {
const kms = new KMS({ ...config, region, endpoint })

const aliasesPromise = new Promise<void>(resolveAliases =>
kms.listAliases({ KeyId }, (err, data) => {
if (err) {
errorLog.generateAwsErrorLog({
functionName: 'kms:listAliases',
err,
})
resolveAliases()
}

/**
* No aliases data
*/

if (isEmpty(data)) {
return resolveAliases()
}

/**
* Add the aliases to the key
*/

const { Aliases: aliases } = data || {}

kmsData[idx].Aliases = aliases

resolveAliases()
})
)

aliasesPromises.push(aliasesPromise)
})

await Promise.all(aliasesPromises)
errorLog.reset()

resolve(groupBy(kmsData, 'region'))
Expand Down
28 changes: 23 additions & 5 deletions src/services/kms/format.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
import t from '../../properties/translations'
import { AliasListEntry } from 'aws-sdk/clients/kms'
import cuid from 'cuid'
import { AwsKms } from './data'
import { AwsKms as AwsKmsType } from '../../types/generated'
import { AwsKms as AwsKmsType, AwsKmsAliasListEntry } from '../../types/generated'
import { formatTagsFromMap, formatIamJsonPolicy } from '../../utils/format'

export const formatAliases = (
aliases?: AliasListEntry[]
): AwsKmsAliasListEntry[] => {
return (
aliases?.map(a => ({
id: cuid(),
aliasName: a.AliasName,
aliasArn: a.AliasArn,
targetKeyId: a.TargetKeyId,
creationDate: a.CreationDate?.toISOString(),
lastUpdatedDate: a.LastUpdatedDate?.toISOString(),
})) || []
)
}

/**
* KMS
*/
Expand Down Expand Up @@ -32,6 +48,7 @@ export default ({
Origin: origin,
DeletionDate: deletionDate,
ValidTo: validTo,
Aliases: aliases = []
} = key

return {
Expand All @@ -47,10 +64,11 @@ export default ({
keyState,
customerMasterKeySpec,
tags: formatTagsFromMap(Tags),
creationDate: creationDate ? creationDate.toString() : undefined,
creationDate: creationDate?.toISOString(),
keyManager,
origin,
deletionDate: deletionDate ? deletionDate.toString() : undefined,
validTo: validTo ? validTo.toString() : undefined,
deletionDate: deletionDate?.toISOString(),
validTo: validTo?.toISOString(),
aliases: formatAliases(aliases),
}
}
22 changes: 19 additions & 3 deletions src/services/kms/schema.graphql
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
type awsKmsAliasListEntry
@generate(
query: { get: false, query: true, aggregate: false }
mutation: { add: false, delete: false }
subscription: false
) {
id: String! @id
aliasName: String @search(by: [hash, regexp])
aliasArn: String @search(by: [hash, regexp])
targetKeyId: String @search(by: [hash, regexp])
creationDate: DateTime @search(by: [day])
lastUpdatedDate: DateTime @search(by: [day])
}

type awsKms implements awsBaseService @key(fields: "id") {
description: String @search(by: [hash, regexp, fulltext])
keyRotationEnabled: Boolean @search
Expand All @@ -7,11 +21,12 @@ type awsKms implements awsBaseService @key(fields: "id") {
keyState: String @search(by: [hash, regexp])
customerMasterKeySpec: String @search(by: [hash, regexp])
tags: [awsRawTag]
creationDate: String @search(by: [hash, regexp])
creationDate: DateTime @search(by: [day])
keyManager: String @search(by: [hash, regexp])
origin: String @search(by: [hash, regexp])
deletionDate: String @search(by: [hash, regexp])
validTo: String @search(by: [hash, regexp])
deletionDate: DateTime @search(by: [day])
validTo: DateTime @search(by: [day])
aliases: [awsKmsAliasListEntry]
lambda: [awsLambda] @hasInverse(field: kms) #change to plural
cloudtrail: [awsCloudtrail] @hasInverse(field: kms) #change to plural
redshiftCluster: [awsRedshiftCluster] @hasInverse(field: kms) #change to plural
Expand All @@ -28,6 +43,7 @@ type awsKms implements awsBaseService @key(fields: "id") {
sageMakerNotebookInstances: [awsSageMakerNotebookInstance]
@hasInverse(field: kms)
rdsClusterSnapshots: [awsRdsClusterSnapshot] @hasInverse(field: kms)
secretsManager: [awsSecretsManager] @hasInverse(field: kms)
ecsCluster: [awsEcsCluster] @hasInverse(field: kms)
dynamodb: [awsDynamoDbTable] @hasInverse(field: kms)
cognitoUserPools: [awsCognitoUserPool] @hasInverse(field: kms)
Expand Down
1 change: 1 addition & 0 deletions src/services/lambda/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type awsLambda implements awsBaseService @key(fields: "arn") {
vpc: [awsVpc] @hasInverse(field: lambda)
cognitoUserPools: [awsCognitoUserPool] @hasInverse(field: lambdas)
appSync: [awsAppSync] @hasInverse(field: lambda)
secretsManager: [awsSecretsManager] @hasInverse(field: lambda)
iamRole: [awsIamRole] @hasInverse(field: lambda)
}

Expand Down
84 changes: 84 additions & 0 deletions src/services/secretsManager/connections.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { ServiceConnection } from '@cloudgraph/sdk'
import { isEmpty } from 'lodash'
import { RawAwsSecretsManager } from './data'
import { AwsKms } from '../kms/data'
import { RawAwsLambdaFunction } from '../lambda/data'
import services from '../../enums/services'

export default ({
service,
data,
region,
}: {
account: string
service: RawAwsSecretsManager
data: Array<{ name: string; data: { [property: string]: any[] } }>
region: string
}): {
[property: string]: ServiceConnection[]
} => {
const connections: ServiceConnection[] = []
const {
ARN: id,
KmsKeyId: kmsKeyId,
RotationLambdaARN: rotationLambdaARN,
ReplicationStatus: replicationStatus,
} = service

/**
* Find KMS
* related to this Secrets Manager
*/
const kmsKeyIds: string[] = replicationStatus?.map(rs => rs.KmsKeyId)
const kmsKeys = data.find(({ name }) => name === services.kms)
if (kmsKeys?.data?.[region]) {
const kmsKeyInRegion: AwsKms[] = kmsKeys.data[region].filter(
({ Arn: arn, KeyId: keyId, Aliases: aliases = [] }: AwsKms) =>
kmsKeyId === arn ||
kmsKeyIds?.includes(arn) ||
kmsKeyIds?.includes(keyId) ||
aliases?.some(a => kmsKeyIds?.includes(a.AliasName))
)

if (!isEmpty(kmsKeyInRegion)) {
for (const kms of kmsKeyInRegion) {
const { KeyId: keyId }: AwsKms = kms
connections.push({
id: keyId,
resourceType: services.kms,
relation: 'child',
field: 'kms',
})
}
}
}

/**
* Find Lambda Functions
* related to this Secrets Manager
*/
const lambdas = data.find(({ name }) => name === services.lambda)

if (rotationLambdaARN && lambdas?.data?.[region]) {
const lambdaInRegion: RawAwsLambdaFunction = lambdas.data[region].find(
({ FunctionArn: functionArn }: RawAwsLambdaFunction) =>
rotationLambdaARN === functionArn
)

if (lambdaInRegion) {
const { FunctionArn: functionArn }: RawAwsLambdaFunction = lambdaInRegion

connections.push({
id: functionArn,
resourceType: services.lambda,
relation: 'child',
field: 'lambda',
})
}
}

const snsResult = {
[id]: connections,
}
return snsResult
}
50 changes: 48 additions & 2 deletions src/services/secretsManager/data.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import SM, { SecretListEntry } from 'aws-sdk/clients/secretsmanager'
import SM, {
SecretListEntry,
DescribeSecretResponse,
ReplicationStatusType,
} from 'aws-sdk/clients/secretsmanager'
import { AWSError } from 'aws-sdk/lib/error'
import CloudGraph from '@cloudgraph/sdk'
import groupBy from 'lodash/groupBy'
Expand All @@ -22,8 +26,33 @@ const endpoint = initTestEndpoint(serviceName)
export interface RawAwsSecretsManager extends Omit<SecretListEntry, 'Tags'> {
region: string
Tags: TagMap
ReplicationStatus?: ReplicationStatusType[]
}

const getSecretDetails = async (
sm: SM,
secretId: string
): Promise<DescribeSecretResponse> =>
new Promise(resolve => {
sm.describeSecret(
{
SecretId: secretId,
},
(err: AWSError, data: DescribeSecretResponse) => {
if (err) {
errorLog.generateAwsErrorLog({
functionName: 'sm:describeSecret',
err,
})
}
if (!isEmpty(data)) {
resolve(data)
}
resolve({})
}
)
})

export default async ({
regions,
config,
Expand All @@ -34,6 +63,7 @@ export default async ({
new Promise(async resolve => {
const smData: RawAwsSecretsManager[] = []
const regionPromises = []
const secretsDetailsPromises = []

regions.split(',').map(region => {
const regionPromise = new Promise<void>(resolveSecretsManagerData => {
Expand Down Expand Up @@ -73,8 +103,24 @@ export default async ({
})
regionPromises.push(regionPromise)
})

await Promise.all(regionPromises)

smData.map(({ ARN: arn, region }, idx) => {
const sm = new SM({ ...config, region, endpoint })
const secretDetailsPromise = new Promise<void>(
async resolveSecretDetails => {
const secretDetails: DescribeSecretResponse = await getSecretDetails(
sm,
arn
)
smData[idx].ReplicationStatus = secretDetails?.ReplicationStatus || []
resolveSecretDetails()
}
)
secretsDetailsPromises.push(secretDetailsPromise)
})
await Promise.all(secretsDetailsPromises)

errorLog.reset()

resolve(groupBy(smData, 'region'))
Expand Down
Loading