Skip to content

Commit

Permalink
feat(route53): add support for grantDelegation on imported PublicHost…
Browse files Browse the repository at this point in the history
…edZone (#26333)

Imported `PublicHostedZone` with `fromPublicHostedZoneId` and `fromPublicHostedZoneAttributes` don't have support for the `grantDelegation` method since they return an instance of type `IPublicHostedZone`.

This change adds support for `grantDelegation` to those instances as well.

Closes #26240.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
lpizzinidev authored Aug 18, 2023
1 parent 4d3ec71 commit a93af2f
Show file tree
Hide file tree
Showing 10 changed files with 339 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
}
}
},
"3222f491727b0389ac87f972f2443b490ff3cee14d24c28f1527c3f085cab460": {
"52da24cb67101152630cedcc08830f183f595580f8a7f6fcef1e0aac216c7198": {
"source": {
"path": "aws-cdk-route53-cross-account-integ.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "3222f491727b0389ac87f972f2443b490ff3cee14d24c28f1527c3f085cab460.json",
"objectKey": "52da24cb67101152630cedcc08830f183f595580f8a7f6fcef1e0aac216c7198.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,86 @@
],
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
},
"Role1ABCC5F0": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"AWS": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":iam::",
{
"Ref": "AWS::AccountId"
},
":root"
]
]
}
}
}
],
"Version": "2012-10-17"
}
}
},
"RoleDefaultPolicy5FFB7DAB": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": "route53:ChangeResourceRecordSets",
"Condition": {
"ForAllValues:StringEquals": {
"route53:ChangeResourceRecordSetsRecordTypes": [
"NS"
],
"route53:ChangeResourceRecordSetsActions": [
"UPSERT",
"DELETE"
]
}
},
"Effect": "Allow",
"Resource": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":route53:::hostedzone/imported-public-zone-id"
]
]
}
},
{
"Action": "route53:ListHostedZonesByName",
"Effect": "Allow",
"Resource": "*"
}
],
"Version": "2012-10-17"
},
"PolicyName": "RoleDefaultPolicy5FFB7DAB",
"Roles": [
{
"Ref": "Role1ABCC5F0"
}
]
}
}
},
"Parameters": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"validateOnSynth": false,
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}",
"cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}",
"stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/3222f491727b0389ac87f972f2443b490ff3cee14d24c28f1527c3f085cab460.json",
"stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/52da24cb67101152630cedcc08830f183f595580f8a7f6fcef1e0aac216c7198.json",
"requiresBootstrapStackVersion": 6,
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version",
"additionalDependencies": [
Expand Down Expand Up @@ -93,6 +93,18 @@
"data": "DelegationWithZoneNameCrossAccountZoneDelegationCustomResourceA1A1C94A"
}
],
"/aws-cdk-route53-cross-account-integ/Role/Resource": [
{
"type": "aws:cdk:logicalId",
"data": "Role1ABCC5F0"
}
],
"/aws-cdk-route53-cross-account-integ/Role/DefaultPolicy/Resource": [
{
"type": "aws:cdk:logicalId",
"data": "RoleDefaultPolicy5FFB7DAB"
}
],
"/aws-cdk-route53-cross-account-integ/BootstrapVersion": [
{
"type": "aws:cdk:logicalId",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,138 @@
"version": "0.0.0"
}
},
"Role": {
"id": "Role",
"path": "aws-cdk-route53-cross-account-integ/Role",
"children": {
"ImportRole": {
"id": "ImportRole",
"path": "aws-cdk-route53-cross-account-integ/Role/ImportRole",
"constructInfo": {
"fqn": "aws-cdk-lib.Resource",
"version": "0.0.0"
}
},
"Resource": {
"id": "Resource",
"path": "aws-cdk-route53-cross-account-integ/Role/Resource",
"attributes": {
"aws:cdk:cloudformation:type": "AWS::IAM::Role",
"aws:cdk:cloudformation:props": {
"assumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"AWS": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":iam::",
{
"Ref": "AWS::AccountId"
},
":root"
]
]
}
}
}
],
"Version": "2012-10-17"
}
}
},
"constructInfo": {
"fqn": "aws-cdk-lib.aws_iam.CfnRole",
"version": "0.0.0"
}
},
"DefaultPolicy": {
"id": "DefaultPolicy",
"path": "aws-cdk-route53-cross-account-integ/Role/DefaultPolicy",
"children": {
"Resource": {
"id": "Resource",
"path": "aws-cdk-route53-cross-account-integ/Role/DefaultPolicy/Resource",
"attributes": {
"aws:cdk:cloudformation:type": "AWS::IAM::Policy",
"aws:cdk:cloudformation:props": {
"policyDocument": {
"Statement": [
{
"Action": "route53:ChangeResourceRecordSets",
"Condition": {
"ForAllValues:StringEquals": {
"route53:ChangeResourceRecordSetsRecordTypes": [
"NS"
],
"route53:ChangeResourceRecordSetsActions": [
"UPSERT",
"DELETE"
]
}
},
"Effect": "Allow",
"Resource": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":route53:::hostedzone/imported-public-zone-id"
]
]
}
},
{
"Action": "route53:ListHostedZonesByName",
"Effect": "Allow",
"Resource": "*"
}
],
"Version": "2012-10-17"
},
"policyName": "RoleDefaultPolicy5FFB7DAB",
"roles": [
{
"Ref": "Role1ABCC5F0"
}
]
}
},
"constructInfo": {
"fqn": "aws-cdk-lib.aws_iam.CfnPolicy",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "aws-cdk-lib.aws_iam.Policy",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "aws-cdk-lib.aws_iam.Role",
"version": "0.0.0"
}
},
"ImportedPublicZone": {
"id": "ImportedPublicZone",
"path": "aws-cdk-route53-cross-account-integ/ImportedPublicZone",
"constructInfo": {
"fqn": "aws-cdk-lib.Resource",
"version": "0.0.0"
}
},
"BootstrapVersion": {
"id": "BootstrapVersion",
"path": "aws-cdk-route53-cross-account-integ/BootstrapVersion",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,16 @@ new CrossAccountZoneDelegationRecord(stack, 'DelegationWithZoneName', {
delegationRole: parentZone.crossAccountZoneDelegationRole!,
});

const role = new iam.Role(stack, 'Role', {
assumedBy: new iam.AccountRootPrincipal(),
});

const importedPublicZone = PublicHostedZone.fromPublicHostedZoneId(stack, 'ImportedPublicZone', 'imported-public-zone-id');
importedPublicZone.grantDelegation(role);

new IntegTest(app, 'Route53CrossAccountInteg', {
testCases: [stack],
diffAssets: true,
});

app.synth();
4 changes: 2 additions & 2 deletions packages/aws-cdk-lib/aws-ec2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -980,8 +980,8 @@ Endpoint services support private DNS, which makes it easier for clients to conn
You can enable private DNS on an endpoint service like so:

```ts
import { HostedZone, VpcEndpointServiceDomainName } from 'aws-cdk-lib/aws-route53';
declare const zone: HostedZone;
import { PublicHostedZone, VpcEndpointServiceDomainName } from 'aws-cdk-lib/aws-route53';
declare const zone: PublicHostedZone;
declare const vpces: ec2.VpcEndpointService;

new VpcEndpointServiceDomainName(this, 'EndpointDomain', {
Expand Down
14 changes: 14 additions & 0 deletions packages/aws-cdk-lib/aws-route53/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,20 @@ const zoneFromAttributes = route53.PublicHostedZone.fromPublicHostedZoneAttribut
const zoneFromId = route53.PublicHostedZone.fromPublicHostedZoneId(this, 'MyZone', 'ZOJJZC49E0EPZ');
```

You can use `CrossAccountZoneDelegationRecord` on imported Public Hosted Zones with the `grantDelegation` method:

```ts
const crossAccountRole = new iam.Role(this, 'CrossAccountRole', {
// The role name must be predictable
roleName: 'MyDelegationRole',
// The other account
assumedBy: new iam.AccountPrincipal('12345678901'),
});

const zoneFromId = route53.PublicHostedZone.fromPublicHostedZoneId(this, 'MyZone', 'ZOJJZC49E0EPZ');
zoneFromId.grantDelegation(crossAccountRole);
```

## VPC Endpoint Service Private DNS

When you create a VPC endpoint service, AWS generates endpoint-specific DNS hostnames that consumers use to communicate with the service.
Expand Down
39 changes: 15 additions & 24 deletions packages/aws-cdk-lib/aws-route53/lib/hosted-zone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { HostedZoneProviderProps } from './hosted-zone-provider';
import { HostedZoneAttributes, IHostedZone, PublicHostedZoneAttributes } from './hosted-zone-ref';
import { CaaAmazonRecord, ZoneDelegationRecord } from './record-set';
import { CfnHostedZone } from './route53.generated';
import { makeHostedZoneArn, validateZoneName } from './util';
import { makeGrantDelegation, makeHostedZoneArn, validateZoneName } from './util';
import * as ec2 from '../../aws-ec2';
import * as iam from '../../aws-iam';
import * as cxschema from '../../cloud-assembly-schema';
Expand Down Expand Up @@ -238,7 +238,12 @@ export interface PublicHostedZoneProps extends CommonHostedZoneProps {
/**
* Represents a Route 53 public hosted zone
*/
export interface IPublicHostedZone extends IHostedZone { }
export interface IPublicHostedZone extends IHostedZone {
/**
* Grant permissions to add delegation records to this zone
*/
grantDelegation(grantee: iam.IGrantable): iam.Grant;
}

/**
* Create a Route53 public hosted zone.
Expand All @@ -264,6 +269,9 @@ export class PublicHostedZone extends HostedZone implements IPublicHostedZone {
public get hostedZoneArn(): string {
return makeHostedZoneArn(this, this.hostedZoneId);
}
public grantDelegation(grantee: iam.IGrantable): iam.Grant {
return makeGrantDelegation(grantee, this.hostedZoneArn);
};
}
return new Import(scope, id);
}
Expand All @@ -284,6 +292,9 @@ export class PublicHostedZone extends HostedZone implements IPublicHostedZone {
public get hostedZoneArn(): string {
return makeHostedZoneArn(this, this.hostedZoneId);
}
public grantDelegation(grantee: iam.IGrantable): iam.Grant {
return makeGrantDelegation(grantee, this.hostedZoneArn);
};
}
return new Import(scope, id);
}
Expand Down Expand Up @@ -354,28 +365,8 @@ export class PublicHostedZone extends HostedZone implements IPublicHostedZone {
});
}

/**
* Grant permissions to add delegation records to this zone
*/
public grantDelegation(grantee: iam.IGrantable) {
const g1 = iam.Grant.addToPrincipal({
grantee,
actions: ['route53:ChangeResourceRecordSets'],
resourceArns: [this.hostedZoneArn],
conditions: {
'ForAllValues:StringEquals': {
'route53:ChangeResourceRecordSetsRecordTypes': ['NS'],
'route53:ChangeResourceRecordSetsActions': ['UPSERT', 'DELETE'],
},
},
});
const g2 = iam.Grant.addToPrincipal({
grantee,
actions: ['route53:ListHostedZonesByName'],
resourceArns: ['*'],
});

return g1.combine(g2);
public grantDelegation(grantee: iam.IGrantable): iam.Grant {
return makeGrantDelegation(grantee, this.hostedZoneArn);
}
}

Expand Down
Loading

0 comments on commit a93af2f

Please sign in to comment.