如何设置我的 HostedZone 以便它委托给另一个 AWS 账户中的父 DNS 记录?
How can I set up my HostedZone so that it delegates to a parent DNS record in another AWS account?
简介
我有一些使用 CDK 创建 API 网关和 Lambda 的 TypeScript 代码。它可以工作并部署到标准 AWS URL。到目前为止一切顺利。
我现在需要转移 API 网关,以便它在自定义域上运行,以便它可以在网络应用程序中设置 cookie。事实证明这要难得多,我怀疑我遇到困难是因为我同时是 TypeScript、AWS 和 CDK 的新手。网上有很多文档资源,但大多数都需要我重写我现有的宝贵的小工作代码,我不愿意这样做。
我手动创建了一个证书,因为这需要验证,因此在代码中创建它没有意义。除此之外,我希望所有其他资源都由堆栈中的 CDK 代码创建。在我看来,如果我必须手动配置东西,它就违背了 CDK 的目的。
问题
下面的代码部署了我需要的一切 gatekeeper.d.aws.example.com
- HostedZone、ARecord、LambdaRestApi 和 Function (lambda)。但是它不起作用,因为新分配给 gatekeeper.d.aws.example.com
的 NS 记录与父 d.aws.example.com
.
中的记录不匹配
我认为这意味着虽然 d.aws.example.com
是“已知的”,但 gateway
子域不能委托给它。
这是我的工作代码:
// Create the lambda resource
const referrerLambda = new lambda.Function(this, 'EisReferrerLambda', {
runtime: lambda.Runtime.NODEJS_14_X,
handler: 'index.handler',
code: lambda.Code.fromAsset(path.join(__dirname, '../../src/lambda')),
environment: env
});
// Set up the domain name on which the API should appear
const domainName = 'gatekeeper.d.aws.example.com';
// TODO need to fetch it with an env var? Or read from environment?
const certificateArn = 'arn:aws:acm:us-east-1:xxx:certificate/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy';
const certificate = acm.Certificate.fromCertificateArn(this, 'SslCertificate', certificateArn);
const hostedZone = new route53.HostedZone(this, 'EisReferrerHostedZone', {
zoneName: domainName
});
// Add an A record
new route53.ARecord(this, 'DnsRecord', {
zone: hostedZone,
target: route53.RecordTarget.fromAlias(new targets.ApiGateway(apiGateway)),
});
// I think I need a DomainNameOptions object
const dno : DomainNameOptions = { certificate, domainName };
// Create the APIG resource
// See https://intro-to-cdk.workshop.aws/the-workshop/4-create-apigateway.html
const apiGateway = new apigw.LambdaRestApi(this, "EisReferrerApi", {
handler: referrerLambda,
// proxy = on means that the lambda handles all requests to the APIG,
// instead of just explicit resource endpoints
proxy: false,
// deploy = on means that we get a default stage of "prod", I don't want
// that - I'm creating a custom Deployment anyway
deploy: false,
// Point to a domain name options object
domainName: dno
});
// Create an endpoint in the APIG
// https://docs.aws.amazon.com/cdk/api/latest/docs/aws-apigateway-readme.html#defining-apis
const items = apiGateway.root.addResource('gatekeeper');
items.addMethod('GET'); // GET /default/gatekeeper
// The deployment resource is just needed by the Stage system
const deployment = new apigw.Deployment(
this,
'EisReferrerDeployment',
{ api: apiGateway }
);
// Create a Stage (this affects the first component in the path
const stageName = 'default';
apiGateway.deploymentStage = new apigw.Stage(
this,
stageName,
{ deployment, stageName }
);
问题
从代码中可以看出,我找到了创建A记录的方法,但是creating/modifying NS记录似乎更难。首先,似乎没有 NSRecord class,至少基于从我的 IDE 自动完成中探索 class 结构。
一个基本的解决方案将允许我使用在别处(在“拥有”该域的 AWS 帐户中)设置的固定值创建 NS 记录。更好的解决方案是读取这些记录是什么,然后使用它们。
更新
为了看看我的思路是否正确,我有运行这个部署代码,手动修改了HostedZone中自动分配的NS记录以匹配parent中的记录(在其他账户中).我想我必须等待此更改渗入 DNS 系统,然后我会更新结果。
更新 2
我的手动调整没有用。因此,我找到了 new thing to try(请参阅“将 NS 记录添加到不同帐户中的 HostedZone”):
// Commented out from earlier code
// const hostedZone = new route53.HostedZone(this, 'EisReferrerHostedZone', {
// zoneName: domainName
// });
// In the account containing the HostedZone
const parentZone = new route53.PublicHostedZone(this, 'HostedZone', {
zoneName: 'd.aws.example.com',
crossAccountZoneDelegationPrincipal: new iam.AccountPrincipal('12345678012')
});
// In this account
const subZone = new route53.PublicHostedZone(this, 'SubZone', {
zoneName: domainName
});
new route53.CrossAccountZoneDelegationRecord(this, 'delegate', {
delegatedZone: subZone,
parentHostedZoneId: parentZone.hostedZoneId,
delegationRole: parentZone.crossAccountDelegationRole
});
这听起来正是我需要的,但我担心这里的 AWS 文档已经过时了 - crossAccountDelegationRole
在我的 IDE 中呈现为红色,并且由于未定义而崩溃 cdk diff
是 运行.
更新 3
我假设上面提到的 属性 是一个拼写错误或对库的过时版本的引用。我现在这样做:
new route53.CrossAccountZoneDelegationRecord(this, 'delegate', {
delegatedZone: subZone,
parentHostedZoneId: parentZone.hostedZoneId,
delegationRole: parentZone.crossAccountZoneDelegationRole
});
这感觉非常接近,但它崩溃了:
Failed to create resource. AccessDenied: User: arn:aws:sts::xxxxxxxxxxxx:assumed-role/CustomCrossAccountZoneDelegationC-xxx is not authorized to
perform: sts:AssumeRole on resource: arn:aws:iam::yyyyyyyyyyyy:role/HostedZoneCrossAccountZoneDelegat-yyy
我想知道我是否需要为其他帐户声明 IAM 信用?我有。
我不确定为什么需要权限 - 难道它不能读取其他帐户中的 NS 记录并将它们复制到本地帐户吗?无论如何,另一个帐户中的 DNS 是 public。
我愿意研究修复 IAM 错误,但这并不像是在黑暗中拍摄。我可能会再花两个小时慢慢地解决那个子问题,结果发现整个事情会因为另一个原因而失败。
更新 4
我已经在远程账户中创建了一个“角色”,以将“AmazonRoute53FullAccess”权限授予我要部署 CDK 的账户。但是我仍然收到 AccessDenied 错误。我想知道我是否需要以某种方式显式调用该远程角色;我该怎么做?
我咨询了两位AWS专家,他们都不赞成跨账户操作。一个说:
This is an anti-pattern, since it requires permissions to remain even after the stack is deployed. What happens if a cross-account operation has to roll back and that permission is revoked? It would result in the app being stuck in a middle/undefined state (the local part is rolled back, but the remote part cannot be rolled back due to an access violation).
对方建议:
Cross-account CDK is hard.
It is much better to split your stacks into two or more operations, so you can run them independently. This applies nicely to "one off" operations like DNS delegation - realistically you are not going to change the zone delegation for your Stack unless you destroy it, which you are not going to do until you actually don't need it. Thus, there is no reason for the zone information to change for the lifetime of the system.
This also works well where you have an app and a database, and you want the ability to take down your app without destroying the data.
因此,从某些人会说“不要这样做”的意义上来说,这是一个答案。但是,AWS 似乎有能力做到这一点,所以仍然欢迎这方面的答案。
今天想做同样的事情,你的 post 让我完成了 90%,谢谢!我最终让它与不同的 IAM 委托人(组织)一起工作,这对我的用例来说没问题。
crossAccountZoneDelegationPrincipal 允许访问托管 subzones 的帐户,以访问您的 root zone 并为子区域写入委托 (NS) 记录。
对于我的用例,所有帐户都位于同一组织内,因此我创建了这样的根区域 ->
const rootZone = new route53.PublicHostedZone(this, 'rootZone', {
zoneName: `root.zone`,
crossAccountZoneDelegationPrincipal: new iam.OrganizationPrincipal('o-####')
});
这会使用以下策略设置 IAM 角色;
"Version": "2012-10-17",
"Statement": [
{
"Action": "route53:ChangeResourceRecordSets",
"Resource": "arn:aws:route53:::hostedzone/#####",
"Effect": "Allow"
}
]
}
以及以下信任策略;
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"aws:PrincipalOrgID": "o-####"
}
}
}
]
}
这实际上允许拥有该 OrgID 的任何人在根区域中写入记录。
在我的分区中,我 运行 使用这个;
const subZone = new route53.PublicHostedZone(this, 'SubZone', {
zoneName: 'sub.root.zone'
});
const delegationRole = iam.Role.fromRoleArn(this, 'delegationRole', 'arn:aws:iam::###:role/###')
new route53.CrossAccountZoneDelegationRecord(this, 'delegate', {
delegatedZone: subZone,
parentHostedZoneId: '###',
delegationRole: delegationRole
});
这最终在我的子区域的根区域中创建了授权记录。如果组织主体不适合您的用例并且您仍然需要授予多个帐户该权限,请尝试复合主体 https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iam.CompositePrincipal.html
还想解决另一个答案中提出的关于它是一个反模式和跨账户 CDK 很难的问题。这不是真正的跨账户 CDK。这是利用 AWS 提供的模式(特别是启动 lambda 来执行根区中子区记录的配置)。
希望对你有用!
简介
我有一些使用 CDK 创建 API 网关和 Lambda 的 TypeScript 代码。它可以工作并部署到标准 AWS URL。到目前为止一切顺利。
我现在需要转移 API 网关,以便它在自定义域上运行,以便它可以在网络应用程序中设置 cookie。事实证明这要难得多,我怀疑我遇到困难是因为我同时是 TypeScript、AWS 和 CDK 的新手。网上有很多文档资源,但大多数都需要我重写我现有的宝贵的小工作代码,我不愿意这样做。
我手动创建了一个证书,因为这需要验证,因此在代码中创建它没有意义。除此之外,我希望所有其他资源都由堆栈中的 CDK 代码创建。在我看来,如果我必须手动配置东西,它就违背了 CDK 的目的。
问题
下面的代码部署了我需要的一切 gatekeeper.d.aws.example.com
- HostedZone、ARecord、LambdaRestApi 和 Function (lambda)。但是它不起作用,因为新分配给 gatekeeper.d.aws.example.com
的 NS 记录与父 d.aws.example.com
.
我认为这意味着虽然 d.aws.example.com
是“已知的”,但 gateway
子域不能委托给它。
这是我的工作代码:
// Create the lambda resource
const referrerLambda = new lambda.Function(this, 'EisReferrerLambda', {
runtime: lambda.Runtime.NODEJS_14_X,
handler: 'index.handler',
code: lambda.Code.fromAsset(path.join(__dirname, '../../src/lambda')),
environment: env
});
// Set up the domain name on which the API should appear
const domainName = 'gatekeeper.d.aws.example.com';
// TODO need to fetch it with an env var? Or read from environment?
const certificateArn = 'arn:aws:acm:us-east-1:xxx:certificate/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy';
const certificate = acm.Certificate.fromCertificateArn(this, 'SslCertificate', certificateArn);
const hostedZone = new route53.HostedZone(this, 'EisReferrerHostedZone', {
zoneName: domainName
});
// Add an A record
new route53.ARecord(this, 'DnsRecord', {
zone: hostedZone,
target: route53.RecordTarget.fromAlias(new targets.ApiGateway(apiGateway)),
});
// I think I need a DomainNameOptions object
const dno : DomainNameOptions = { certificate, domainName };
// Create the APIG resource
// See https://intro-to-cdk.workshop.aws/the-workshop/4-create-apigateway.html
const apiGateway = new apigw.LambdaRestApi(this, "EisReferrerApi", {
handler: referrerLambda,
// proxy = on means that the lambda handles all requests to the APIG,
// instead of just explicit resource endpoints
proxy: false,
// deploy = on means that we get a default stage of "prod", I don't want
// that - I'm creating a custom Deployment anyway
deploy: false,
// Point to a domain name options object
domainName: dno
});
// Create an endpoint in the APIG
// https://docs.aws.amazon.com/cdk/api/latest/docs/aws-apigateway-readme.html#defining-apis
const items = apiGateway.root.addResource('gatekeeper');
items.addMethod('GET'); // GET /default/gatekeeper
// The deployment resource is just needed by the Stage system
const deployment = new apigw.Deployment(
this,
'EisReferrerDeployment',
{ api: apiGateway }
);
// Create a Stage (this affects the first component in the path
const stageName = 'default';
apiGateway.deploymentStage = new apigw.Stage(
this,
stageName,
{ deployment, stageName }
);
问题
从代码中可以看出,我找到了创建A记录的方法,但是creating/modifying NS记录似乎更难。首先,似乎没有 NSRecord class,至少基于从我的 IDE 自动完成中探索 class 结构。
一个基本的解决方案将允许我使用在别处(在“拥有”该域的 AWS 帐户中)设置的固定值创建 NS 记录。更好的解决方案是读取这些记录是什么,然后使用它们。
更新
为了看看我的思路是否正确,我有运行这个部署代码,手动修改了HostedZone中自动分配的NS记录以匹配parent中的记录(在其他账户中).我想我必须等待此更改渗入 DNS 系统,然后我会更新结果。
更新 2
我的手动调整没有用。因此,我找到了 new thing to try(请参阅“将 NS 记录添加到不同帐户中的 HostedZone”):
// Commented out from earlier code
// const hostedZone = new route53.HostedZone(this, 'EisReferrerHostedZone', {
// zoneName: domainName
// });
// In the account containing the HostedZone
const parentZone = new route53.PublicHostedZone(this, 'HostedZone', {
zoneName: 'd.aws.example.com',
crossAccountZoneDelegationPrincipal: new iam.AccountPrincipal('12345678012')
});
// In this account
const subZone = new route53.PublicHostedZone(this, 'SubZone', {
zoneName: domainName
});
new route53.CrossAccountZoneDelegationRecord(this, 'delegate', {
delegatedZone: subZone,
parentHostedZoneId: parentZone.hostedZoneId,
delegationRole: parentZone.crossAccountDelegationRole
});
这听起来正是我需要的,但我担心这里的 AWS 文档已经过时了 - crossAccountDelegationRole
在我的 IDE 中呈现为红色,并且由于未定义而崩溃 cdk diff
是 运行.
更新 3
我假设上面提到的 属性 是一个拼写错误或对库的过时版本的引用。我现在这样做:
new route53.CrossAccountZoneDelegationRecord(this, 'delegate', {
delegatedZone: subZone,
parentHostedZoneId: parentZone.hostedZoneId,
delegationRole: parentZone.crossAccountZoneDelegationRole
});
这感觉非常接近,但它崩溃了:
Failed to create resource. AccessDenied: User: arn:aws:sts::xxxxxxxxxxxx:assumed-role/CustomCrossAccountZoneDelegationC-xxx is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::yyyyyyyyyyyy:role/HostedZoneCrossAccountZoneDelegat-yyy
我想知道我是否需要为其他帐户声明 IAM 信用?我有。
我不确定为什么需要权限 - 难道它不能读取其他帐户中的 NS 记录并将它们复制到本地帐户吗?无论如何,另一个帐户中的 DNS 是 public。
我愿意研究修复 IAM 错误,但这并不像是在黑暗中拍摄。我可能会再花两个小时慢慢地解决那个子问题,结果发现整个事情会因为另一个原因而失败。
更新 4
我已经在远程账户中创建了一个“角色”,以将“AmazonRoute53FullAccess”权限授予我要部署 CDK 的账户。但是我仍然收到 AccessDenied 错误。我想知道我是否需要以某种方式显式调用该远程角色;我该怎么做?
我咨询了两位AWS专家,他们都不赞成跨账户操作。一个说:
This is an anti-pattern, since it requires permissions to remain even after the stack is deployed. What happens if a cross-account operation has to roll back and that permission is revoked? It would result in the app being stuck in a middle/undefined state (the local part is rolled back, but the remote part cannot be rolled back due to an access violation).
对方建议:
Cross-account CDK is hard.
It is much better to split your stacks into two or more operations, so you can run them independently. This applies nicely to "one off" operations like DNS delegation - realistically you are not going to change the zone delegation for your Stack unless you destroy it, which you are not going to do until you actually don't need it. Thus, there is no reason for the zone information to change for the lifetime of the system.
This also works well where you have an app and a database, and you want the ability to take down your app without destroying the data.
因此,从某些人会说“不要这样做”的意义上来说,这是一个答案。但是,AWS 似乎有能力做到这一点,所以仍然欢迎这方面的答案。
今天想做同样的事情,你的 post 让我完成了 90%,谢谢!我最终让它与不同的 IAM 委托人(组织)一起工作,这对我的用例来说没问题。
crossAccountZoneDelegationPrincipal 允许访问托管 subzones 的帐户,以访问您的 root zone 并为子区域写入委托 (NS) 记录。
对于我的用例,所有帐户都位于同一组织内,因此我创建了这样的根区域 ->
const rootZone = new route53.PublicHostedZone(this, 'rootZone', {
zoneName: `root.zone`,
crossAccountZoneDelegationPrincipal: new iam.OrganizationPrincipal('o-####')
});
这会使用以下策略设置 IAM 角色;
"Version": "2012-10-17",
"Statement": [
{
"Action": "route53:ChangeResourceRecordSets",
"Resource": "arn:aws:route53:::hostedzone/#####",
"Effect": "Allow"
}
]
}
以及以下信任策略;
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"aws:PrincipalOrgID": "o-####"
}
}
}
]
}
这实际上允许拥有该 OrgID 的任何人在根区域中写入记录。
在我的分区中,我 运行 使用这个;
const subZone = new route53.PublicHostedZone(this, 'SubZone', {
zoneName: 'sub.root.zone'
});
const delegationRole = iam.Role.fromRoleArn(this, 'delegationRole', 'arn:aws:iam::###:role/###')
new route53.CrossAccountZoneDelegationRecord(this, 'delegate', {
delegatedZone: subZone,
parentHostedZoneId: '###',
delegationRole: delegationRole
});
这最终在我的子区域的根区域中创建了授权记录。如果组织主体不适合您的用例并且您仍然需要授予多个帐户该权限,请尝试复合主体 https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iam.CompositePrincipal.html
还想解决另一个答案中提出的关于它是一个反模式和跨账户 CDK 很难的问题。这不是真正的跨账户 CDK。这是利用 AWS 提供的模式(特别是启动 lambda 来执行根区中子区记录的配置)。
希望对你有用!