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

feat(route53): add support for grantDelegation on imported PublicHostedZone #26333

Merged
merged 10 commits into from
Aug 18, 2023
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