Skip to content

Commit 3cbcf66

Browse files
authored
Merge branch 'master' into huijbers/deadly-embrace
2 parents ce715df + 415eb86 commit 3cbcf66

26 files changed

Lines changed: 624 additions & 34 deletions

File tree

packages/@aws-cdk/assert/lib/assertions/have-resource.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export class HaveResourceAssertion extends JestFriendlyAssertion<StackInspector>
6666
for (const logicalId of Object.keys(inspector.value.Resources || {})) {
6767
const resource = inspector.value.Resources[logicalId];
6868
if (resource.Type === this.resourceType) {
69-
const propsToCheck = this.part === ResourcePart.Properties ? resource.Properties : resource;
69+
const propsToCheck = this.part === ResourcePart.Properties ? (resource.Properties ?? {}) : resource;
7070

7171
// Pass inspection object as 2nd argument, initialize failure with default string,
7272
// to maintain backwards compatibility with old predicate API.

packages/@aws-cdk/assets/lib/fs/options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface CopyOptions {
1010
* A strategy for how to handle symlinks.
1111
*
1212
* @default Never
13+
* @deprecated use `followSymlinks` instead
1314
*/
1415
readonly follow?: FollowMode;
1516

packages/@aws-cdk/aws-codebuild/lib/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export * from './cache';
1010
export * from './build-spec';
1111
export * from './file-location';
1212
export * from './linux-gpu-build-image';
13+
export * from './untrusted-code-boundary-policy';
1314

1415
// AWS::CodeBuild CloudFormation Resources:
1516
export * from './codebuild.generated';
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import * as iam from '@aws-cdk/aws-iam';
2+
import { Construct } from 'constructs';
3+
4+
/**
5+
* Construction properties for UntrustedCodeBoundaryPolicy
6+
*/
7+
export interface UntrustedCodeBoundaryPolicyProps {
8+
/**
9+
* The name of the managed policy.
10+
*
11+
* @default - A name is automatically generated.
12+
*/
13+
readonly managedPolicyName?: string;
14+
15+
/**
16+
* Additional statements to add to the default set of statements
17+
*
18+
* @default - No additional statements
19+
*/
20+
readonly additionalStatements?: iam.PolicyStatement[];
21+
}
22+
23+
/**
24+
* Permissions Boundary for a CodeBuild Project running untrusted code
25+
*
26+
* This class is a Policy, intended to be used as a Permissions Boundary
27+
* for a CodeBuild project. It allows most of the actions necessary to run
28+
* the CodeBuild project, but disallows reading from Parameter Store
29+
* and Secrets Manager.
30+
*
31+
* Use this when your CodeBuild project is running untrusted code (for
32+
* example, if you are using one to automatically build Pull Requests
33+
* that anyone can submit), and you want to prevent your future self
34+
* from accidentally exposing Secrets to this build.
35+
*
36+
* (The reason you might want to do this is because otherwise anyone
37+
* who can submit a Pull Request to your project can write a script
38+
* to email those secrets to themselves).
39+
*
40+
* @example
41+
*
42+
* iam.PermissionsBoundary.of(project).apply(new UntrustedCodeBoundaryPolicy(this, 'Boundary'));
43+
*/
44+
export class UntrustedCodeBoundaryPolicy extends iam.ManagedPolicy {
45+
constructor(scope: Construct, id: string, props: UntrustedCodeBoundaryPolicyProps = {}) {
46+
super(scope, id, {
47+
managedPolicyName: props.managedPolicyName,
48+
description: 'Permissions Boundary Policy for CodeBuild Projects running untrusted code',
49+
statements: [
50+
new iam.PolicyStatement({
51+
actions: [
52+
// For logging
53+
'logs:CreateLogGroup',
54+
'logs:CreateLogStream',
55+
'logs:PutLogEvents',
56+
57+
// For test reports
58+
'codebuild:CreateReportGroup',
59+
'codebuild:CreateReport',
60+
'codebuild:UpdateReport',
61+
'codebuild:BatchPutTestCases',
62+
'codebuild:BatchPutCodeCoverages',
63+
64+
// For batch builds
65+
'codebuild:StartBuild',
66+
'codebuild:StopBuild',
67+
'codebuild:RetryBuild',
68+
69+
// For pulling ECR images
70+
'ecr:GetDownloadUrlForLayer',
71+
'ecr:BatchGetImage',
72+
'ecr:BatchCheckLayerAvailability',
73+
74+
// For running in a VPC
75+
'ec2:CreateNetworkInterfacePermission',
76+
'ec2:CreateNetworkInterface',
77+
'ec2:DescribeNetworkInterfaces',
78+
'ec2:DeleteNetworkInterface',
79+
'ec2:DescribeSubnets',
80+
'ec2:DescribeSecurityGroups',
81+
'ec2:DescribeDhcpOptions',
82+
'ec2:DescribeVpcs',
83+
84+
// NOTABLY MISSING:
85+
// - Reading secrets
86+
// - Reading parameterstore
87+
],
88+
resources: ['*'],
89+
}),
90+
...props.additionalStatements ?? [],
91+
],
92+
});
93+
}
94+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { expect, haveResourceLike, arrayWith } from '@aws-cdk/assert';
2+
import * as iam from '@aws-cdk/aws-iam';
3+
import * as cdk from '@aws-cdk/core';
4+
import { Test } from 'nodeunit';
5+
import * as codebuild from '../lib';
6+
7+
export = {
8+
'can attach permissions boundary to Project'(test: Test) {
9+
// GIVEN
10+
const stack = new cdk.Stack();
11+
12+
// WHEN
13+
const project = new codebuild.Project(stack, 'Project', {
14+
source: codebuild.Source.gitHub({ owner: 'a', repo: 'b' }),
15+
});
16+
iam.PermissionsBoundary.of(project).apply(new codebuild.UntrustedCodeBoundaryPolicy(stack, 'Boundary'));
17+
18+
// THEN
19+
expect(stack).to(haveResourceLike('AWS::IAM::Role', {
20+
PermissionsBoundary: { Ref: 'BoundaryEA298153' },
21+
}));
22+
23+
test.done();
24+
},
25+
26+
'can add additional statements Boundary'(test: Test) {
27+
// GIVEN
28+
const stack = new cdk.Stack();
29+
30+
// WHEN
31+
const project = new codebuild.Project(stack, 'Project', {
32+
source: codebuild.Source.gitHub({ owner: 'a', repo: 'b' }),
33+
});
34+
iam.PermissionsBoundary.of(project).apply(new codebuild.UntrustedCodeBoundaryPolicy(stack, 'Boundary', {
35+
additionalStatements: [
36+
new iam.PolicyStatement({
37+
actions: ['a:a'],
38+
resources: ['b'],
39+
}),
40+
],
41+
}));
42+
43+
// THEN
44+
expect(stack).to(haveResourceLike('AWS::IAM::ManagedPolicy', {
45+
PolicyDocument: {
46+
Statement: arrayWith({
47+
Effect: 'Allow',
48+
Action: 'a:a',
49+
Resource: 'b',
50+
}),
51+
},
52+
}));
53+
54+
test.done();
55+
},
56+
};

packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ import * as fs from 'fs';
22
import * as path from 'path';
33
import * as assets from '@aws-cdk/assets';
44
import * as ecr from '@aws-cdk/aws-ecr';
5-
import { Annotations, Construct as CoreConstruct, FeatureFlags, IgnoreMode, Stack, Token } from '@aws-cdk/core';
5+
import {
6+
Annotations, AssetStaging, Construct as CoreConstruct, FeatureFlags, FileFingerprintOptions, IgnoreMode, Stack, SymlinkFollowMode, Token,
7+
} from '@aws-cdk/core';
68
import * as cxapi from '@aws-cdk/cx-api';
79
import { Construct } from 'constructs';
810

911
/**
1012
* Options for DockerImageAsset
1113
*/
12-
export interface DockerImageAssetOptions extends assets.FingerprintOptions {
14+
export interface DockerImageAssetOptions extends assets.FingerprintOptions, FileFingerprintOptions {
1315
/**
1416
* ECR repository name
1517
*
@@ -137,8 +139,9 @@ export class DockerImageAsset extends CoreConstruct implements assets.IAsset {
137139
// deletion of the ECR repository the app used).
138140
extraHash.version = '1.21.0';
139141

140-
const staging = new assets.Staging(this, 'Staging', {
142+
const staging = new AssetStaging(this, 'Staging', {
141143
...props,
144+
follow: props.followSymlinks ?? toSymlinkFollow(props.follow),
142145
exclude,
143146
ignoreMode,
144147
sourcePath: dir,
@@ -181,3 +184,13 @@ function validateBuildArgs(buildArgs?: { [key: string]: string }) {
181184
}
182185
}
183186
}
187+
188+
function toSymlinkFollow(follow?: assets.FollowMode): SymlinkFollowMode | undefined {
189+
switch (follow) {
190+
case undefined: return undefined;
191+
case assets.FollowMode.NEVER: return SymlinkFollowMode.NEVER;
192+
case assets.FollowMode.ALWAYS: return SymlinkFollowMode.ALWAYS;
193+
case assets.FollowMode.BLOCK_EXTERNAL: return SymlinkFollowMode.BLOCK_EXTERNAL;
194+
case assets.FollowMode.EXTERNAL: return SymlinkFollowMode.EXTERNAL;
195+
}
196+
}

packages/@aws-cdk/aws-ecr/README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,29 @@ grants an IAM user access to call this API.
5151

5252
```ts
5353
import * as iam from '@aws-cdk/aws-iam';
54+
import * as ecr from '@aws-cdk/aws-ecr';
5455

5556
const user = new iam.User(this, 'User', { ... });
56-
iam.AuthorizationToken.grantRead(user);
57+
ecr.AuthorizationToken.grantRead(user);
5758
```
5859

60+
If you access images in the [Public ECR Gallery](https://gallery.ecr.aws/) as well, it is recommended you authenticate to the regsitry to benefit from
61+
higher rate and bandwidth limits.
62+
63+
> See `Pricing` in https://aws.amazon.com/blogs/aws/amazon-ecr-public-a-new-public-container-registry/ and [Service quotas](https://docs.aws.amazon.com/AmazonECR/latest/public/public-service-quotas.html).
64+
65+
The following code snippet grants an IAM user access to retrieve an authorization token for the public gallery.
66+
67+
```ts
68+
import * as iam from '@aws-cdk/aws-iam';
69+
import * as ecr from '@aws-cdk/aws-ecr';
70+
71+
const user = new iam.User(this, 'User', { ... });
72+
ecr.PublicGalleryAuthorizationToken.grantRead(user);
73+
```
74+
75+
This user can then proceed to login to the registry using one of the [authentication methods](https://docs.aws.amazon.com/AmazonECR/latest/public/public-registries.html#public-registry-auth).
76+
5977
## Automatically clean up repositories
6078

6179
You can set life cycle rules to automatically clean up old images from your

packages/@aws-cdk/aws-ecr/lib/auth-token.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import * as iam from '@aws-cdk/aws-iam';
22

33
/**
4-
* Authorization token to access ECR repositories via Docker CLI.
4+
* Authorization token to access private ECR repositories in the current environment via Docker CLI.
5+
*
6+
* @see https://docs.aws.amazon.com/AmazonECR/latest/userguide/registry_auth.html
57
*/
68
export class AuthorizationToken {
79
/**
@@ -18,3 +20,27 @@ export class AuthorizationToken {
1820
private constructor() {
1921
}
2022
}
23+
24+
/**
25+
* Authorization token to access the global public ECR Gallery via Docker CLI.
26+
*
27+
* @see https://docs.aws.amazon.com/AmazonECR/latest/public/public-registries.html#public-registry-auth
28+
*/
29+
export class PublicGalleryAuthorizationToken {
30+
31+
/**
32+
* Grant access to retrieve an authorization token.
33+
*/
34+
public static grantRead(grantee: iam.IGrantable) {
35+
grantee.grantPrincipal.addToPrincipalPolicy(new iam.PolicyStatement({
36+
actions: ['ecr-public:GetAuthorizationToken', 'sts:GetServiceBearerToken'],
37+
// GetAuthorizationToken only allows '*'. See https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonelasticcontainerregistry.html#amazonelasticcontainerregistry-actions-as-permissions
38+
// GetServiceBearerToken only allows '*'. See https://docs.aws.amazon.com/service-authorization/latest/reference/list_awssecuritytokenservice.html#awssecuritytokenservice-actions-as-permissions
39+
resources: ['*'],
40+
}));
41+
}
42+
43+
private constructor() {
44+
}
45+
46+
}

packages/@aws-cdk/aws-ecr/test/test.auth-token.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { expect, haveResourceLike } from '@aws-cdk/assert';
22
import * as iam from '@aws-cdk/aws-iam';
33
import { Stack } from '@aws-cdk/core';
44
import { Test } from 'nodeunit';
5-
import { AuthorizationToken } from '../lib';
5+
import { AuthorizationToken, PublicGalleryAuthorizationToken } from '../lib';
66

77
export = {
8-
'grant()'(test: Test) {
8+
'AuthorizationToken.grantRead()'(test: Test) {
99
// GIVEN
1010
const stack = new Stack();
1111
const user = new iam.User(stack, 'User');
@@ -28,4 +28,32 @@ export = {
2828

2929
test.done();
3030
},
31+
32+
'PublicGalleryAuthorizationToken.grantRead()'(test: Test) {
33+
// GIVEN
34+
const stack = new Stack();
35+
const user = new iam.User(stack, 'User');
36+
37+
// WHEN
38+
PublicGalleryAuthorizationToken.grantRead(user);
39+
40+
// THEN
41+
expect(stack).to(haveResourceLike('AWS::IAM::Policy', {
42+
PolicyDocument: {
43+
Statement: [
44+
{
45+
Action: [
46+
'ecr-public:GetAuthorizationToken',
47+
'sts:GetServiceBearerToken',
48+
],
49+
Effect: 'Allow',
50+
Resource: '*',
51+
},
52+
],
53+
},
54+
}));
55+
56+
test.done();
57+
},
58+
3159
};

packages/@aws-cdk/aws-ecs-patterns/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,3 +427,17 @@ const loadBalancedFargateService = new ApplicationLoadBalancedFargateService(sta
427427
},
428428
});
429429
```
430+
431+
### Set PlatformVersion for ScheduledFargateTask
432+
433+
```ts
434+
const scheduledFargateTask = new ScheduledFargateTask(stack, 'ScheduledFargateTask', {
435+
cluster,
436+
scheduledFargateTaskImageOptions: {
437+
image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
438+
memoryLimitMiB: 512,
439+
},
440+
schedule: events.Schedule.expression('rate(1 minute)'),
441+
platformVersion: ecs.FargatePlatformVersion.VERSION1_4,
442+
});
443+
```

0 commit comments

Comments
 (0)