11

无论如何,我们可以将 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 用户数据?

4

6 回答 6

16

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

AWS 文档中没有关于您可以在模板中的确切位置使用这些引用的确切信息。目前的措辞是{{resolve:secretsmanager:...}}

“secretsmanager 动态引用可用于所有资源属性”

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

我有一个与 AWS 相关的有效支持案例,他们同意动态引用的行为没有充分记录。当我了解更多信息时,我会更新这个答案。

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

于 2019-02-13T06:12:20.397 回答
6

我不确定为什么没有为您正确扩展。但是,您可能不希望 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"
                        ]
                    ]
                }
            }
    }
}

这并不理想,因为它会在命令行上扩展密码。通过首先将密码放入文件并从那里读取密码然后粉碎文件,可能会更安全。

于 2018-12-05T18:26:41.260 回答
3

我可以确认@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.... 如果不想显式声明区域和账户,则需要使用通配符。埋在为 Secrets Manager 使用基于身份的策略(IAM 策略)的底部

如果您不关心拥有密钥的区域或账户,则必须为 ARN 的区域和账户 ID 号字段指定通配符 *(不是空字段)。

也许不太重要,也不太可能导致意外失败,但仍然值得注意:

使用 '??????' 作为通配符来匹配 Secrets Manager 分配的 6 个随机字符,可以避免使用“*”通配符时出现的问题。如果您使用语法“another_secret_name-*”,它不仅匹配具有 6 个随机字符的预期秘密,而且还匹配“another_secret_name-a1b2c3”。使用 '??????' 语法使您能够安全地授予对尚不存在的密钥的权限。

于 2019-04-10T04:30:28.670 回答
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,这也很难阅读。

于 2019-08-22T21:46:08.030 回答
1

正确的方法是调用秘密经理来获取您的数据,这是我成功的方法:

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'
于 2020-05-11T08:59:15.680 回答
-1

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-参数版本/

于 2021-05-03T05:55:58.743 回答