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
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi
| dynamodb | appSync |
| dmsReplicationInstance | securityGroup, subnet, vpc, kms |
| ebs | asg, ec2, emrInstance |
| ec2 | alb, asg, ebs, eip, emrInstance, networkInterface, securityGroup, subnet, systemsManagerInstance, vpc, ecsContainer |
| ec2 | alb, asg, ebs, eip, emrInstance, eksCluster, elasticBeanstalkEnv, iamInstanceProfile, iamRole, networkInterface, securityGroup, subnet, systemsManagerInstance, vpc, ecsContainer |
| ecr | |
| ecsCluster | ecsService, ecsTask, ecsTaskSet |
| ecsContainer | ecsTask, ec2 |
Expand All @@ -101,11 +101,11 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi
| efs | kms |
| efsMountTarget | networkInterface, subnet, vpc |
| eip | ec2, networkInterface, vpc |
| eksCluster | iamRole, kms, securityGroup, subnet, vpc |
| eksCluster | ec2, iamRole, kms, securityGroup, subnet, vpc |
| elastiCacheCluster | securityGroup, subnet, vpc |
| elastiCacheReplicationGroup | kms |
| elasticBeanstalkApp | elasticBeanstalkEnv |
| elasticBeanstalkEnv | elasticBeanstalkApp |
| elasticBeanstalkEnv | ec2, elasticBeanstalkApp |
| elasticSearchDomain | kms, securityGroup, subnet, vpc |
| elb | cloudfront, ecsService, securityGroup, subnet, vpc |
| emrCluster | kms, subnet |
Expand All @@ -115,14 +115,14 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi
| glueJob | iamRole |
| glueRegistry | |
| guardDutyDetector | iamRole |
| iamInstanceProfile | iamRole |
| iamInstanceProfile | ec2, iamRole |
| iamPasswordPolicy | |
| iamSamlProvider | |
| iamOpenIdConnectProvider | |
| iamServerCertificate | |
| iamUser | iamGroup |
| iamPolicy | iamRole, iamGroup |
| iamRole | codebuild, configurationRecorder, iamInstanceProfile, iamPolicy, eksCluster, ecsService, flowLog, glueJob, managedAirflow, sageMakerNotebookInstance, systemsManagerInstance guardDutyDetector |
| iamRole | codebuild, configurationRecorder, ec2, iamInstanceProfile, iamPolicy, eksCluster, ecsService, flowLog, glueJob, managedAirflow, sageMakerNotebookInstance, systemsManagerInstance guardDutyDetector |
| iamGroup | iamUser, iamPolicy |
| igw | vpc |
| iot | |
Expand Down
1 change: 1 addition & 0 deletions src/enums/relations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export default {
route53HostedZone: ['route53Record'],
emrCluster: ['emrInstance', 'emrStep'],
ecsService: ['ecsTaskSet', 'ecsTaskDefinition'],
iamInstanceProfile: ['ec2Instance'],
}
131 changes: 120 additions & 11 deletions src/services/ec2/connections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ import { RawAwsSubnet } from '../subnet/data'
import { RawAwsEcsContainer } from '../ecsContainer/data'
import { RawAwsSystemsManagerInstance } from '../systemsManagerInstance/data'
import { ssmManagedInstanceArn } from '../../utils/generateArns'
import { RawAwsElasticBeanstalkEnv } from '../elasticBeanstalkEnvironment/data'
import { RawAwsEksCluster } from '../eksCluster/data'
import { getEksClusterName, getElasticBeanstalkEnvId } from './utils'
import { RawAwsInstanceProfile } from '../iamInstanceProfile/data'
import { globalRegionName } from '../../enums/regions'
import { RawAwsIamRole } from '../iamRole/data'

/**
* EC2
Expand All @@ -35,6 +41,7 @@ export default ({
KeyPairName?: string
Tags?: TagList
IamInstanceProfile: IamInstanceProfile
IamRolesArn?: string[]
}
region: string
}): { [key: string]: ServiceConnection[] } => {
Expand All @@ -44,6 +51,9 @@ export default ({
SecurityGroups: instanceSecurityGroups = [],
NetworkInterfaces: instanceNetworkInterfaces = [],
SubnetId: subnetId,
Tags: tags,
IamInstanceProfile: iamInstanceProfile,
IamRolesArn: rolesArn,
} = instance

/**
Expand Down Expand Up @@ -189,17 +199,37 @@ export default ({
id: subnet.SubnetId,
resourceType: services.subnet,
relation: 'child',
field: 'subnet',
field: 'subnets',
})
}
}
}

/**
* Find EKS
* Find EKS cluster
* related to this EC2
*/
// TODO: Implement when eks service is ready
const eksClusterName = getEksClusterName(tags)
const eksClusters: {
name: string
data: { [property: string]: any[] }
} = data.find(({ name }) => name === services.eksCluster)
if (eksClusters?.data?.[region]) {
const eksClustersInRegion: RawAwsEksCluster[] = eksClusters.data[
region
].filter(({ name }: RawAwsEksCluster) => name === eksClusterName)

if (!isEmpty(eksClustersInRegion)) {
for (const eksCluster of eksClustersInRegion) {
connections.push({
id: eksCluster.arn,
resourceType: services.eksCluster,
relation: 'child',
field: 'eksCluster',
})
}
}
}

/**
* Find ECS Container
Expand All @@ -210,9 +240,9 @@ export default ({
data: { [property: string]: any[] }
} = data.find(({ name }) => name === services.ecsContainer)
if (ecsContainers?.data?.[region]) {
const containersInRegion: RawAwsEcsContainer[] = ecsContainers.data[region].filter(
({ ec2InstanceId }) => ec2InstanceId === id
)
const containersInRegion: RawAwsEcsContainer[] = ecsContainers.data[
region
].filter(({ ec2InstanceId }) => ec2InstanceId === id)

if (!isEmpty(containersInRegion)) {
for (const container of containersInRegion) {
Expand All @@ -230,18 +260,24 @@ export default ({
* Find SSM managed instances
* related to this EC2 instance
*/
const instances: {
const instances: {
name: string
data: { [property: string]: any[] }
} = data.find(({ name }) => name === services.systemsManagerInstance)
if (instances?.data?.[region]) {
const dataInRegion: RawAwsSystemsManagerInstance[] = instances.data[region].filter(
const dataInRegion: RawAwsSystemsManagerInstance[] = instances.data[
region
].filter(
({ InstanceId }: RawAwsSystemsManagerInstance) => InstanceId === id
)

if (!isEmpty(dataInRegion)) {
for (const ssmInstance of dataInRegion) {
const arn = ssmManagedInstanceArn({ region, account, name: ssmInstance.InstanceId })
const arn = ssmManagedInstanceArn({
region,
account,
name: ssmInstance.InstanceId,
})
connections.push({
id: arn,
resourceType: services.systemsManagerInstance,
Expand All @@ -254,9 +290,82 @@ export default ({

/**
* Find Elastic Beanstalk
* related to this EC2 loadbalancer
* related to this EC2 instance
*/
const elasticBeanstalkEnvId = getElasticBeanstalkEnvId(tags)
const elasticBeanstalkEnvs: {
name: string
data: { [property: string]: any[] }
} = data.find(({ name }) => name === services.elasticBeanstalkEnv)
if (elasticBeanstalkEnvs?.data?.[region]) {
const elasticBeanstalkEnvsInRegion: RawAwsElasticBeanstalkEnv[] =
elasticBeanstalkEnvs.data[region].filter(
({ EnvironmentId }: RawAwsElasticBeanstalkEnv) =>
elasticBeanstalkEnvId === EnvironmentId
)

if (!isEmpty(elasticBeanstalkEnvsInRegion)) {
for (const elasticBeanstalkEnv of elasticBeanstalkEnvsInRegion) {
connections.push({
id: elasticBeanstalkEnv.EnvironmentId,
resourceType: services.elasticBeanstalkEnv,
relation: 'child',
field: 'elasticBeanstalkEnv',
})
}
}
}

/**
* Find IAM Instance Profiles
* related to this EC2 instance
*/
const iamInstanceProfiles: {
name: string
data: { [property: string]: any[] }
} = data.find(({ name }) => name === services.iamInstanceProfile)
if (iamInstanceProfiles?.data?.[globalRegionName]) {
const dataAtRegion: RawAwsInstanceProfile[] = iamInstanceProfiles.data[
globalRegionName
].filter(instanceProfile => instanceProfile.Arn === iamInstanceProfile?.Arn)
if (!isEmpty(dataAtRegion)) {
for (const iamInstance of dataAtRegion) {
const { InstanceProfileId: instanceProfileId }: RawAwsInstanceProfile = iamInstance

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

/**
* Find IAM Roles
* related to this EC2 instance
*/
// TODO: Implement when eb service is ready
const roles: { name: string; data: { [property: string]: any[] } } =
data.find(({ name }) => name === services.iamRole)

if (roles?.data?.[globalRegionName]) {
const dataAtRegion: RawAwsIamRole[] = roles.data[globalRegionName].filter(
({ Arn }: RawAwsIamRole) => rolesArn?.includes(Arn)
)
if (!isEmpty(dataAtRegion)) {
for (const iamRole of dataAtRegion) {
const { Arn: arn } :RawAwsIamRole = iamRole

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

const ec2Result = {
[id]: connections,
Expand Down
25 changes: 25 additions & 0 deletions src/services/ec2/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import awsLoggerText from '../../properties/logger'
import { initTestEndpoint } from '../../utils'
import AwsErrorLog from '../../utils/errorLog'
import { convertAwsTagsToTagMap } from '../../utils/format'
import getIamInstanceProfiles, {
RawAwsInstanceProfile,
} from '../iamInstanceProfile/data'

const lt = { ...awsLoggerText }
const { logger } = CloudGraph
Expand All @@ -54,6 +57,7 @@ export interface RawAwsEC2 extends Omit<Instance, 'Tags'> {
IamInstanceProfile?: IamInstanceProfile
cloudWatchMetricData?: any
PlatformDetails?: string
IamRolesArn?: string[]
}

/**
Expand All @@ -63,9 +67,11 @@ export interface RawAwsEC2 extends Omit<Instance, 'Tags'> {
export default async ({
regions,
config,
rawData,
}: {
regions: string
config: Config
rawData: any
}): Promise<{
[region: string]: RawAwsEC2[]
}> =>
Expand Down Expand Up @@ -625,6 +631,25 @@ export default async ({
iamInstanceProfile[InstanceId] || {}
})

// populate ec2Instances with the iamRoles Arn
const iamInstancesProfiles: RawAwsInstanceProfile[] =
Object.values(
await getIamInstanceProfiles({
config,
rawData,
})
)?.reduce((acc, val) => acc.concat(val), []) || []

ec2Instances.map(({ IamInstanceProfile: instanceProfile }, ec2Idx) => {
const instance = iamInstancesProfiles.find(
i => i.Arn === instanceProfile?.Arn
)
if (instance) {
ec2Instances[ec2Idx].IamRolesArn =
instance?.Roles?.map(r => r.Arn) || []
}
})

errorLog.reset()

/**
Expand Down
2 changes: 0 additions & 2 deletions src/services/ec2/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export default ({
fingerprint,
tags: keyPairTags = {},
} = {},
IamInstanceProfile: iamInstanceProfile,
cloudWatchMetricData,
PlatformDetails: platformDetails,
InstanceLifecycle: instanceLifecycle,
Expand Down Expand Up @@ -114,7 +113,6 @@ export default ({
sourceDestCheck: sourceDestCheck ? t.yes : t.no,
availabilityZone: placement?.AvailabilityZone || '',
cpuThreadsPerCore: cpuOptions?.ThreadsPerCore || 0,
iamInstanceProfile: iamInstanceProfile?.Arn || '',
deletionProtection: deletionProtection ? t.yes : t.no,
primaryNetworkInterface: networkInterfaceId,
metadataOptions: {
Expand Down
12 changes: 7 additions & 5 deletions src/services/ec2/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ type awsEc2 implements awsBaseService @key(fields: "arn") {
sourceDestCheck: String @search(by: [hash, regexp])
availabilityZone: String @search(by: [hash, regexp])
cpuThreadsPerCore: Int @search
iamInstanceProfile: String @search(by: [hash, regexp]) # TODO: use to make a connection to a iamRole
deletionProtection: String @search(by: [hash, regexp])
dailyCost: awsTotalBillingInfo
primaryNetworkInterface: String @search(by: [hash, regexp])
Expand All @@ -112,9 +111,12 @@ type awsEc2 implements awsBaseService @key(fields: "arn") {
eip: [awsEip] @hasInverse(field: ec2Instance)
networkInterfaces: [awsNetworkInterface] @hasInverse(field: ec2Instance)
securityGroups: [awsSecurityGroup] @hasInverse(field: ec2Instance)
subnet: [awsSubnet] @hasInverse(field: ec2Instance) #change to plural
ecsContainer: [awsEcsContainer] @hasInverse(field: ec2Instance) #change to plural
subnets: [awsSubnet] @hasInverse(field: ec2Instances)
ecsContainer: [awsEcsContainer] @hasInverse(field: ec2Instances)
emrInstance: [awsEmrInstance] @hasInverse(field: ec2Instance)
systemsManagerInstance: [awsSystemsManagerInstance]
@hasInverse(field: ec2Instance)
systemsManagerInstance: [awsSystemsManagerInstance] @hasInverse(field: ec2Instance)
eksCluster: [awsEksCluster] @hasInverse(field: ec2Instances)
elasticBeanstalkEnv: [awsElasticBeanstalkEnv] @hasInverse(field: ec2Instances)
iamInstanceProfile: [awsIamInstanceProfile] @hasInverse(field: ec2Instances)
iamRole: [awsIamRole] @hasInverse(field: ec2Instances)
}
35 changes: 35 additions & 0 deletions src/services/ec2/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Tag } from 'aws-sdk/clients/ec2'
import { isEmpty, last } from 'lodash'

const clusterTag = 'kubernetes.io/cluster/'
const environmentIdTag = 'elasticbeanstalk:environment-id'

export const getEksClusterName = (tags?: Tag[]): string => {
if (isEmpty(tags)) {
return ''
}
let eksClusterName = ''
Object.keys(tags)?.some(key => {
const isMatch = key.includes(clusterTag)
if (isMatch) {
eksClusterName = last(key.split('/'))
}
return isMatch
})
return eksClusterName
}

export const getElasticBeanstalkEnvId = (tags?: Tag[]): string => {
if (isEmpty(tags)) {
return ''
}
let elasticBeanstalkId = ''
Object.keys(tags)?.some(key => {
const isMatch = key.includes(environmentIdTag)
if (isMatch) {
elasticBeanstalkId = tags[key]
}
return isMatch
})
return elasticBeanstalkId
}
Loading