【问题标题】:CloudFormation SecretTargetAttachment return SecretString is not valid JSONCloudFormation SecretTargetAttachment 返回 SecretString 是无效的 JSON
【发布时间】:2019-07-31 06:24:42
【问题描述】:

我正在尝试创建一个简单的 CloudFormation 堆栈,但它不起作用。这是我的 CloudFormation 模板。

Resources:
  MyDBSecrets:
    Type: AWS::SecretsManager::Secret
    Properties: 
      Description: 'This is password of mysql database'
      GenerateSecretString:
        PasswordLength: 16
        ExcludePunctuation: true
      Name: MyDBSecrets
  MyDBInstance:
    Type: AWS::RDS::DBInstance
    Properties:
      DBName: MyDBInstance
      AllocatedStorage: '20'
      DBInstanceClass: db.t3.micro
      Engine: mysql
      MasterUsername: 'testdb'
      MasterUserPassword: !Join ['', ['{{resolve:secretsmanager:', !Ref MyDBSecrets, ':SecretString}}' ]]
  SecretRDSInstanceAttachment:
    Type: "AWS::SecretsManager::SecretTargetAttachment"
    Properties:
      SecretId: !Ref MyDBSecrets
      TargetId: !Ref MyDBInstance
      TargetType: AWS::RDS::DBInstance

在创建堆栈时,我可以看到我的秘密资源已创建,我的 RDS 实例也已创建,但在 SecretTargetAttachment 上我收到 CREATE_FAILED with 'SecretString is not valid JSON' 错误。我错过了什么吗?

【问题讨论】:

    标签: amazon-web-services amazon-cloudformation


    【解决方案1】:

    Secrets Manager 通常以两种格式之一存储秘密:

    • 作为 JSON 对象的一部分 { "user": "master", "password": "password123" }
    • 或作为普通的测试秘密,例如password123

    Secrets Manager 文档推荐 JSON 版本,他们的示例都使用它。在 CloudFormation 中,这可以使用 SecretStringTemplate 生成,并且可以在动态字段插值中的秘密 ARN 之后使用 :SecretText:password 提取密码字段(例如)。

    此外,AWS::SecretsManager::SecretTargetAttachment 似乎需要这种格式,因为它将 RDS 实例存储在 JSON 对象的另一个字段中。这就是你的错误的原因。

    如果您将其与 ECS 一起使用,请注意:您不应在任务定义中使用动态插值,因为这将以纯文本形式保存,任何人都可以从控制台/cli 读取。相反,您应该使用带有 ValueFrom 秘密管理器的 Secrets 部分。不幸的是,这目前似乎不支持从 JSON blob 中提取字段。相反,您必须在 docker 容器中解析 JSON blob。

    【讨论】:

    【解决方案2】:

    Secrets Manager secrets 的参考模式,reference-key 段由几个段组成,包括 secret id、secret value key、version stage 和 version id。

    使用以下模式:{{resolve:secretsmanager:secret-id:secret-string:json-key:version-stage:version-id}}

    secret-id:用作密钥唯一标识符的名称或 Amazon 资源名称 (ARN)。要访问 AWS 账户中的密钥,您只需指定密钥名称。要访问其他 AWS 账户中的密钥,请指定密钥的完整 ARN。必需。

    secret-string:目前唯一支持的值是 SecretString。默认为 SecretString。

    json-key:指定要检索其值的键值对的键名。如果您不指定 json-key,CloudFormation 将检索整个密文。此段可能不包含冒号字符 (:)。

    version-stage:通过附加到版本的暂存标签指定要检索的机密版本。暂存标签用于在轮换过程中跟踪不同的版本。如果您使用 version-stage,则不要指定 version-id。如果您未指定版本阶段或版本 ID,则默认为检索版本阶段值为 AWSCURRENT 的版本。此段可能不包含冒号字符 ( : )。

    version-id:指定要在堆栈操作中使用的密钥版本的唯一标识符。如果指定 version-id,则不要指定 version-stage。如果您未指定版本阶段或版本 ID,则默认检索版本阶段值为 AWSCURRENT 的版本。此段可能不包含冒号字符 ( : )。

    欲了解更多信息,请转到here

    【讨论】:

    • 我尝试了此处docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/… 指定的所有可能选项,您可能已经注意到我的秘密是纯文本,我猜这个问题与“AWS::SecretsManager::SecretTargetAttachment”有关跨度>
    • 什么时候在没有 SecretRDSInstanceAttachment 的情况下直接应用我的秘密
    • 这不是我问题的正确答案我想知道为什么我会得到那个异常。
    • @YasirAli,因为关联只适用于 json。
    【解决方案3】:

    我发现我的问题的解决方案并不简单。我联系了 AWS 支持团队,他们帮助我解决了这个问题。 AWS 支持团队建议使用宏对模板进行自定义处理。

    Step1 : Macro.yml

        AWSTemplateFormatVersion: 2010-09-09
    Resources:
      TransformExecutionRole:
        Type: AWS::IAM::Role
        Properties:
          AssumeRolePolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Principal:
                  Service: [lambda.amazonaws.com]
                Action: ['sts:AssumeRole']
          Path: /
          Policies:
            - PolicyName: root
              PolicyDocument:
                Version: 2012-10-17
                Statement:
                  - Effect: Allow
                    Action: ['logs:*']
                    Resource: 'arn:aws:logs:*:*:*'
                  - Effect: Allow
                    Action: ['s3:*']
                    Resource: '*'
      TransformFunction:
        Type: AWS::Lambda::Function
        Properties:
          Code:
            ZipFile: |
                import traceback
                def handler(event, context):
                    response = {
                        "requestId": event["requestId"],
                        "status": "success"
                    }
                    try:
                        paramPassword= event["params"]["paramPassword"]
                        Description= event["fragment"]["Description"]
                        Name= event["fragment"]["Name"]
                        print(event)
                        print("starting macro execution")
                        fragment = {}
                        fragment['Name'] = Name
                        fragment['Description'] = Description
                        if paramPassword == "":
                          fragment['GenerateSecretString'] = {}
                          fragment['GenerateSecretString']['PasswordLength'] = 16
                          fragment['GenerateSecretString']['ExcludePunctuation'] = 'true'
                        else:
                          fragment['SecretString'] = {}
                          fragment['SecretString']['Ref'] = "paramPassword"
                        print(fragment)
                        response["fragment"] = fragment
                        print(response)
                    except Exception:
                        traceback.print_exc()
                        response["status"] = "failure"
                        macro_response["errorMessage"] = str(e)
                    return response
          Handler: index.handler
          Runtime: python3.6
          Role: !GetAtt TransformExecutionRole.Arn
      TransformFunctionPermissions:
        Type: AWS::Lambda::Permission
        Properties:
          Action: 'lambda:InvokeFunction'
          FunctionName: !GetAtt TransformFunction.Arn
          Principal: 'cloudformation.amazonaws.com'
      Transform:
        Type: AWS::CloudFormation::Macro
        Properties:
          Name: 'SecretManager'
          Description: To check for secret's default value and conditionally create secret
          FunctionName: !GetAtt TransformFunction.Arn
    

    第二步:template.yml

    Parameters:
      paramPassword:
        Type: String
        Default: test
        Description: Enter the default value of SecretString
    Resources:
      LambdaIAMRole:
        Type: 'AWS::IAM::Role'
        Properties:
          AssumeRolePolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Principal:
                  Service:
                    - lambda.amazonaws.com
                Action:
                  - 'sts:AssumeRole'
          Path: /
          Policies:
            - PolicyName: root
              PolicyDocument:
                Version: 2012-10-17
                Statement:
                  - Effect: Allow
                    Action:
                      - 's3:*'
                    Resource: '*'
                  - Effect: Allow
                    Action:
                      - 'logs:CreateLogGroup'
                      - 'logs:CreateLogStream'
                      - 'logs:PutLogEvents'
                    Resource: 'arn:aws:logs:*:*:*'
    Resources:
      TestSecrets:
        Type: AWS::SecretsManager::Secret
        Properties:    
          'Fn::Transform':
                - Name: SecretManager
                  Parameters:
                    paramPassword: !Ref paramPassword
          Description: 'This is my password'
          Name: 'my-secret-password2'
    

    【讨论】:

      猜你喜欢
      • 2019-12-02
      • 1970-01-01
      • 1970-01-01
      • 2018-11-17
      • 1970-01-01
      • 1970-01-01
      • 2017-12-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多