Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(region-info): incorrect codedeploy service principals #18505

Merged
merged 3 commits into from
Jan 25, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,15 @@ describe('CodeDeploy Lambda DeploymentGroup', () => {
Action: 'sts:AssumeRole',
Effect: 'Allow',
Principal: {
Service: { 'Fn::Join': ['', ['codedeploy.', { Ref: 'AWS::Region' }, '.', { Ref: 'AWS::URLSuffix' }]] },
Service: {
'Fn::FindInMap': [
'ServiceprincipalMap',
{
Ref: 'AWS::Region',
},
'codedeploy',
],
},
},
}],
Version: '2012-10-17',
Expand Down
33 changes: 16 additions & 17 deletions packages/@aws-cdk/region-info/lib/aws-entities.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
// Rule prefix
const RULE_ = 'RULE_';

/**
* After this point, SSM only creates regional principals
*/
export const RULE_SSM_PRINCIPALS_ARE_REGIONAL = `${RULE_}SSM_PRINCIPALS_ARE_REGIONAL`;
export const RULE_SSM_PRINCIPALS_ARE_REGIONAL = Symbol('SSM_PRINCIPALS_ARE_REGIONAL');

/**
* After this point, S3 website domains look like `s3-website.REGION.s3.amazonaws.com`
*
* Before this point, S3 website domains look like `s3-website-REGION.s3.amazonaws.com`.
*/
export const RULE_S3_WEBSITE_REGIONAL_SUBDOMAIN = `${RULE_}S3_WEBSITE_REGIONAL_SUBDOMAIN`;
export const RULE_S3_WEBSITE_REGIONAL_SUBDOMAIN = Symbol('S3_WEBSITE_REGIONAL_SUBDOMAIN');

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

/**
* Possibly non-exaustive list of all service names, used to locate service principals.
*
* Not in the list ==> default service principal mappings.
*/
export const AWS_SERVICES = [
export const AWS_SERVICES: readonly string[] = [
'application-autoscaling',
'autoscaling',
'codedeploy',
Expand All @@ -96,10 +93,10 @@ export const AWS_SERVICES = [
*
* Unknown region => we have to assume no.
*/
export function before(region: string, ruleOrRegion: string) {
export function before(region: string, ruleOrRegion: string | symbol) {
const ruleIx = AWS_REGIONS_AND_RULES.indexOf(ruleOrRegion);
if (ruleIx === -1) {
throw new Error(`Unknown rule: ${ruleOrRegion}`);
throw new Error(`Unknown rule: ${String(ruleOrRegion)}`);
}
const regionIx = AWS_REGIONS_AND_RULES.indexOf(region);
return regionIx === -1 ? false : regionIx < ruleIx;
Expand All @@ -108,17 +105,19 @@ export function before(region: string, ruleOrRegion: string) {
/**
* Return all regions before a given rule was introduced (or region)
*/
export function regionsBefore(ruleOrRegion: string): string[] {
export function regionsBefore(ruleOrRegion: string | symbol): string[] {
const ruleIx = AWS_REGIONS_AND_RULES.indexOf(ruleOrRegion);
if (ruleIx === -1) {
throw new Error(`Unknown rule: ${ruleOrRegion}`);
throw new Error(`Unknown rule: ${String(ruleOrRegion)}`);
}
return AWS_REGIONS_AND_RULES.filter((_, i) => i < ruleIx).sort();
return AWS_REGIONS_AND_RULES.slice(0, ruleIx)
.filter((entry) => typeof entry === 'string')
.sort() as string[];
}

export interface Region { partition: string, domainSuffix: string }
export interface Region { readonly partition: string, readonly domainSuffix: string }

const PARTITION_MAP: { [region: string]: Region } = {
const PARTITION_MAP: {readonly [region: string]: Region } = {
'default': { partition: 'aws', domainSuffix: 'amazonaws.com' },
'cn-': { partition: 'aws-cn', domainSuffix: 'amazonaws.com.cn' },
'us-gov-': { partition: 'aws-us-gov', domainSuffix: 'amazonaws.com' },
Expand Down
134 changes: 74 additions & 60 deletions packages/@aws-cdk/region-info/lib/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export class Default {
* @param urlSuffix deprecated and ignored.
*/
public static servicePrincipal(serviceFqn: string, region: string, urlSuffix: string): string {
const service = extractSimpleName(serviceFqn);
if (!service) {
const serviceName = extractSimpleName(serviceFqn);
if (!serviceName) {
// Return "service" if it does not look like any of the following:
// - s3
// - s3.amazonaws.com
Expand All @@ -34,72 +34,86 @@ export class Default {
return serviceFqn;
}

// Exceptions for Service Principals in us-iso-*
const US_ISO_EXCEPTIONS = new Set([
'cloudhsm',
'config',
'states',
'workspaces',
]);

// Account for idiosyncratic Service Principals in `us-iso-*` regions
if (region.startsWith('us-iso-') && US_ISO_EXCEPTIONS.has(service)) {
switch (service) {
// Services with universal principal
case ('states'):
return `${service}.amazonaws.com`;

// Services with a partitional principal
default:
return `${service}.${urlSuffix}`;
function determineConfiguration(service: string): (service: string, region: string, urlSuffix: string) => string {
function universal(s: string) { return `${s}.amazonaws.com`; };
function partitional(s: string, _: string, u: string) { return `${s}.${u}`; };
function regional(s: string, r: string) { return `${s}.${r}.amazonaws.com`; };
function regionalPartitional(s: string, r: string, u: string) { return `${s}.${r}.${u}`; };

// Exceptions for Service Principals in us-iso-*
const US_ISO_EXCEPTIONS = new Set([
'cloudhsm',
'config',
'states',
'workspaces',
]);

// Account for idiosyncratic Service Principals in `us-iso-*` regions
if (region.startsWith('us-iso-') && US_ISO_EXCEPTIONS.has(service)) {
switch (service) {
// Services with universal principal
case ('states'):
return universal;

// Services with a partitional principal
default:
return partitional;
}
}
}

// Exceptions for Service Principals in us-isob-*
const US_ISOB_EXCEPTIONS = new Set([
'dms',
'states',
]);
// Exceptions for Service Principals in us-isob-*
const US_ISOB_EXCEPTIONS = new Set([
'dms',
'states',
]);

// Account for idiosyncratic Service Principals in `us-isob-*` regions
if (region.startsWith('us-isob-') && US_ISOB_EXCEPTIONS.has(service)) {
switch (service) {
// Services with universal principal
case ('states'):
return universal;

// Services with a partitional principal
default:
return partitional;
}
}

// Account for idiosyncratic Service Principals in `us-isob-*` regions
if (region.startsWith('us-isob-') && US_ISOB_EXCEPTIONS.has(service)) {
switch (service) {
// Services with universal principal
case ('states'):
return `${service}.amazonaws.com`;
// SSM turned from global to regional at some point
case 'ssm':
return before(region, RULE_SSM_PRINCIPALS_ARE_REGIONAL)
? universal
: regional;

// CodeDeploy is regional+partitional in CN, only regional everywhere else
case 'codedeploy':
return region.startsWith('cn-')
? regionalPartitional
: regional;

// Services with a regional AND partitional principal
case 'logs':
return regionalPartitional;

// Services with a regional principal
case 'states':
return regional;

// Services with a partitional principal
default:
return `${service}.${urlSuffix}`;
}
}

// SSM turned from global to regional at some point
if (service === 'ssm') {
return before(region, RULE_SSM_PRINCIPALS_ARE_REGIONAL)
? `${service}.amazonaws.com`
: `${service}.${region}.amazonaws.com`;
}

switch (service) {
// Services with a regional AND partitional principal
case 'codedeploy':
case 'logs':
return `${service}.${region}.${urlSuffix}`;

// Services with a regional principal
case 'states':
return `${service}.${region}.amazonaws.com`;
case 'ec2':
return partitional;

// Services with a partitional principal
case 'ec2':
return `${service}.${urlSuffix}`;
// Services with a universal principal across all regions/partitions (the default case)
default:
return universal;

// Services with a universal principal across all regions/partitions (the default case)
default:
return `${service}.amazonaws.com`;
}
};

}
const configuration = determineConfiguration(serviceName);
return configuration(serviceName, region, urlSuffix);
}

private constructor() { }
Expand All @@ -108,4 +122,4 @@ export class Default {
function extractSimpleName(serviceFqn: string) {
const matches = serviceFqn.match(/^([^.]+)(?:(?:\.amazonaws\.com(?:\.cn)?)|(?:\.c2s\.ic\.gov)|(?:\.sc2s\.sgov\.gov))?$/);
return matches ? matches[1] : undefined;
}
}
3 changes: 2 additions & 1 deletion packages/@aws-cdk/region-info/lib/fact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export class Fact {
* may not be an exhaustive list of all available AWS regions.
*/
public static get regions(): string[] {
return AWS_REGIONS;
// Return by copy to ensure no modifications can be made to the undelying constant.
return Array.from(AWS_REGIONS);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,7 @@ Object {
"servicePrincipals": Object {
"application-autoscaling": "application-autoscaling.amazonaws.com",
"autoscaling": "autoscaling.amazonaws.com",
"codedeploy": "codedeploy.us-iso-east-1.c2s.ic.gov",
"codedeploy": "codedeploy.us-iso-east-1.amazonaws.com",
"ec2": "ec2.c2s.ic.gov",
"events": "events.amazonaws.com",
"lambda": "lambda.amazonaws.com",
Expand Down Expand Up @@ -826,7 +826,7 @@ Object {
"servicePrincipals": Object {
"application-autoscaling": "application-autoscaling.amazonaws.com",
"autoscaling": "autoscaling.amazonaws.com",
"codedeploy": "codedeploy.us-iso-west-1.c2s.ic.gov",
"codedeploy": "codedeploy.us-iso-west-1.amazonaws.com",
"ec2": "ec2.c2s.ic.gov",
"events": "events.amazonaws.com",
"lambda": "lambda.amazonaws.com",
Expand Down Expand Up @@ -857,7 +857,7 @@ Object {
"servicePrincipals": Object {
"application-autoscaling": "application-autoscaling.amazonaws.com",
"autoscaling": "autoscaling.amazonaws.com",
"codedeploy": "codedeploy.us-isob-east-1.sc2s.sgov.gov",
"codedeploy": "codedeploy.us-isob-east-1.amazonaws.com",
"ec2": "ec2.sc2s.sgov.gov",
"events": "events.amazonaws.com",
"lambda": "lambda.amazonaws.com",
Expand Down
10 changes: 8 additions & 2 deletions packages/@aws-cdk/region-info/test/default.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ const urlSuffix = '.nowhere.null';

describe('servicePrincipal', () => {
for (const suffix of ['', '.amazonaws.com', '.amazonaws.com.cn']) {
for (const service of ['states', 'ssm']) {
for (const service of ['codedeploy', 'states', 'ssm']) {
test(`${service}${suffix}`, () => {
expect(Default.servicePrincipal(`${service}${suffix}`, region, urlSuffix)).toBe(`${service}.${region}.amazonaws.com`);
});
}
for (const service of ['codedeploy', 'logs']) {
for (const service of ['logs']) {
test(`${service}${suffix}`, () => {
expect(Default.servicePrincipal(`${service}${suffix}`, region, urlSuffix)).toBe(`${service}.${region}.${urlSuffix}`);
});
Expand Down Expand Up @@ -48,6 +48,12 @@ describe('servicePrincipal', () => {
});
}

for (const cnRegion of ['cn-north-1', 'cn-northwest-1']) {
test(`Exceptions: codedeploy in ${cnRegion}`, () => {
expect(Default.servicePrincipal('codedeploy', cnRegion, 'amazonaws.com.cn')).toBe(`codedeploy.${cnRegion}.amazonaws.com.cn`);
});
}

});


Expand Down