Skip to content

Commit 16db963

Browse files
author
Romain Marcadier
authored
fix(region-info): incorrect codedeploy service principals (#18505)
In "special" regions (GovCloud, US-ISO), the CodeDeploy service principal uses the `amazonaws.com` DNS suffix. In fact, that is true of all regions, in all partitions, with the notable exception of `aws-cn` which uses the `amazonaws.com.cn` suffix instead. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 36d356d commit 16db963

File tree

6 files changed

+112
-84
lines changed

6 files changed

+112
-84
lines changed

packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,15 @@ describe('CodeDeploy Lambda DeploymentGroup', () => {
8787
Action: 'sts:AssumeRole',
8888
Effect: 'Allow',
8989
Principal: {
90-
Service: { 'Fn::Join': ['', ['codedeploy.', { Ref: 'AWS::Region' }, '.', { Ref: 'AWS::URLSuffix' }]] },
90+
Service: {
91+
'Fn::FindInMap': [
92+
'ServiceprincipalMap',
93+
{
94+
Ref: 'AWS::Region',
95+
},
96+
'codedeploy',
97+
],
98+
},
9199
},
92100
}],
93101
Version: '2012-10-17',

packages/@aws-cdk/region-info/lib/aws-entities.ts

+16-17
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
// Rule prefix
2-
const RULE_ = 'RULE_';
3-
41
/**
52
* After this point, SSM only creates regional principals
63
*/
7-
export const RULE_SSM_PRINCIPALS_ARE_REGIONAL = `${RULE_}SSM_PRINCIPALS_ARE_REGIONAL`;
4+
export const RULE_SSM_PRINCIPALS_ARE_REGIONAL = Symbol('SSM_PRINCIPALS_ARE_REGIONAL');
85

96
/**
107
* After this point, S3 website domains look like `s3-website.REGION.s3.amazonaws.com`
118
*
129
* Before this point, S3 website domains look like `s3-website-REGION.s3.amazonaws.com`.
1310
*/
14-
export const RULE_S3_WEBSITE_REGIONAL_SUBDOMAIN = `${RULE_}S3_WEBSITE_REGIONAL_SUBDOMAIN`;
11+
export const RULE_S3_WEBSITE_REGIONAL_SUBDOMAIN = Symbol('S3_WEBSITE_REGIONAL_SUBDOMAIN');
1512

1613
/**
1714
* List of AWS region, ordered by launch date (oldest to newest)
@@ -21,13 +18,13 @@ export const RULE_S3_WEBSITE_REGIONAL_SUBDOMAIN = `${RULE_}S3_WEBSITE_REGIONAL_S
2118
* regions are left as-is.
2219
*
2320
* We mix the list of regions with a list of rules that were introduced over
24-
* time (rules are strings starting with `RULE_`).
21+
* time (rules are symbols).
2522
*
2623
* Therefore, if we want to know if a rule applies to a certain region, we
2724
* only need to check its position in the list and compare it to when a
2825
* rule was introduced.
2926
*/
30-
export const AWS_REGIONS_AND_RULES = [
27+
export const AWS_REGIONS_AND_RULES: readonly (string | symbol)[] = [
3128
'us-east-1', // US East (N. Virginia)
3229
'eu-west-1', // Europe (Ireland)
3330
'us-west-1', // US West (N. California)
@@ -68,15 +65,15 @@ export const AWS_REGIONS_AND_RULES = [
6865
* Not in the list ==> no built-in data for that region.
6966
*/
7067
export const AWS_REGIONS = AWS_REGIONS_AND_RULES
71-
.filter((x) => !x.startsWith(RULE_))
72-
.sort();
68+
.filter((x) => typeof x === 'string')
69+
.sort() as readonly string[];
7370

7471
/**
7572
* Possibly non-exaustive list of all service names, used to locate service principals.
7673
*
7774
* Not in the list ==> default service principal mappings.
7875
*/
79-
export const AWS_SERVICES = [
76+
export const AWS_SERVICES: readonly string[] = [
8077
'application-autoscaling',
8178
'autoscaling',
8279
'codedeploy',
@@ -96,10 +93,10 @@ export const AWS_SERVICES = [
9693
*
9794
* Unknown region => we have to assume no.
9895
*/
99-
export function before(region: string, ruleOrRegion: string) {
96+
export function before(region: string, ruleOrRegion: string | symbol) {
10097
const ruleIx = AWS_REGIONS_AND_RULES.indexOf(ruleOrRegion);
10198
if (ruleIx === -1) {
102-
throw new Error(`Unknown rule: ${ruleOrRegion}`);
99+
throw new Error(`Unknown rule: ${String(ruleOrRegion)}`);
103100
}
104101
const regionIx = AWS_REGIONS_AND_RULES.indexOf(region);
105102
return regionIx === -1 ? false : regionIx < ruleIx;
@@ -108,17 +105,19 @@ export function before(region: string, ruleOrRegion: string) {
108105
/**
109106
* Return all regions before a given rule was introduced (or region)
110107
*/
111-
export function regionsBefore(ruleOrRegion: string): string[] {
108+
export function regionsBefore(ruleOrRegion: string | symbol): string[] {
112109
const ruleIx = AWS_REGIONS_AND_RULES.indexOf(ruleOrRegion);
113110
if (ruleIx === -1) {
114-
throw new Error(`Unknown rule: ${ruleOrRegion}`);
111+
throw new Error(`Unknown rule: ${String(ruleOrRegion)}`);
115112
}
116-
return AWS_REGIONS_AND_RULES.filter((_, i) => i < ruleIx).sort();
113+
return AWS_REGIONS_AND_RULES.slice(0, ruleIx)
114+
.filter((entry) => typeof entry === 'string')
115+
.sort() as string[];
117116
}
118117

119-
export interface Region { partition: string, domainSuffix: string }
118+
export interface Region { readonly partition: string, readonly domainSuffix: string }
120119

121-
const PARTITION_MAP: { [region: string]: Region } = {
120+
const PARTITION_MAP: {readonly [region: string]: Region } = {
122121
'default': { partition: 'aws', domainSuffix: 'amazonaws.com' },
123122
'cn-': { partition: 'aws-cn', domainSuffix: 'amazonaws.com.cn' },
124123
'us-gov-': { partition: 'aws-us-gov', domainSuffix: 'amazonaws.com' },

packages/@aws-cdk/region-info/lib/default.ts

+74-60
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ export class Default {
2323
* @param urlSuffix deprecated and ignored.
2424
*/
2525
public static servicePrincipal(serviceFqn: string, region: string, urlSuffix: string): string {
26-
const service = extractSimpleName(serviceFqn);
27-
if (!service) {
26+
const serviceName = extractSimpleName(serviceFqn);
27+
if (!serviceName) {
2828
// Return "service" if it does not look like any of the following:
2929
// - s3
3030
// - s3.amazonaws.com
@@ -34,72 +34,86 @@ export class Default {
3434
return serviceFqn;
3535
}
3636

37-
// Exceptions for Service Principals in us-iso-*
38-
const US_ISO_EXCEPTIONS = new Set([
39-
'cloudhsm',
40-
'config',
41-
'states',
42-
'workspaces',
43-
]);
44-
45-
// Account for idiosyncratic Service Principals in `us-iso-*` regions
46-
if (region.startsWith('us-iso-') && US_ISO_EXCEPTIONS.has(service)) {
47-
switch (service) {
48-
// Services with universal principal
49-
case ('states'):
50-
return `${service}.amazonaws.com`;
51-
52-
// Services with a partitional principal
53-
default:
54-
return `${service}.${urlSuffix}`;
37+
function determineConfiguration(service: string): (service: string, region: string, urlSuffix: string) => string {
38+
function universal(s: string) { return `${s}.amazonaws.com`; };
39+
function partitional(s: string, _: string, u: string) { return `${s}.${u}`; };
40+
function regional(s: string, r: string) { return `${s}.${r}.amazonaws.com`; };
41+
function regionalPartitional(s: string, r: string, u: string) { return `${s}.${r}.${u}`; };
42+
43+
// Exceptions for Service Principals in us-iso-*
44+
const US_ISO_EXCEPTIONS = new Set([
45+
'cloudhsm',
46+
'config',
47+
'states',
48+
'workspaces',
49+
]);
50+
51+
// Account for idiosyncratic Service Principals in `us-iso-*` regions
52+
if (region.startsWith('us-iso-') && US_ISO_EXCEPTIONS.has(service)) {
53+
switch (service) {
54+
// Services with universal principal
55+
case ('states'):
56+
return universal;
57+
58+
// Services with a partitional principal
59+
default:
60+
return partitional;
61+
}
5562
}
56-
}
5763

58-
// Exceptions for Service Principals in us-isob-*
59-
const US_ISOB_EXCEPTIONS = new Set([
60-
'dms',
61-
'states',
62-
]);
64+
// Exceptions for Service Principals in us-isob-*
65+
const US_ISOB_EXCEPTIONS = new Set([
66+
'dms',
67+
'states',
68+
]);
69+
70+
// Account for idiosyncratic Service Principals in `us-isob-*` regions
71+
if (region.startsWith('us-isob-') && US_ISOB_EXCEPTIONS.has(service)) {
72+
switch (service) {
73+
// Services with universal principal
74+
case ('states'):
75+
return universal;
76+
77+
// Services with a partitional principal
78+
default:
79+
return partitional;
80+
}
81+
}
6382

64-
// Account for idiosyncratic Service Principals in `us-isob-*` regions
65-
if (region.startsWith('us-isob-') && US_ISOB_EXCEPTIONS.has(service)) {
6683
switch (service) {
67-
// Services with universal principal
68-
case ('states'):
69-
return `${service}.amazonaws.com`;
84+
// SSM turned from global to regional at some point
85+
case 'ssm':
86+
return before(region, RULE_SSM_PRINCIPALS_ARE_REGIONAL)
87+
? universal
88+
: regional;
89+
90+
// CodeDeploy is regional+partitional in CN, only regional everywhere else
91+
case 'codedeploy':
92+
return region.startsWith('cn-')
93+
? regionalPartitional
94+
: regional;
95+
96+
// Services with a regional AND partitional principal
97+
case 'logs':
98+
return regionalPartitional;
99+
100+
// Services with a regional principal
101+
case 'states':
102+
return regional;
70103

71104
// Services with a partitional principal
72-
default:
73-
return `${service}.${urlSuffix}`;
74-
}
75-
}
76-
77-
// SSM turned from global to regional at some point
78-
if (service === 'ssm') {
79-
return before(region, RULE_SSM_PRINCIPALS_ARE_REGIONAL)
80-
? `${service}.amazonaws.com`
81-
: `${service}.${region}.amazonaws.com`;
82-
}
83-
84-
switch (service) {
85-
// Services with a regional AND partitional principal
86-
case 'codedeploy':
87-
case 'logs':
88-
return `${service}.${region}.${urlSuffix}`;
89-
90-
// Services with a regional principal
91-
case 'states':
92-
return `${service}.${region}.amazonaws.com`;
105+
case 'ec2':
106+
return partitional;
93107

94-
// Services with a partitional principal
95-
case 'ec2':
96-
return `${service}.${urlSuffix}`;
108+
// Services with a universal principal across all regions/partitions (the default case)
109+
default:
110+
return universal;
97111

98-
// Services with a universal principal across all regions/partitions (the default case)
99-
default:
100-
return `${service}.amazonaws.com`;
112+
}
113+
};
101114

102-
}
115+
const configuration = determineConfiguration(serviceName);
116+
return configuration(serviceName, region, urlSuffix);
103117
}
104118

105119
private constructor() { }
@@ -108,4 +122,4 @@ export class Default {
108122
function extractSimpleName(serviceFqn: string) {
109123
const matches = serviceFqn.match(/^([^.]+)(?:(?:\.amazonaws\.com(?:\.cn)?)|(?:\.c2s\.ic\.gov)|(?:\.sc2s\.sgov\.gov))?$/);
110124
return matches ? matches[1] : undefined;
111-
}
125+
}

packages/@aws-cdk/region-info/lib/fact.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ export class Fact {
99
* may not be an exhaustive list of all available AWS regions.
1010
*/
1111
public static get regions(): string[] {
12-
return AWS_REGIONS;
12+
// Return by copy to ensure no modifications can be made to the undelying constant.
13+
return Array.from(AWS_REGIONS);
1314
}
1415

1516
/**

packages/@aws-cdk/region-info/test/__snapshots__/region-info.test.js.snap

+3-3
Original file line numberDiff line numberDiff line change
@@ -795,7 +795,7 @@ Object {
795795
"servicePrincipals": Object {
796796
"application-autoscaling": "application-autoscaling.amazonaws.com",
797797
"autoscaling": "autoscaling.amazonaws.com",
798-
"codedeploy": "codedeploy.us-iso-east-1.c2s.ic.gov",
798+
"codedeploy": "codedeploy.us-iso-east-1.amazonaws.com",
799799
"ec2": "ec2.c2s.ic.gov",
800800
"events": "events.amazonaws.com",
801801
"lambda": "lambda.amazonaws.com",
@@ -826,7 +826,7 @@ Object {
826826
"servicePrincipals": Object {
827827
"application-autoscaling": "application-autoscaling.amazonaws.com",
828828
"autoscaling": "autoscaling.amazonaws.com",
829-
"codedeploy": "codedeploy.us-iso-west-1.c2s.ic.gov",
829+
"codedeploy": "codedeploy.us-iso-west-1.amazonaws.com",
830830
"ec2": "ec2.c2s.ic.gov",
831831
"events": "events.amazonaws.com",
832832
"lambda": "lambda.amazonaws.com",
@@ -857,7 +857,7 @@ Object {
857857
"servicePrincipals": Object {
858858
"application-autoscaling": "application-autoscaling.amazonaws.com",
859859
"autoscaling": "autoscaling.amazonaws.com",
860-
"codedeploy": "codedeploy.us-isob-east-1.sc2s.sgov.gov",
860+
"codedeploy": "codedeploy.us-isob-east-1.amazonaws.com",
861861
"ec2": "ec2.sc2s.sgov.gov",
862862
"events": "events.amazonaws.com",
863863
"lambda": "lambda.amazonaws.com",

packages/@aws-cdk/region-info/test/default.test.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ const urlSuffix = '.nowhere.null';
55

66
describe('servicePrincipal', () => {
77
for (const suffix of ['', '.amazonaws.com', '.amazonaws.com.cn']) {
8-
for (const service of ['states', 'ssm']) {
8+
for (const service of ['codedeploy', 'states', 'ssm']) {
99
test(`${service}${suffix}`, () => {
1010
expect(Default.servicePrincipal(`${service}${suffix}`, region, urlSuffix)).toBe(`${service}.${region}.amazonaws.com`);
1111
});
1212
}
13-
for (const service of ['codedeploy', 'logs']) {
13+
for (const service of ['logs']) {
1414
test(`${service}${suffix}`, () => {
1515
expect(Default.servicePrincipal(`${service}${suffix}`, region, urlSuffix)).toBe(`${service}.${region}.${urlSuffix}`);
1616
});
@@ -48,6 +48,12 @@ describe('servicePrincipal', () => {
4848
});
4949
}
5050

51+
for (const cnRegion of ['cn-north-1', 'cn-northwest-1']) {
52+
test(`Exceptions: codedeploy in ${cnRegion}`, () => {
53+
expect(Default.servicePrincipal('codedeploy', cnRegion, 'amazonaws.com.cn')).toBe(`codedeploy.${cnRegion}.amazonaws.com.cn`);
54+
});
55+
}
56+
5157
});
5258

5359

0 commit comments

Comments
 (0)