在AWS Cloudformation中指定Secret Manager值的动态引用

时间:2018-12-03 08:21:51

标签: amazon-web-services amazon-cloudformation aws-secrets-manager

无论如何,我们可以将dynamic references传递到Secret Manager到AWS Launch Config用户数据吗?

这是我尝试过的代码段:

"SampleLaunchConfig": {
            "Type": "AWS::AutoScaling::LaunchConfiguration",
             "Properties": {
                "ImageId": {
                    "Fn::FindInMap": [
                        "AWSRegionArch2AMI",
                        {
                            "Ref": "AWS::Region"
                        },
                        "AMI"
                    ]
                },
                "UserData": {
                    "Fn::Base64": {
                        "Fn::Join": [
                            "",
                            [
                                "#!/bin/bash -xe\n",
                                "yum update -y\n",
                                "useradd -p <<pwd>>{{resolve:secretsmanager:Credentials:SecretString:userName}}\n",
                                "\n"
                            ]
                        ]
                    }
                }
        }
    }

在获取useradd时似乎出错:无效的用户名'{{resolve:secretsmanager:Credentials:SecretString:userName}}'

如何将Secret Manager机密值传递给cloudformation用户数据?

6 个答案:

答案 0 :(得分:3)

似乎{{resolve:...}}动态引用仅在模板中的某些上下文中扩展。

AWS文档中没有关于可以使用这些引用的确切位置的确切信息。关于{{resolve:secretsmanager:...}}的当前措辞如下:

  

“ secretsmanager动态引用可以在所有资源属性中使用”

但是,这与您的示例相矛盾,并且我还观察到动态引用未能在CloudFormation :: Init数据内部解析。

我正在就此问题与AWS展开积极的支持案例,他们同意没有充分记录动态引用的行为。当我了解更多信息时,我将更新此答案。

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html#dynamic-references-secretsmanager

答案 1 :(得分:2)

我不确定为什么无法为您正确展开此内容。但是,您可能不希望CFN在用户数据中扩展秘密,因为密码将嵌入在EC2控制台中可见的base64编码的用户数据脚本中。

相反,您应该利用以下事实:您有一个脚本在主机上执行,并在脚本执行时调用秘密管理器(警告未经测试):

"SampleLaunchConfig": {
        "Type": "AWS::AutoScaling::LaunchConfiguration",
         "Properties": {
            "ImageId": {
                "Fn::FindInMap": [
                    "AWSRegionArch2AMI",
                    {
                        "Ref": "AWS::Region"
                    },
                    "AMI"
                ]
            },
            "UserData": {
                "Fn::Base64": {
                    "Fn::Join": [
                        "",
                        [
                            "#!/bin/bash -xe\n",
                            "yum update -y\n",
                            "yum install -y jq\n",
                            !Sub "useradd -p `aws --region ${AWS::Region} secretsmanager get-secret-value --secret-id Credentials --query SecretString --output text | jq -r .passwordKey` `aws --region ${AWS::Region} secretsmanager get-secret-value --secret-id Credentials --query SecretString --output text | jq -r .userName`\n",
                            "\n"
                        ]
                    ]
                }
            }
    }
}

这并不理想,因为它在命令行上扩展了密码。首先将密码放在文件中,然后从那里读取密码,然后将其切碎,可以使其更加安全。

答案 2 :(得分:1)

我可以确认@JoeB的“警告未经测试”的答案有效,但需要注意的是,有关机器必须具有读取机密的权限。您将需要类似

的内容
  MyInstancePolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: MyPolicy
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          -
            Effect: Allow
            Action:
              - secretsmanager:GetSecretValue
            Resource: !Join
              - ''
              - - !Sub "arn:aws:secretsmanager:${AWS::Region}:"
                - !Sub "${AWS::AccountId}:secret:Credentials-??????"

请注意以下几点:

与S3存储桶不同,您无法执行arn:aws:secretsmanager:::secret...。如果您不想显式声明区域和帐户,则需要使用通配符。埋在Using Identity-based Policies (IAM Policies) for Secrets Manager

的底部
  

如果您不关心拥有秘密的地区或帐户,   您必须为通配符指定一个通配符*(而不是一个空字段)   ARN的区域和帐户ID号字段。

也许不那么重要,并且不太可能导致意外故障,但是仍然值得注意:

  

使用'??????'作为通配符来匹配6个随机字符   由Secrets Manager分配,可以避免出现以下问题:   请改用'*'通配符。如果使用语法   “ another_secret_name- *”,它不仅与   6个随机字符,但也可以匹配   “ another_secret_name-a1b2c3”。使用 '??????'句法   使您能够安全地将权限授予尚未存在的机密   存在。

答案 3 :(得分:1)

@JoeB回答中的变体形式:

Resources:
  SampleLaunchConfig:
    Type: AWS::AutoScaling::LaunchConfiguration
    Properties:
      ImageId: !FindInMap [ AWSRegionArch2AMI, !Ref: 'AWS::Region', AMI ]
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash -xe
          exec > >(tee /var/log/user-data.log | logger -t user-data) 2>&1

          yum update -y
          yum install -y jq

          username=$(aws secretsmanager get-secret-value --secret-id Credentials \
                                                         --query SecretString \
                                                         --region ${AWS::Region} --output text | jq -r .userName)
          password=$(aws secretsmanager get-secret-value --secret-id Credentials \
                                                         --query SecretString \
                                                         --region ${AWS::Region} --output text | jq -r .passwordKey)
          useradd -p "$password" $username
如今,

JSON中的UserData让人很难看。

我还添加了一种将UserData逻辑拆分到其自己的日志文件中的技术,否则它会进入cloud-init.log,这也很难读取。

答案 4 :(得分:1)

执行此操作的正确方法是致电Secret Manager以获取您的数据,这是我成功进行的操作:

SftpCredsUserPasswordSecret:
    Type: 'AWS::SecretsManager::Secret'
    Properties:
      Name: 'sftp-creds-user-password-secret'
      Description: DB Credentials
      GenerateSecretString:
        SecretStringTemplate: '{"username":"sftpuser"}'
        GenerateStringKey: "password"
        PasswordLength: 30
        ExcludePunctuation: true

  Ec2SftpRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument: # Tells that Ec2 can assume this role
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - sts:AssumeRole
            Principal:
              Service:
                - ec2.amazonaws.com
      Policies: # Tells that you can call for the secret value
        - PolicyName: "root"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action: "secretsmanager:GetSecretValue"
                Resource: !Ref SftpCredsUserPasswordSecret
      RoleName: 'role-ec2-sftp'

  Ec2SftpIamInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName:  'instance-profile-ec2-sftp'
      Roles:
        - !Ref Ec2SftpRole

  Ec2Sftp:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: "ami-06ce3edf0cff21f07"
      InstanceType: t2.micro
      SecurityGroupIds:
        - !ImportValue SgBridgeId
        - !ImportValue SgSftpId
      SubnetId: !ImportValue PublicSubnetAZbId
      KeyName: !Ref KeyName
      UserData:
        Fn::Base64: !Sub
        - |
          #!/bin/bash

          # Get Variables
          secret=`aws --region ${AWS::Region} secretsmanager get-secret-value --secret-id ${secretRef} --query SecretString --output text`
          sftpuser=`echo "$secret" | sed -n 's/.*"username":["]*\([^(",})]*\)[",}].*/\1/p'`
          sftppassword=`echo "$secret" | sed -n 's/.*"password":["]*\([^(",})]*\)[",}].*/\1/p'`

          # Create Sftp User
          adduser $sftpuser
          echo "$sftpuser:$sftppassword" | chpasswd

          # Configure sftp connection
          echo "" >> /etc/ssh/sshd_config
          echo "Match User $sftpuser" >> /etc/ssh/sshd_config
          echo "    PasswordAuthentication yes" >> /etc/ssh/sshd_config
          echo "    ForceCommand /usr/libexec/openssh/sftp-server" >> /etc/ssh/sshd_config

          # Restart the service
          systemctl restart sshd
        -
          secretRef: !Ref SftpCredsUserPasswordSecret
      IamInstanceProfile: !Ref Ec2SftpIamInstanceProfile
      Tags:
        - Key: Name
          Value: 'ec2-sftp'

答案 5 :(得分:0)

AWS CloudFormation 增强了 CloudFormation 模板中 AWS Systems Manager Parameter Store 参数的现有动态引用。您现在可以在 CloudFormation 模板中引用最新的 Systems Manager 参数值,而无需指定参数版本。

查看更多

https://aws.amazon.com/about-aws/whats-new/2021/04/now-reference-latest-aws-systems-manager-parameter-values-in-aws-cloudformation-templates-without-specifying-parameter-versions/