CDK DnsValidatedCertificate:当托管区域是父账户的一部分时,可以在链接的 AWS 账户中创建证



我正在尝试使用 AWS 的云开发工具包为我网站的某些子域创建 SSL 证书。问题是我正在使用 AWS 组织,并且相关资源属于不同的 AWS 账户。我的域的托管区域是我们主账户的一部分,但我正在运行 CDK 以在链接账户中部署堆栈。这意味着DnsValidatedCertificate类能够请求新证书(堆栈回滚后,它们在 ACM 中仍然可见),但在尝试创建 DNS 记录以自动验证请求时会引发错误。

这是错误(我的帐号和堆栈名称已编辑):

5/6 | 22:44:14 | CREATE_FAILED        | AWS::CloudFormation::CustomResource | SubSubDomainsCertificate/CertificateRequestorResource/Default (SubSubDomainsCertificateCertificateRequestorResourceBC626C85) Failed to create resource. User: arn:aws:sts::123456789012:assumed-role/MyStack-SubSubDomainsCertificateCertificat-16QRI74P8POO2/MyStack-SubSubDomainsCertificateCertificat-BXZ55WHIH1XC is not authorized to access this resource
new CustomResource (C:reposmy-projectnode_modules@aws-cdkaws-cloudformationlibcustom-resource.ts:92:21)
_ new DnsValidatedCertificate (C:reposmy-projectnode_modules@aws-cdkaws-certificatemanagerlibdns-validated-certificate.ts:81:29)
_ new MyStack (C:reposmy-project.elasticbeanstalkapi-stack.js:91:25)

这是相关的CDK代码(再次,HZ和域已编辑):

// Executed with `cdk deploy --profile profileForLinkedAwsAccount`
const hostedZone = route53.HostedZone.fromHostedZoneAttributes(
this,
'MyDomainHostedZone',
{
hostedZoneId: 'Z2ABC1234RYN', // in master AWS account
zoneName: 'mydomain.com.'
}
);
const certificate = new certificatemanager.DnsValidatedCertificate(
this,
'SubSubDomainsCertificate',
{
domainName: `*.demo.mydomain.com`,
hostedZone,
region: 'us-east-1',
validationMethod: certificatemanager.ValidationMethod.DNS // ???
}
);

那么,有没有办法配置 CDK 以允许 DNS 验证自动发生?还是我需要使用不同的配置文件作为第二步执行此操作?

编辑:根据迈克尔的建议,我向AWS账户添加了一个名为LinkedAccountCertValidatorRole的角色。我附加到角色的托管策略及其信任关系如下所示。不幸的是,我仍然收到同样的错误。此外,"访问顾问"选项卡指示此角色从未使用该策略。

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "route53:ChangeResourceRecordSets",
"Resource": "arn:aws:route53:::hostedzone/Z2ABC1234RYN"
}
]
}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "sts:AssumeRole",
"Condition": {}
}
]
}

为了完整起见,我将在这里发布简单的答案:使用 Certificate 类而不是 DnsValidatedCertificate。我可以让 CDK 创建证书请求,但不能让它尝试自动验证子域。这意味着我必须:

  1. 去链接账户的亚马逊证书管理器中查找请求,
  2. 检查(或导出)它要求添加的 CNAME 记录,以及
  3. 切换到主 AWS 账户并在 Route53 中添加记录。
// Executed with `cdk deploy --profile profileForLinkedAwsAccount`
const certificate = new certificatemanager.Certificate(this, 'SubSubDomainsCertificate', {
domainName: `*.${SUBDOMAIN}.mydomain.com`,
validationMethod: ValidationMethod.DNS
});

目前,我已经确定了此选项,但是完全自动化该过程会很好。

IAM可能很难做到正确。首先,您创建的角色必须与用户/帐户/组具有信任关系,而不是可以代入该角色。我没有看到您在 OP 中提到过这一点。我不知道CDK是什么,所以我无法清楚地了解你在做什么。

角色对可以执行的操作具有权限。还有一个信任关系部分,用于定义谁或什么可以承担该角色。

信任关系应该有一个映射到组织的主帐户,例如......

在主账户中创建附加权限的角色:

My_Role_To_Assume
Assign Permissions in Master:
Trust Relationship(Master Account)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::0123456789012:root"
},
"Action": "sts:AssumeRole"
}
]
}

在主账户中创建一个组,并将用户分配到该组。组权限应具有一个策略文档,其中显示允许用户代入的角色和子帐号。

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole"
],
"Resource": [
"arn:aws:iam::987654321098:role/My_Role_To_Assume",
"arn:aws:iam::567890123456:role/My_Other_Role_Assume"
]
}
]
}

然后在帐户中,您希望角色能够访问。创建一个具有相同名称的角色(不必如此,但记住角色的用途要容易得多)。

My_Role_To_Assume
Assign Permissions for role in sub-account:
Attach Trust Realtionship policy for sub-account role to trust master account:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::0123456789012:root"
},
"Action": "sts:AssumeRole"
}
]
}

您可以调整每个账户中的权限集,以提供对资源的更精细的控制/访问权限。通常在主账户中,您可能没有任何权限,但 IAM 密码、密钥管理等除外。

这种方法效果很好,总体要点是您在根级别创建信任关系,但主账户中的组策略决定了该用户/组在子账户中可以担任哪些角色。

由于您使用的是 CLI,因此在子账户中创建或更新资源之前,您必须发出 aws sts 调用以代入角色。有一些脚本可以为您处理此问题。

例:

#! /bin/bash
#
# Dependencies:
#   yum install -y jq
#
# Setup:
#   chmod +x ./assume_cloudadmin_role.sh
#
# Execute:
#   source ./assume_cloudadmin_role.sh
#
# Description:
#   Makes assuming an AWS IAM role (+ exporting new temp keys) easier. You're users access key and secret must allow you to assume the role in the sts CLI call.
unset  AWS_SESSION_TOKEN
export AWS_ACCESS_KEY_ID=<place_your_key_here> #Master Account API Key
export AWS_SECRET_ACCESS_KEY=<place_your_secret_here>#Master Account API Secret
export AWS_REGION=us-east-1
temp_role=$(aws sts assume-role 
--role-arn "arn:aws:iam::0123456789012:role/My_Role_To_Assume" 
--role-session-name "temp_cli_role")
export AWS_ACCESS_KEY_ID=$(echo $temp_role | jq -r .Credentials.AccessKeyId)
export AWS_SECRET_ACCESS_KEY=$(echo $temp_role | jq -r .Credentials.SecretAccessKey)
export AWS_SESSION_TOKEN=$(echo $temp_role | jq -r .Credentials.SessionToken)
env | grep -i AWS_

此调用将设置您的访问密钥和密钥 + 会话令牌,以对子账户执行操作。

希望你能让它工作!

截至本答案发布之日,随着 API 的更新,CDK 允许这种类型的 DNS 验证。我已成功在预先存在的托管区域中创建了一个具有子域和多个备用域的证书。但是,我没有使用链接的帐户,所以我还没有检查这方面是否有效。

DnsValidatedCertificate上,输入属性validation可以获取调用CertificateValidation.fromDnsMultiZone的结果。此静态成员调用采用一个对象,该对象的键/值对表示证书中的域及其IHostedZone对象。您可以通过调用 CDK Route53 的HostedZone.fromLookup(this, id, { domainName: domain })来获取对托管区域的引用。

把所有的东西放在一起:

import { HostedZone } from '@aws-cdk/aws-route53';
import { Certificate, CertificateValidation } from '@aws-cdk/aws-certificatemanager';
// within the stack...
const hzone = HostedZone.fromLookup(this, 'hz', { domainName: 'www.example.com' });
const cert  = new Certificate(this, 'cert', {
domainName: site,
validation: CertificateValidation.fromDnsMultiZone({ 'www.example.com': hzone }),
region: 'us-east-1',
});

注意:您可能需要对上述代码片段进行一些调整,具体取决于您的 DNS 配置。例如,www.example.com可以是托管区域example.com中的 CNAME 或 A 记录。在这种情况下,对fromLookup的调用应使用example.com作为domainName,而不是www子域。

此外,此过程还会创建一个文件cdk.context.json,该文件保留托管区域引用的 DNS 查找上下文。下面是关于需要将此文件签入源代码管理的完整讨论线程。

CDK v2 有一个 CrossAccountZoneDelegationRecord 类,允许您设置角色,与 Michael Quale 的答案非常相似,但完全在 CDK 中。

从 API 文档:

要将 NS 记录添加到不同账户中的托管区域,您可以执行 以后:

在包含父托管区域的账户中:

const parentZone = new route53.PublicHostedZone(this, 'HostedZone', {
zoneName: 'someexample.com',
crossAccountZoneDelegationPrincipal: new iam.AccountPrincipal('12345678901'),
crossAccountZoneDelegationRoleName: 'MyDelegationRole',
});

在包含要委派的子区域的帐户中:

const subZone = new route53.PublicHostedZone(this, 'SubZone', {
zoneName: 'sub.someexample.com',
});
// import the delegation role by constructing the roleArn
const delegationRoleArn = Stack.of(this).formatArn({
region: '', // IAM is global in each partition
service: 'iam',
account: 'parent-account-id',
resource: 'role',
resourceName: 'MyDelegationRole',
});
const delegationRole = iam.Role.fromRoleArn(this, 'DelegationRole', delegationRoleArn);
// create the record
new route53.CrossAccountZoneDelegationRecord(this, 'delegate', {
delegatedZone: subZone,
parentHostedZoneName: 'someexample.com', // or you can use parentHostedZoneId
delegationRole,
});

相关内容

  • 没有找到相关文章

最新更新