Skip to content

Commit 1d56b46

Browse files
authored
fix(mediapackagev2): cdnAuth on OriginEndpoint now generates the required policy (#38013)
### Issue # (if applicable) Closes #<issue number here>. ### Reason for this change ### Description of changes Setting `cdnAuth` on `OriginEndpoint` previously stored the configuration but did not by itself produce a working endpoint policy — the `CdnAuthConfiguration` block on `OriginEndpointPolicy` is non-functional without an accompanying gating policy statement that requires the matching `mediapackagev2:RequestHasMatchingCdnAuthHeader` condition. Per the AWS docs [1], both the gating statement and the configuration block are required. This change: - Auto-emits the AWS-documented gating policy statement when `cdnAuth` is set on `OriginEndpointProps`. The resulting `OriginEndpointPolicy` now contains both the gating statement and the `CdnAuthConfiguration` block. - Drops the optional `cdnAuth` second argument from `addToResourcePolicy(...)` on `IOriginEndpoint` — the field had no clean home there and only the first call ever applied. CDN auth now lives only on the endpoint props. - Drops the `cdnAuth` prop on `MediaPackageV2OriginProps` for the same reason — set `cdnAuth` on the `OriginEndpoint` directly. - Adds an integ test that deploys the full pipeline. - Updates the README to document the new pattern, including the JSON shape the secret must use (`MediaPackageV2CDNIdentifier` key). [1] https://docs.aws.amazon.com/mediapackage/latest/userguide/cdn-auth-setup.html BREAKING CHANGE: `OriginEndpoint.addToResourcePolicy()` no longer accepts an optional `cdnAuth` second argument. `MediaPackageV2OriginProps.cdnAuth` has been removed. Set `cdnAuth` on `OriginEndpointProps` instead. ### Describe any new or updated permissions being added ### Description of how you validated changes ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 1798f73 commit 1d56b46

14 files changed

Lines changed: 580 additions & 125 deletions

packages/@aws-cdk/aws-mediapackagev2-alpha/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,50 @@ origin.addToResourcePolicy(new PolicyStatement({
243243
}));
244244
```
245245

246+
### CDN Authorization
247+
248+
MediaPackage V2 supports two ways to lock an origin endpoint to your CDN:
249+
250+
- **AWS Signature Version 4 (SigV4)** — the CDN signs requests with an IAM
251+
role. For Amazon CloudFront, see [CloudFront Integration](#cloudfront-integration).
252+
See the [SigV4 authentication guide][sigv4-auth].
253+
- **Header-based CDN authorization** — the CDN attaches a shared secret in
254+
a request header that MediaPackage validates. Use this when your CDN
255+
doesn't support SigV4. See the [CDN authorization guide][cdn-auth].
256+
257+
[sigv4-auth]: https://docs.aws.amazon.com/mediapackage/latest/userguide/sig-v4-authenticating-requests.html
258+
[cdn-auth]: https://docs.aws.amazon.com/mediapackage/latest/userguide/cdn-auth.html
259+
260+
To configure header-based authorization, set `cdnAuth` on the `OriginEndpoint`
261+
props. The L2 auto-creates the endpoint policy with:
262+
263+
- a `PolicyStatement` requiring the `mediapackagev2:RequestHasMatchingCdnAuthHeader`
264+
condition on every `GetObject` request
265+
- the `CdnAuthConfiguration` block that references the secrets and the read role
266+
267+
If you don't supply a role, one is created with the needed Secrets Manager
268+
and KMS permissions.
269+
270+
```ts
271+
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
272+
273+
declare const channel: Channel;
274+
declare const mySecret: secretsmanager.ISecret;
275+
276+
new OriginEndpoint(this, 'OriginEndpoint', {
277+
channel,
278+
segment: Segment.ts(),
279+
manifests: [Manifest.hls({ manifestName: 'index' })],
280+
cdnAuth: {
281+
secrets: [mySecret],
282+
},
283+
});
284+
```
285+
286+
You can still call `addToResourcePolicy()` to add extra statements (e.g. a
287+
harvester allow); they're appended to the auto-created policy alongside the
288+
gating statement.
289+
246290
## Granting Permissions
247291

248292
### Granting Ingest Access to MediaLive

packages/@aws-cdk/aws-mediapackagev2-alpha/lib/cloudfront-origin.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { lit } from 'aws-cdk-lib/core/lib/helpers-internal';
66
import type { Construct } from 'constructs';
77
import type { IOriginEndpoint } from './endpoint';
88
import type { IChannelGroup } from './group';
9-
import type { CdnAuthConfiguration } from './origin-endpoint-policy';
109

1110
/**
1211
* Properties for a MediaPackage V2 Origin with OAC.
@@ -25,17 +24,6 @@ export interface MediaPackageV2OriginProps extends cloudfront.OriginProps {
2524
* @default - an Origin Access Control will be created automatically.
2625
*/
2726
readonly originAccessControl?: cloudfront.IOriginAccessControlRef;
28-
29-
/**
30-
* Optional CDN authorization configuration.
31-
*
32-
* If you need CDN auth on this endpoint, provide it here so it is configured
33-
* on the first `addToResourcePolicy` call. If CDN auth is added separately
34-
* after this origin is bound, it will be ignored.
35-
*
36-
* @default - no CDN authorization
37-
*/
38-
readonly cdnAuth?: CdnAuthConfiguration;
3927
}
4028

4129
/**
@@ -66,7 +54,6 @@ export interface MediaPackageV2OriginProps extends cloudfront.OriginProps {
6654
export class MediaPackageV2Origin extends cloudfront.OriginBase {
6755
private originAccessControl?: cloudfront.IOriginAccessControlRef;
6856
private readonly endpoint: IOriginEndpoint;
69-
private readonly cdnAuth?: CdnAuthConfiguration;
7057

7158
constructor(endpoint: IOriginEndpoint, props: MediaPackageV2OriginProps) {
7259
if (!props.channelGroup.egressDomain) {
@@ -78,7 +65,6 @@ export class MediaPackageV2Origin extends cloudfront.OriginBase {
7865
super(props.channelGroup.egressDomain, props);
7966
this.endpoint = endpoint;
8067
this.originAccessControl = props.originAccessControl;
81-
this.cdnAuth = props.cdnAuth;
8268
}
8369

8470
protected renderCustomOriginConfig(): cloudfront.CfnDistribution.CustomOriginConfigProperty | undefined {
@@ -116,7 +102,6 @@ export class MediaPackageV2Origin extends cloudfront.OriginBase {
116102
},
117103
},
118104
}),
119-
this.cdnAuth,
120105
);
121106

122107
return {

packages/@aws-cdk/aws-mediapackagev2-alpha/lib/endpoint.ts

Lines changed: 65 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { RemovalPolicy, ArnFormat, Duration, Fn, Lazy, Names, Resource, Stack, T
33
import type { ICertificate } from 'aws-cdk-lib/aws-certificatemanager';
44
import type { MetricOptions } from 'aws-cdk-lib/aws-cloudwatch';
55
import { Metric, Unit } from 'aws-cdk-lib/aws-cloudwatch';
6-
import type { IRole, PolicyStatement, AddToResourcePolicyResult } from 'aws-cdk-lib/aws-iam';
6+
import type { IRole, AddToResourcePolicyResult } from 'aws-cdk-lib/aws-iam';
7+
import { Effect, PolicyStatement, StarPrincipal } from 'aws-cdk-lib/aws-iam';
78
import { CfnOriginEndpoint } from 'aws-cdk-lib/aws-mediapackagev2';
89
import type { IOriginEndpointRef, OriginEndpointReference } from 'aws-cdk-lib/aws-mediapackagev2';
910
import { ValidationError, UnscopedValidationError, CfnResource } from 'aws-cdk-lib/core';
@@ -1054,9 +1055,8 @@ export interface IOriginEndpoint extends IResource, IOriginEndpointRef {
10541055
* If you have already defined one, it will append to the policy already created.
10551056
*
10561057
* @param statement - The policy statement to add
1057-
* @param cdnAuth - Optional CDN authorization configuration. Only the first CDN auth configuration is used if provided multiple times.
10581058
*/
1059-
addToResourcePolicy(statement: PolicyStatement, cdnAuth?: CdnAuthConfiguration): AddToResourcePolicyResult;
1059+
addToResourcePolicy(statement: PolicyStatement): AddToResourcePolicyResult;
10601060

10611061
/**
10621062
* Create a CloudWatch metric.
@@ -1829,6 +1829,35 @@ export interface OriginEndpointOptions {
18291829
/**
18301830
* Provide access to MediaPackage V2 Origin Endpoint via secret header.
18311831
*
1832+
* Use this when your CDN doesn't support AWS Signature Version 4 (SigV4)
1833+
* authentication. For SigV4-based access with Amazon CloudFront, see
1834+
* `MediaPackageV2Origin`.
1835+
*
1836+
* Setting `cdnAuth` auto-creates the following policy on the OriginEndpoint:
1837+
*
1838+
* ```json
1839+
* {
1840+
* "Version":"2012-10-17",
1841+
* "Statement": [
1842+
* {
1843+
* "Sid": "AllowGetObjectAccessForAuthorizedRequest",
1844+
* "Effect": "Allow",
1845+
* "Principal": "*",
1846+
* "Action": "mediapackagev2:GetObject",
1847+
* "Resource": "arn:aws:mediapackagev2:us-east-1:111122223333:channelGroup/channelGroupName/channel/channelName/originEndpoint/originEndpointName",
1848+
* "Condition": {
1849+
* "Bool": {
1850+
* "mediapackagev2:RequestHasMatchingCdnAuthHeader": "true"
1851+
* }
1852+
* }
1853+
* }
1854+
* ]
1855+
* }
1856+
* ```
1857+
*
1858+
* @see https://docs.aws.amazon.com/mediapackage/latest/userguide/cdn-auth.html
1859+
* @see https://docs.aws.amazon.com/mediapackage/latest/userguide/cdn-auth-setup.html
1860+
*
18321861
* @default undefined - Not configured on endpoint
18331862
*/
18341863
readonly cdnAuth?: CdnAuthConfiguration;
@@ -2653,36 +2682,21 @@ abstract class OriginEndpointBase extends Resource implements IOriginEndpoint {
26532682

26542683
/**
26552684
* CDN authorization configuration to be applied when the policy is created.
2685+
* Set by subclass constructors when `cdnAuth` is provided in props.
26562686
*/
2657-
private cdnAuthConfig?: CdnAuthConfiguration;
2658-
2659-
/**
2660-
* Set CDN auth config. Used by subclass constructors.
2661-
* @internal
2662-
*/
2663-
protected _setCdnAuth(cdnAuth: CdnAuthConfiguration): void {
2664-
if (!this.cdnAuthConfig) {
2665-
this.cdnAuthConfig = cdnAuth;
2666-
}
2667-
}
2687+
protected cdnAuthConfig?: CdnAuthConfiguration;
26682688

26692689
/**
26702690
* Configure origin endpoint policy.
26712691
*
2672-
* You can only add 1 OriginEndpointPolicy to an OriginEndpoint.
2673-
* If you have already defined one, it will append to the policy already created.
2692+
* You can only add 1 OriginEndpointPolicy to an OriginEndpoint. If you have already
2693+
* defined one, this will append to the policy already created.
2694+
*
2695+
* To configure CDN authentication, set `cdnAuth` on the construct's props instead.
26742696
*
26752697
* @param statement - The policy statement to add
2676-
* @param cdnAuth - Optional CDN authorization configuration. If provided, the policy will be
2677-
* created with CDN authentication enabled using secrets from AWS Secrets Manager.
2678-
* If cdnAuth is provided multiple times, only the first configuration is used.
2679-
*/
2680-
public addToResourcePolicy(statement: PolicyStatement, cdnAuth?: CdnAuthConfiguration): AddToResourcePolicyResult {
2681-
// Store CDN auth config if provided (only if not already set)
2682-
if (cdnAuth && !this.cdnAuthConfig) {
2683-
this.cdnAuthConfig = cdnAuth;
2684-
}
2685-
2698+
*/
2699+
public addToResourcePolicy(statement: PolicyStatement): AddToResourcePolicyResult {
26862700
if (!this.policy && this.autoCreatePolicy) {
26872701
this.policy = new OriginEndpointPolicy(this, 'Policy', {
26882702
originEndpoint: this,
@@ -3012,9 +3026,32 @@ export class OriginEndpoint extends OriginEndpointBase implements IOriginEndpoin
30123026

30133027
origin.applyRemovalPolicy(props?.removalPolicy ?? RemovalPolicy.DESTROY);
30143028

3015-
// Pre-set CDN auth config if provided in props
3029+
// When cdnAuth is set, pre-create the endpoint policy with the AWS-recommended gating
3030+
// statement. The principal is `*` (anonymous) because CDN requests to MediaPackage are
3031+
// unsigned HTTPS — only the matching CDN-Identifier header proves authorisation.
3032+
// See https://docs.aws.amazon.com/mediapackage/latest/userguide/cdn-auth-setup.html
30163033
if (props.cdnAuth) {
3017-
this._setCdnAuth(props.cdnAuth);
3034+
if (props.cdnAuth.secrets.length === 0) {
3035+
throw new ValidationError(
3036+
lit`CdnAuthSecretsRequired`,
3037+
'cdnAuth.secrets must contain at least one secret. CDN authorization needs a secret to validate incoming CDN-Identifier headers.',
3038+
this,
3039+
);
3040+
}
3041+
3042+
this.cdnAuthConfig = props.cdnAuth;
3043+
this.addToResourcePolicy(new PolicyStatement({
3044+
sid: 'AllowGetObjectAccessForAuthorizedRequest',
3045+
effect: Effect.ALLOW,
3046+
principals: [new StarPrincipal()],
3047+
actions: ['mediapackagev2:GetObject'],
3048+
resources: [this.originEndpointArn],
3049+
conditions: {
3050+
Bool: {
3051+
'mediapackagev2:RequestHasMatchingCdnAuthHeader': 'true',
3052+
},
3053+
},
3054+
}));
30183055
}
30193056
}
30203057
}

packages/@aws-cdk/aws-mediapackagev2-alpha/lib/origin-endpoint-policy.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ import type { IOriginEndpoint } from './endpoint';
1616
export interface CdnAuthConfiguration {
1717
/**
1818
* Secrets to use for CDN authorization.
19+
*
20+
* Each secret must be a JSON object with a `MediaPackageV2CDNIdentifier` key whose
21+
* value is the CDN-Identifier header value. See the
22+
* {@link https://docs.aws.amazon.com/mediapackage/latest/userguide/cdn-auth-setup.html | MediaPackage CDN authorization docs}.
1923
*/
2024
readonly secrets: ISecret[];
2125
/**

packages/@aws-cdk/aws-mediapackagev2-alpha/test/cloudfront-origin.test.ts

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -232,40 +232,70 @@ test('distribution origin uses egress domain and OAC ID', () => {
232232
});
233233
});
234234

235-
test('MediaPackageV2Origin with cdnAuth configures endpoint policy', () => {
235+
test('MediaPackageV2Origin works alongside cdnAuth set on the endpoint props', () => {
236236
const group = new mediapackagev2.ChannelGroup(stack, 'Group', {
237237
channelGroupName: 'test-group',
238238
});
239239
const channel = new mediapackagev2.Channel(stack, 'Channel', {
240240
channelGroup: group,
241241
channelName: 'test-channel',
242242
});
243+
const secret = new Secret(stack, 'CdnSecret');
244+
243245
const endpoint = new mediapackagev2.OriginEndpoint(stack, 'Endpoint', {
244246
channel,
245247
originEndpointName: 'test-endpoint',
246248
segment: mediapackagev2.Segment.cmaf(),
247249
manifests: [mediapackagev2.Manifest.hls({ manifestName: 'index' })],
250+
cdnAuth: {
251+
secrets: [secret],
252+
},
248253
});
249254

250-
const secret = new Secret(stack, 'CdnSecret');
251-
252255
new cloudfront.Distribution(stack, 'Dist', {
253256
defaultBehavior: {
254257
origin: new mediapackagev2.MediaPackageV2Origin(endpoint, {
255258
channelGroup: group,
256-
cdnAuth: {
257-
secrets: [secret],
258-
},
259259
}),
260260
},
261261
});
262262

263263
const template = Template.fromStack(stack);
264264

265-
// CDN auth configuration is present on the endpoint policy
266265
template.hasResourceProperties('AWS::MediaPackageV2::OriginEndpointPolicy', {
267266
CdnAuthConfiguration: Match.objectLike({
268267
CdnIdentifierSecretArns: Match.anyValue(),
269268
}),
269+
Policy: {
270+
Version: '2012-10-17',
271+
Statement: [
272+
// cdnAuth gating statement — third-party CDN with header check
273+
{
274+
Sid: 'AllowGetObjectAccessForAuthorizedRequest',
275+
Effect: 'Allow',
276+
Principal: '*',
277+
Action: 'mediapackagev2:GetObject',
278+
Resource: { 'Fn::GetAtt': [Match.stringLikeRegexp('Endpoint'), 'Arn'] },
279+
Condition: {
280+
Bool: {
281+
'mediapackagev2:RequestHasMatchingCdnAuthHeader': 'true',
282+
},
283+
},
284+
},
285+
// OAC SigV4 statement — CloudFront via service principal
286+
{
287+
Sid: 'AllowCloudFrontServicePrincipal',
288+
Effect: 'Allow',
289+
Principal: { Service: 'cloudfront.amazonaws.com' },
290+
Action: ['mediapackagev2:GetObject', 'mediapackagev2:GetHeadObject'],
291+
Resource: { 'Fn::GetAtt': [Match.stringLikeRegexp('Endpoint'), 'Arn'] },
292+
Condition: {
293+
StringEquals: {
294+
'aws:SourceArn': Match.anyValue(),
295+
},
296+
},
297+
},
298+
],
299+
},
270300
});
271301
});

packages/@aws-cdk/aws-mediapackagev2-alpha/test/integ.mediapackagev2-endpoint-cdn-auth.js.snapshot/aws-cdk-mediapackagev2-endpoint-cdn-auth.assets.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)