【问题标题】:Dynamic References to Specify Secret Manager Values in AWS Cloudformation在 AWS Cloudformation 中指定 Secret Manager 值的动态引用
【发布时间】:2019-05-04 12:26:36
【问题描述】:

我们是否可以将 dynamic references 传递给 Secret Manager 到 AWS Launch Config 用户数据?

这是我试过的代码sn-p:

"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 用户数据?

【问题讨论】:

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


    【解决方案1】:

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

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

    【讨论】:

    • 为了简化你可以使用--query而不是jq
    • @PawełPrażak SecretString 的值实际上是一个 JSON 字符串。换句话说,JSON 嵌套在 JSON 中。因此,这里--query SecretString 从响应中获取SecretString 的值,然后jq 获取JSON 字符串中userName 键的值。
    【解决方案2】:

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

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

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

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

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

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

    【讨论】:

    • 嘿,我想知道 AWS 支持是否设法提供有意义的回馈?
    • AWS Support 已确认动态引用在UserData 或CloudFormation::Init 属性中不解析。它们确实在许多其他资源属性中解析,并且在属性支持动态引用的情况下,您可以在函数调用(!Sub、!Join 等)中使用它们。仍然不清楚的是哪些属性支持这些引用,哪些不支持。我的支持案例仍在处理中。
    【解决方案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...。如果不想显式声明区域和账户,则需要使用通配符。埋在Using Identity-based Policies (IAM Policies) for Secrets Manager的底部

    如果您不关心拥有秘密的区域或帐户, 您必须为 ARN 的区域和帐号 ID 字段。

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

    使用'??????'作为通配符匹配 6 个随机字符 由 Secrets Manager 分配避免出现问题,如果您 请改用“*”通配符。如果你使用语法 “another_secret_name-*”,它不仅匹配预期的秘密 6 个随机字符,但它也匹配 “另一个秘密名称-a1b2c3”。使用 '??????'句法 使您能够安全地授予对尚未授权的密钥的权限 存在。

    【讨论】:

      【解决方案4】:

      @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,这也很难阅读。

      【讨论】:

        【解决方案5】:

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

        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'
        

        【讨论】:

          【解决方案6】:

          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/

          【讨论】:

          • 这似乎没有回答这个问题,即在 EC2 实例的 UserData 参数或启动配置中扩展动态引用。
          猜你喜欢
          • 2019-06-11
          • 2021-09-10
          • 2021-06-29
          • 2020-12-25
          • 2020-06-18
          • 2020-08-16
          • 2018-12-09
          • 2022-01-27
          • 2020-03-31
          相关资源
          最近更新 更多