Skip to content

Commit 19c75b0

Browse files
authored
Merge pull request #164 from rbclark/detect-active-regions
Detect and scan all active (opted-in) regions if no region list is provided
2 parents a1ee5fa + 8902339 commit 19c75b0

File tree

2 files changed

+78
-41
lines changed

2 files changed

+78
-41
lines changed

src/services/base/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ export interface rawDataInterface {
1414
accountId?: string
1515
data: any
1616
}
17+
18+
export interface rawDataResponseInterface {
19+
rawData: rawDataInterface[]
20+
regions: string[]
21+
}
22+
1723
export default class BaseService {
1824
constructor(config: any) {
1925
this.logger = config.logger

src/services/index.ts

Lines changed: 72 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import relations from '../enums/relations'
2424
import { Credentials } from '../types'
2525
import { obfuscateSensitiveString } from '../utils/format'
2626
import { checkAndMergeConnections } from '../utils'
27-
import { Account, rawDataInterface } from './base'
27+
import { Account, rawDataInterface, rawDataResponseInterface } from './base'
2828
import enhancers, { EnhancerConfig } from './base/enhancers'
2929
import AwsErrorLog from '../utils/errorLog'
3030

@@ -53,28 +53,35 @@ export default class Provider extends CloudGraph.Client {
5353

5454
private properties: {
5555
services: { [key: string]: string }
56-
regions: string[]
5756
resources: { [key: string]: string }
5857
}
5958

60-
logSelectedAccessRegionsAndResources(
59+
logSelectedAccessResources(
6160
profilesOrRolesToLog: string[],
62-
regionsToLog: string,
63-
resourcesToLog: string
61+
resourcesToLog: string,
6462
): void {
6563
this.logger.info(
6664
`Profiles and role ARNs configured: ${chalk.green(
6765
profilesOrRolesToLog.join(', ')
6866
)}`
6967
)
70-
this.logger.info(
71-
`Regions configured: ${chalk.green(regionsToLog.replace(/,/g, ', '))}`
72-
)
7368
this.logger.info(
7469
`Resources configured: ${chalk.green(resourcesToLog.replace(/,/g, ', '))}`
7570
)
7671
}
7772

73+
logSelectedRegions(regions: string | undefined): void {
74+
if(regions) {
75+
this.logger.info(
76+
`Regions configured: ${chalk.green(regions.replace(/,/g, ', '))}`
77+
)
78+
} else {
79+
this.logger.info(
80+
'Active regions will be detected automatically during the scan.'
81+
)
82+
}
83+
}
84+
7885
// TODO: update to also support ignorePrompts config
7986
async configure(): Promise<{ [key: string]: any }> {
8087
const { flags = {}, cloudGraphConfig, ...providerSettings } = this.config
@@ -178,12 +185,10 @@ export default class Provider extends CloudGraph.Client {
178185
])
179186
this.logger.debug(`Regions selected: ${regionsAnswer}`)
180187
if (!regionsAnswer.length) {
188+
result.regions = undefined
181189
this.logger.info(
182-
`No Regions selected, using default region: ${chalk.green(
183-
DEFAULT_REGION
184-
)}`
190+
`No Regions selected, active regions will be detected automatically.`
185191
)
186-
result.regions = DEFAULT_REGION
187192
} else {
188193
result.regions = regionsAnswer.join(',')
189194
}
@@ -218,11 +223,11 @@ export default class Provider extends CloudGraph.Client {
218223
'AWS'
219224
)} configuration successfully completed ${confettiBall}`
220225
)
221-
this.logSelectedAccessRegionsAndResources(
226+
this.logSelectedAccessResources(
222227
result.accounts.map(acct => acct.roleArn ?? acct.profile),
223-
result.regions,
224228
result.resources
225229
)
230+
this.logSelectedRegions(result.regions)
226231

227232
return result
228233
}
@@ -573,15 +578,9 @@ export default class Provider extends CloudGraph.Client {
573578
private async getRawData(
574579
account: Account,
575580
opts?: Opts
576-
): Promise<rawDataInterface[]> {
577-
let { regions: configuredRegions, resources: configuredResources } =
578-
this.config
581+
): Promise<rawDataResponseInterface> {
582+
let { resources: configuredResources } = this.config
579583
const result: rawDataInterface[] = []
580-
if (!configuredRegions) {
581-
configuredRegions = this.properties.regions.join(',')
582-
} else {
583-
configuredRegions = [...new Set(configuredRegions.split(','))].join(',')
584-
}
585584
if (!configuredResources) {
586585
configuredResources = Object.values(this.properties.services).join(',')
587586
}
@@ -591,6 +590,7 @@ export default class Provider extends CloudGraph.Client {
591590

592591
const config = await this.getAwsConfig(account)
593592
const { accountId } = await this.getIdentity(account)
593+
const configuredRegions = await this.getRegions(account)
594594
for (const resource of resourceNames) {
595595
const serviceClass = this.getService(resource)
596596
if (serviceClass && serviceClass.getData) {
@@ -622,7 +622,7 @@ export default class Provider extends CloudGraph.Client {
622622
}
623623
}
624624
this.logger.success(`Account: ${accountId} scan completed`)
625-
return result
625+
return {rawData: result, regions: configuredRegions.split(',')}
626626
}
627627

628628
private enhanceData({ data, ...config }: EnhancerConfig): ProviderData {
@@ -655,7 +655,7 @@ export default class Provider extends CloudGraph.Client {
655655
entities: [],
656656
connections: {},
657657
}
658-
let { regions: configuredRegions, resources: configuredResources } =
658+
let { resources: configuredResources } =
659659
this.config
660660
const {
661661
accounts: configuredAccounts,
@@ -664,25 +664,18 @@ export default class Provider extends CloudGraph.Client {
664664
accounts: Account[]
665665
cloudGraphConfig: { ignoreEnvVariables: boolean }
666666
} = this.config
667-
if (!configuredRegions) {
668-
configuredRegions = this.properties.regions.join(',')
669-
} else {
670-
configuredRegions = [...new Set(configuredRegions.split(','))].join(',')
671-
}
672667
if (!configuredResources) {
673668
configuredResources = Object.values(this.properties.services).join(',')
674669
}
675-
676670
const usingEnvCreds = !!process.env.AWS_ACCESS_KEY_ID && !ignoreEnvVariables
677671

678-
this.logSelectedAccessRegionsAndResources(
672+
this.logSelectedAccessResources(
679673
usingEnvCreds
680674
? [ENV_VAR_CREDS_LOG]
681675
: configuredAccounts.map(acct => {
682676
return acct.roleArn || acct.profile
683677
}),
684-
configuredRegions,
685-
configuredResources
678+
configuredResources,
686679
)
687680

688681
// Leaving this here in case we need to test another service or to inject a logging function
@@ -692,6 +685,7 @@ export default class Provider extends CloudGraph.Client {
692685
// data so we can pass along accountId
693686
// TODO: find a better way to handle this
694687
let mergedRawData: rawDataInterface[] = []
688+
let relevantRegions = new Set<string>()
695689
const globalRegion = 'aws-global'
696690
const tags = { className: 'Tag', name: 'tag', data: { [globalRegion]: [] } }
697691
const accounts = {
@@ -701,10 +695,13 @@ export default class Provider extends CloudGraph.Client {
701695
}
702696
// If the user has passed aws creds as env variables, dont use profile list
703697
if (usingEnvCreds) {
704-
rawData = await this.getRawData(
698+
let {rawData: results, regions: activeRegions} = await this.getRawData(
705699
{ profile: 'default', roleArn: undefined, externalId: undefined },
706700
opts
707701
)
702+
703+
rawData = results
704+
relevantRegions = new Set([...relevantRegions, ...activeRegions])
708705
} else {
709706
const crawledAccounts = []
710707
for (const account of configuredAccounts) {
@@ -721,13 +718,14 @@ export default class Provider extends CloudGraph.Client {
721718
}
722719
}
723720
const { accountId } = await this.getIdentity(account)
724-
accounts.data[globalRegion].push({
725-
id: accountId,
726-
regions: configuredRegions.split(','),
727-
})
728721
if (!crawledAccounts.find(val => val === accountId)) {
729722
crawledAccounts.push(accountId)
730-
const newRawData = await this.getRawData(account, opts)
723+
const {rawData: newRawData, regions} = await this.getRawData(account, opts)
724+
relevantRegions = new Set([...relevantRegions, ...regions])
725+
accounts.data[globalRegion].push({
726+
id: accountId,
727+
regions: regions,
728+
})
731729
mergedRawData = this.mergeRawData(mergedRawData, newRawData)
732730
rawData = [...rawData, ...newRawData]
733731
} else {
@@ -797,7 +795,7 @@ export default class Provider extends CloudGraph.Client {
797795
if (typeof serviceClass.getConnections === 'function') {
798796
// We need to loop through all configured regions here because services can be connected to things in another region
799797
let serviceConnections = {}
800-
for (const connectionRegion of configuredRegions.split(',')) {
798+
for (const connectionRegion of relevantRegions) {
801799
const connections = serviceClass.getConnections({
802800
service,
803801
region: connectionRegion,
@@ -865,10 +863,43 @@ export default class Provider extends CloudGraph.Client {
865863

866864
const enhancedData = this.enhanceData({
867865
accounts: accounts.data[globalRegion],
868-
configuredRegions,
866+
configuredRegions: [...relevantRegions].join(','),
869867
rawData: mergedRawData,
870868
data: result,
871869
})
872870
return { ...enhancedData, errors: AwsErrorLog.errorsHistory }
873871
}
872+
873+
/* Determine the list of regions to scan */
874+
private async getRegions(account: Account): Promise<string> {
875+
let { regions: configuredRegions } = this.config
876+
if (configuredRegions) {
877+
configuredRegions = [...new Set(configuredRegions.split(','))].join(',')
878+
} else {
879+
configuredRegions = (await this.getActiveRegions(account)).join(',')
880+
}
881+
882+
this.logger.info(
883+
`Regions configured for profile ${account.profile}: ${chalk.green(configuredRegions.replace(/,/g, ', '))}`
884+
)
885+
886+
return configuredRegions
887+
}
888+
889+
/* Fetch the list of regions that are active for the account */
890+
private async getActiveRegions(account: Account): Promise<string[]> {
891+
const config = await this.getAwsConfig(account)
892+
// Assume a default region in order to get the list of enabled regions
893+
config.region = DEFAULT_REGION
894+
const ec2 = new AWS.EC2(config)
895+
const regions = await ec2
896+
.describeRegions({
897+
AllRegions: false,
898+
})
899+
.promise()
900+
901+
const activeRegions = await regions.Regions.map(({ RegionName }) => RegionName)
902+
903+
return activeRegions
904+
}
874905
}

0 commit comments

Comments
 (0)