【问题标题】:CDK DnsValidatedCertificate: Can create a certificate in a linked AWS account, when the hosted zone is part of the parent account?CDK DnsValidatedCertificate:当托管区域是父账户的一部分时,可以在链接的 AWS 账户中创建证书吗?
【发布时间】:2020-01-25 20:21:15
【问题描述】:

我正在尝试使用 AWS 的云开发工具包为我网站的某些子域创建 SSL 证书。问题是我正在使用 AWS Organizations 并且相关资源属于不同的 AWS 账户。我的域的hosted zone 是我们主帐户的一部分,但我正在运行 CDK 以在链接帐户中部署堆栈。这意味着DnsValidatedCertificate class 能够请求新证书(在堆栈回滚后它们仍然在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:\repos\my-project\node_modules\@aws-cdk\aws-cloudformation\lib\custom-resource.ts:92:21)
        \_ new DnsValidatedCertificate (C:\repos\my-project\node_modules\@aws-cdk\aws-certificatemanager\lib\dns-validated-certificate.ts:81:29)
        \_ new MyStack (C:\repos\my-project\.elasticbeanstalk\api-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 验证自动发生?还是我需要在第二步中使用不同的配置文件?

编辑:根据 Michael 的建议,我向 master AWS 账户添加了一个名为 LinkedAccountCertValidatorRole 的角色。我附加到角色的托管策略及其信任关系如下所示。不幸的是,我仍然遇到同样的错误。此外,Access Advisor 选项卡表明该角色从未使用过该策略。

{
    "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": {}
    }
  ]
}

【问题讨论】:

    标签: amazon-web-services amazon-cloudformation amazon-iam aws-cdk aws-sts


    【解决方案1】:

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

    1. 去关联帐户的Amazon Certificate Manager 中查找请求,
    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
    });
    

    我现在已经选择了这个选项,但是完全自动化这个过程会很好。

    【讨论】:

    • 我发现 CDK 与证书管理器的关系非常模糊。我假设DnsValidatedCertificate 实际上会尝试获取我手动创建的证书,因为我在自动化时也遇到了 DNS 验证问题。但是,当我使用DnsValidatedCertificate 时,它仍然会创建新证书,而不是使用已经可用的证书。 DNS 验证有效,因为我之前已经验证了证书,但现在我最终得到了重复......我现在试试你的方法。
    • 在下面查看我的答案;我相信这会以您希望的方式解决它,并且现在 API 已经升级,完全自动化。
    【解决方案2】:

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

    角色有权执行可以执行的操作。还有一个信任关系部分定义了谁或什么可以担任该角色。

    受信任的关系应该映射到 orgs 主帐户,例如......

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

    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_
    

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

    希望你能成功!

    【讨论】:

    • 抱歉,您似乎回复了我最初的帖子,然后我重写了很多,一旦我发现了根本原因。不过,我认为你在正确的轨道上。我想我需要向有权创建 DNS 记录的主账户添加一个角色,然后使用 IAM>Roles 中的信任关系选项卡允许我在链接账户中的 CLI 用户组担任该角色。我试试看。
    • 知道了,我看到你更新了问题。对于您的信任关系,您只需添加我在编辑后的回复中添加的内容,只要您的主帐户具有正确的用户/角色映射,它就可以工作。是的,根是正确的。在主帐户中,您还将拥有一个角色并将用户分配给该角色。
    • 嗯。还没有运气。 ARN 的 root 部分是否存在潜在问题?因为,它是一个假定的角色,它试图连接到主帐户并担任第二个角色?
    • 没有根部分是正确的。编辑了我的答案以使其更清楚。
    【解决方案3】:

    截至本答案发布之日,随着 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 查找上下文。这是一个完整的discussion thread,关于需要将此文件签入源代码管理。

    【讨论】:

      猜你喜欢
      • 2017-11-08
      • 2018-11-24
      • 2017-11-18
      • 1970-01-01
      • 2022-10-13
      • 2012-10-30
      • 1970-01-01
      • 1970-01-01
      • 2019-01-18
      相关资源
      最近更新 更多