Use AWS Secret Manager to handle credentials


AWS Secret Manager is a great solution for secret management. It is similar to HarshiCorp Vault, but with better integrations with other AWS services, e.g. IAM, RDS, Redshift, DocumentDB.

As illustrated above, I created a database in RDS and a credential in Secret Manager, then attached the credential to the database for dynamic reference. The benefit of doing that is I don’t need to provide the hardcoded password when creating the database, it is generated by secret manager and associate with the database. So whenever the credential in secret manager is changed (e.g password rotation), the database will change the password in RDS accordingly. To access the database, the EC2 instance or any services that can assume roles retrieve the password from the secret manager.

Here is the CloudFormation sample:

Resources:

  DbMasterUserPassword:
    Type: 'AWS::SecretsManager::Secret'
    Properties:
      Name: !Sub /${AppName}/database/master 
      Description: !Sub DB master user (postgres) password for ${AppName}-${Env}
      GenerateSecretString:
        SecretStringTemplate: '{"username": "postgres"}'
        GenerateStringKey: "password"
        PasswordLength: 16
        ExcludeCharacters: '"@/\'
      KmsKeyId: !ImportValue EnvKmsKeyId  
      Tags:
        - Key: Name
          Value: !Join [ "-", [ !Ref AppName, !Ref Env, db ] ] 

  Db:
    Type: "AWS::RDS::DBInstance"
    Properties:
      AllocatedStorage: !Ref DbAllocatedStorage
      AutoMinorVersionUpgrade: !Ref DbAutoMinorVersionUpgrade
      BackupRetentionPeriod: !Ref DbBackupRetentionPeriod
      DBName: !If [RestoreFromSnapshot, !Ref 'AWS::NoValue', !Ref DbName]
      DBInstanceClass: !Ref DbInstanceClass
      DBInstanceIdentifier: !Join [ "-", [ !Ref AppName, !Ref Env, db ] ] 
      DBSubnetGroupName: !Ref DbSubnetGroup
      DBSnapshotIdentifier: !If [RestoreFromSnapshot, !Ref RestoreSnapshot, !Ref 'AWS::NoValue'] 
      Engine: postgres
      EngineVersion: !Ref DbEngineVersion
      Iops: !If [DbProvisionedIops, !Ref DbIops, !Ref 'AWS::NoValue']
      KmsKeyId: !If [UseDatabaseEncryption, !ImportValue EnvKmsKeyId, !Ref 'AWS::NoValue']
      MasterUsername: postgres
      MasterUserPassword: !Join ['', ['{{resolve:secretsmanager:', !Ref DbMasterUserPassword, ':SecretString:password}}' ]]
      MultiAZ: !Ref DbMultiAZ
      StorageEncrypted: !If [UseDatabaseEncryption, !Ref DbAllocatedStorageEncrypted, !Ref 'AWS::NoValue']
      StorageType: !If [DbProvisionedIops, io1, gp2]
      Tags:
        - Key: Name
          Value: !Join [ "-", [ !Ref AppName, !Ref Env, db ] ]

        
  SecretRDSInstanceAttachment:
    Type: "AWS::SecretsManager::SecretTargetAttachment"
    Properties:
      SecretId: !Ref DbMasterUserPassword
      TargetId: !Ref Db
      TargetType: AWS::RDS::DBInstance 

To retrieve the password from the AWS secret manager, you can use AWS CLI or SDK. Also Ansible lookup plugin supports it too. Here is a example to retrieve above password.

"{{ (lookup('aws_secret', '/AppName/database/master', region='ap-southeast-2')| from_json).get('password') }}"

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s