Skip to content

Commit a9d6966

Browse files
authored
fix(ecr): policytext errors when includes resource (#24401)
ECR does not allow resource to be included in private repository resource policies. CFN largely swallows the error message. Most resources require or at least allow a resource in their policies, so we should at least warn. See issue #24314
1 parent 259fb5b commit a9d6966

File tree

5 files changed

+63
-5
lines changed

5 files changed

+63
-5
lines changed

packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.template.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,19 @@
55
"Properties": {
66
"LifecyclePolicy": {
77
"LifecyclePolicyText": "{\"rules\":[{\"rulePriority\":1,\"selection\":{\"tagStatus\":\"any\",\"countType\":\"imageCountMoreThan\",\"countNumber\":5},\"action\":{\"type\":\"expire\"}}]}"
8-
}
8+
},
9+
"RepositoryPolicyText": {
10+
"Statement": [
11+
{
12+
"Action": "ecr:GetDownloadUrlForLayer",
13+
"Effect": "Allow",
14+
"Principal": {
15+
"AWS": "*"
16+
}
17+
}
18+
],
19+
"Version": "2012-10-17"
20+
}
921
},
1022
"UpdateReplacePolicy": "Retain",
1123
"DeletionPolicy": "Retain"

packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import * as cdk from 'aws-cdk-lib';
22
import { IntegTest } from '@aws-cdk/integ-tests-alpha';
33
import * as ecr from 'aws-cdk-lib/aws-ecr';
4+
import * as iam from 'aws-cdk-lib/aws-iam';
45

56
const app = new cdk.App();
67
const stack = new cdk.Stack(app, 'aws-ecr-integ-stack');
78

89
const repo = new ecr.Repository(stack, 'Repo');
910
repo.addLifecycleRule({ maxImageCount: 5 });
11+
repo.addToResourcePolicy(new iam.PolicyStatement({
12+
actions: ['ecr:GetDownloadUrlForLayer'],
13+
principals: [new iam.AnyPrincipal()],
14+
}));
1015

1116
new cdk.CfnOutput(stack, 'RepositoryURI', {
1217
value: repo.repositoryUri,

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,18 @@ const repository = new Repository(this, 'MyTempRepo', {
126126
autoDeleteImages: true,
127127
});
128128
```
129+
130+
## Managing the Resource Policy
131+
132+
You can add statements to the resource policy of the repository using the
133+
`addToResourcePolicy` method. However, be advised that you must not include
134+
a `resources` section in the `PolicyStatement`.
135+
136+
```ts
137+
declare const repository: ecr.Repository;
138+
repository.addToResourcePolicy(new iam.PolicyStatement({
139+
actions: ['ecr:GetDownloadUrlForLayer'],
140+
// resources: ['*'], // not currently allowed!
141+
principals: [new iam.AnyPrincipal()],
142+
}));
143+
```

packages/aws-cdk-lib/aws-ecr/lib/repository.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as events from '../../aws-events';
44
import * as iam from '../../aws-iam';
55
import * as kms from '../../aws-kms';
66
import {
7+
Annotations,
78
ArnFormat,
89
IResource,
910
Lazy,
@@ -424,7 +425,7 @@ export interface OnCloudTrailImagePushedOptions extends events.OnEventOptions {
424425
*/
425426
export interface OnImageScanCompletedOptions extends events.OnEventOptions {
426427
/**
427-
* Only watch changes to the image tags spedified.
428+
* Only watch changes to the image tags specified.
428429
* Leave it undefined to watch the full repository.
429430
*
430431
* @default - Watch the changes to the repository with all image tags
@@ -660,7 +661,17 @@ export class Repository extends RepositoryBase {
660661
this.node.addValidation({ validate: () => this.policyDocument?.validateForResourcePolicy() ?? [] });
661662
}
662663

664+
/**
665+
* Add a policy statement to the repository's resource policy.
666+
*
667+
* While other resources policies in AWS either require or accept a resource section,
668+
* Cfn for ECR does not allow us to specify a resource policy.
669+
* It will fail if a resource section is present at all.
670+
*/
663671
public addToResourcePolicy(statement: iam.PolicyStatement): iam.AddToResourcePolicyResult {
672+
if (statement.resources) {
673+
Annotations.of(this).addWarning('ECR resource policy does not allow resource statements.');
674+
}
664675
if (this.policyDocument === undefined) {
665676
this.policyDocument = new iam.PolicyDocument();
666677
}

packages/aws-cdk-lib/aws-ecr/test/repository.test.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { EOL } from 'os';
2-
import { Template } from '../../assertions';
2+
import { Annotations, Template } from '../../assertions';
33
import * as iam from '../../aws-iam';
44
import * as kms from '../../aws-kms';
55
import * as cdk from '../../core';
@@ -347,7 +347,6 @@ describe('repository', () => {
347347

348348
// WHEN
349349
repo.addToResourcePolicy(new iam.PolicyStatement({
350-
resources: ['*'],
351350
principals: [new iam.ArnPrincipal('arn')],
352351
}));
353352

@@ -363,14 +362,30 @@ describe('repository', () => {
363362

364363
// WHEN
365364
repo.addToResourcePolicy(new iam.PolicyStatement({
366-
resources: ['*'],
367365
actions: ['ecr:*'],
368366
}));
369367

370368
// THEN
371369
expect(() => app.synth()).toThrow(/A PolicyStatement used in a resource-based policy must specify at least one IAM principal/);
372370
});
373371

372+
test('warns if repository policy has resources', () => {
373+
// GIVEN
374+
const app = new cdk.App();
375+
const stack = new cdk.Stack(app, 'my-stack');
376+
const repo = new ecr.Repository(stack, 'Repo');
377+
378+
// WHEN
379+
repo.addToResourcePolicy(new iam.PolicyStatement({
380+
resources: ['*'],
381+
actions: ['ecr:*'],
382+
principals: [new iam.AnyPrincipal()],
383+
}));
384+
385+
// THEN
386+
Annotations.fromStack(stack).hasWarning('*', 'ECR resource policy does not allow resource statements.');
387+
});
388+
374389
test('default encryption configuration', () => {
375390
// GIVEN
376391
const app = new cdk.App();

0 commit comments

Comments
 (0)