SecretsManager CloudFormation으로 작성해보기!

2022.05.13

소개

안녕하세요! 클래스메소드 금상원 입니다. 이번 블로그에서는 SecretsManager를 CloudFormation을 이용하여 작성하는 방법에 대해 해보도록 하겠습니다.

콘솔 화면에서 SecretsManager작성 하는 방법에 대하여

SecretsManager작성하기

SecretString JSON 에 램덤으로 생산되는 패스워드를 가진 새로운 SecretsManager를 작성합니다.

이 정보에는 암호, 사용자 이름 및 암호와 같은 자격 증명 집합, OAuth 토큰 또는 Secrets Manager에서 암호화된 형식으로 정보를 저장 합니다.

RDSRotationSecret: 
    Type: AWS::SecretsManager::Secret
    Properties:
      Description: "test rds instance secret"
      Name: "/aws/rds/cluster/test/keum"
      GenerateSecretString:
        SecretStringTemplate: '{"username": "admin"}'
        GenerateStringKey: "password"
        PasswordLength: 32
        ExcludeCharacters: '"@/\'

GenerateSecretString : 비밀번호를 생성하고 SecretsManager에 저장하는 방법을 지정하는 구조입니다.

SecretStringTemplate : 생성된 문자열이 RDS와 일치되여야하는 부분이기때문에 RDS의 MasterUsername을 입력해줍니다.

GenerateStringKey : 키/값 쌍의 JSON 키 이름입니다. 여기서 값은 생성된 RDS의 비밀번호입니다.

PasswordLength : 비밀번호의 길이를 지정합니다.

ExcludeCharacters : 암호에 포함하지 않는 문자를 지정합니다. (문제가 되는 문자들을 주로 기입합니다.)

RDS와 연결하기

RDS와 생성한 SecretsManager를 연결하여 해당 RDS의 비밀번호를 관리 합니다.

SecretRDSInstanceAttachment:
    Type: AWS::SecretsManager::SecretTargetAttachment
    Properties:
      SecretId: !Ref RDSRotationSecret
      TargetId: !Ref RDSCluster
      TargetType: AWS::RDS::DBCluster

SecretId : 생성된 SecretsManager의 ID를 입력합니다. (!Ref를 사용하는 것을 추천합니다.)

TargetId : 연결할 서비스의 ID를 입력합니다. (!Ref나 !ImportValue를 사용하는 것을 추천합니다.)

TargetType : 연결할 서비스의 유형을 링크 에서 확인 후 입력합니다.

로테이션 작성하기

보안 암호에 대한 교체 일정 및 Lambda 교체 함수를 설정합니다.

MySecretRotationSchedule:
    Type: AWS::SecretsManager::RotationSchedule
    DependsOn: SecretRDSInstanceAttachment
    Properties:
      SecretId: !Ref RDSRotationSecret
      HostedRotationLambda:
        RotationType: "PostgreSQLSingleUser"
        RotationLambdaName: "SecretsManagerRotation"
        VpcSecurityGroupIds: !ImportValue SMSG
        VpcSubnetIds: "subnet-bb293dd2" 
      RotationRules:
        Duration: "1h"
        ScheduleExpression: "cron(0 17 ? * FRI *)"

SecretId : 로테이션을 연결할 SecretsManager의 ID를 입력합니다. (!Ref를 사용하는 것을 추천합니다.)

HostedRotationLambda : Secrets Manager 교체 함수 템플릿 중 하나를 기반으로 새 Lambda 교체 함수를 생성합니다 .

RotationType : 회전 기능의 기반이 되는 회전 템플릿을 링크에서 확인한 후 입력 해주세요.

VpcSecurityGroupIds : 람다에 사용할 보안그룹ID을 입력합니다.

VpcSubnetIds : 람다를 배치할 Subnet의 ID를 입력합니다.

RotationRules : Secrets Manager에 대한 Rotation 구성을 정의하는 구조입니다.

Duration : Rotation을 실행하는 시간의 길이입니다. Secrets Manager는 이 기간 동안 언제든지 암호를 교체합니다.

ScheduleExpression : 암호 교체 일정을 정의합니다. 표현식은 cron()또는 rate() 사용합니다.

전체 코드

AWSTemplateFormatVersion: "2010-09-09"
Description: "Secrets Manager with automatic rotation"
Transform: "AWS::SecretsManager-2020-07-23"
Parameters:
  USERNAME:
    Type: String
    Default: "keum"
    Description: user name
  DBNAME:
    Type: String
    Default: "test"
    Description: DB Name
  DBTYPE:
    Type: String
    Default: "db.r6g.large"
    Description: DB Instance Type
  ENGINE:
    Type: String
    Default: "aurora-postgresql"
    Description: DB Engine
  ENGINEVERSION:
    Type: String
    Default: "13.5"
    Description: DB Engine Version

Resources:

  RDSRotationSecret: 
    Type: AWS::SecretsManager::Secret
    Properties:
      Description: "test rds instance secret"
      Name: "/aws/rds/cluster/test-prd-db-rds-aurora-postgresql/okina"
      GenerateSecretString:
        SecretStringTemplate: '{"username": "keum"}'
        GenerateStringKey: "password"
        PasswordLength: 32
        ExcludeCharacters: '"@/\'

  RDSClusterParameterGroup:
    Type: "AWS::RDS::DBClusterParameterGroup"
    Properties:
      Description: "DB Parameter Group for test-prd-db-rds-aurora-postgresql"
      Family: "aurora-postgresql13"
      Parameters:
        timezone : Asia/Tokyo
        client_encoding: UTF8
      Tags:
        - Key: Name
          Value: "test-prd-db-rds-aurora-postgresql-cluster-dbparametergroup"

  DBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties: 
      DBSubnetGroupDescription: "DB Subnet Group on Isolated Subnets"
      DBSubnetGroupName: "test-prd-rds-sbng"
      SubnetIds: 
        - !ImportValue PrivateSubnet-a #from subnet.yaml
        - !ImportValue PrivateSubnet-c #from subnet.yaml
      Tags: 
        - Key: Name
          Value: "test-prd-rds-sbng"

  RDSCluster: 
    Type: AWS::RDS::DBCluster
    Properties: 
      DatabaseName: !Ref DBNAME
      Engine: !Ref ENGINE
      EngineMode: "provisioned"
      EngineVersion: !Ref ENGINEVERSION
      DBClusterIdentifier: "test-prd-db-rds-aurora-postgresql"
      MasterUsername: !Join ['', ['{{resolve:secretsmanager:', !Ref RDSRotationSecret, ':SecretString:username}}' ]]
      MasterUserPassword: !Join ['', ['{{resolve:secretsmanager:', !Ref RDSRotationSecret, ':SecretString:password}}' ]]
      DBSubnetGroupName: !Ref DBSubnetGroup
      Port: 5432
      BackupRetentionPeriod: 7
      CopyTagsToSnapshot: true
      PreferredBackupWindow: "16:00-16:30"
      PreferredMaintenanceWindow: "Fri:18:00-Fri:18:30"
      DeletionProtection: true
      EnableCloudwatchLogsExports: 
        - postgresql
      VpcSecurityGroupIds:
        - !ImportValue RDSSG
      DBClusterParameterGroupName: !Ref RDSClusterParameterGroup
      StorageEncrypted: true

  RDSDBParameterGroup: 
    Type: "AWS::RDS::DBParameterGroup"
    Properties: 
      Description: "DB Parameter Group for test-prd-db-rds-aurora-postgresql"
      Family: "aurora-postgresql13"
      Tags:
        - Key: Name
          Value: "test-prd-db-rds-aurora-postgresql-dbparametergroup"

  RDSDBInstance: 
    Type: "AWS::RDS::DBInstance"
    Properties: 
      DBClusterIdentifier: !Ref RDSCluster
      DBInstanceClass: !Ref DBTYPE  
      MultiAZ: false
      AvailabilityZone: "ap-northeast-1a"
      PubliclyAccessible: false
      EnablePerformanceInsights: true
      PerformanceInsightsRetentionPeriod: 7
      AutoMinorVersionUpgrade: true
      PreferredMaintenanceWindow: "Fri:18:30-Fri:19:00"
      DBSubnetGroupName: !Ref DBSubnetGroup
      DBParameterGroupName: !Ref RDSDBParameterGroup
      Engine: !Ref ENGINE
      EngineVersion: !Ref ENGINEVERSION

  SecretRDSInstanceAttachment:
    Type: AWS::SecretsManager::SecretTargetAttachment
    Properties:
      SecretId: !Ref RDSRotationSecret
      TargetId: !Ref RDSCluster
      TargetType: AWS::RDS::DBCluster

  MySecretRotationSchedule:
    Type: AWS::SecretsManager::RotationSchedule
    DependsOn: SecretRDSInstanceAttachment
    Properties:
      SecretId: !Ref RDSRotationSecret
      HostedRotationLambda:
        RotationType: "PostgreSQLSingleUser"
        RotationLambdaName: "SecretsManagerRotation"
        VpcSecurityGroupIds: !ImportValue SMSG # from sg.yaml
        VpcSubnetIds: "subnet-bb293dd2" # ec2 subnet
      RotationRules:
        Duration: "1h"
        ScheduleExpression: "cron(0 17 ? * FRI *)"

마무리

이번 블로그에서는 CloudFormation을 이용하여 AWS ScretsManager를 작성하는 방법에 대해 알아보았습니다.

ScretsManager를 CloudFormation으로 작성 하실려는 분들에게 조금이나마 도움이 되었으면 좋겠습니다.

참고자료

본 블로그 게시글을 보시고 문의 사항이 있으신 분들은 클래스메소드코리아 (info@classmethod.kr)로 연락 주시면 빠른 시일 내 담당자가 회신 드릴 수 있도록 하겠습니다 !