AWS Secrets Manager Integration with Amazon RDS

AWS Secrets Manager Integration with Amazon RDS

avatar

Phong Nguyen

2025.01.16

Bài blog này sẽ thực hiện hoá kiến trúc Lambda lấy thông tin DB Credential từ AWS Secret Manager để access đến RDS. Kiến trúc này cũng sẽ tuân theo best practice.

Giới thiệu

Khi các bạn học SAA hay DVA, sẽ có những câu hỏi kiểu như

  • Cấu hình thế nào để Lambda có thể kết nối đến RDS? => đặt Lambda trong cùng subnet với RDS (Tuân theo best practice thì RDS cũng như Lambda phải nằm trong private subnet)
  • Setting DB credential thế nào để có thể rotation? => Sử dụng AWS Secret Manager - nơi lưu trữ các thông tin secret như: DB credential, API key, ssh key, Access token...
  • Lambda trong private subnet sẽ lấy thông tin secret ở AWS Secret Manager như thế nào?
    • Cách 1 là đi qua NAT gateway ở public subnet -> internet -> AWS Secret Manager
    • Cách 2 là dùng VPC Interface Endpoint cho AWS Secret Manager (bài blog này áp dụng cách 2)

Bài blog này sẽ đi thực hiện những lý thuyết phía trên nhằm giúp các bạn hiểu rõ, làm xong bài này thì đi gặp mấy câu tương tự thì rất khó để mà sai.

Lab Introduction

  • AWS experience: Intermediate
  • Time to complete: 60 minutes
  • AWS Region: US East (N. Virginia) us-east-1
  • Cost to complete: 1$ hoặc rất ít nếu làm xong bạn xoá ngay
  • Services used: EC2, RDS, VPC, AWS Secret Manager, Lambda, VPC Endpoint

Giới thiệu AWS Secret Manager

AWS Secrets Manager là dịch vụ quản lý thông tin nhạy cảm của AWS, giúp bạn:

  1. Lưu trữ an toàn các thông tin bảo mật như:
  • Mật khẩu
  • API keys
  • Thông tin xác thực database
  • Các credentials khác
  1. Các tính năng chính:
  • Mã hóa dữ liệu khi lưu trữ và truyền tải
  • Tự động rotate (thay đổi) credentials theo lịch định kỳ
  • Kiểm soát truy cập chi tiết thông qua IAM
  • Tích hợp sẵn với nhiều dịch vụ AWS khác
  1. Cách sử dụng:
  • Thông qua AWS Console
  • AWS CLI
  • AWS SDK
  • API calls
  1. Lợi ích:
  • Tăng tính bảo mật khi không phải hardcode credentials
  • Quản lý tập trung các thông tin nhạy cảm
  • Giảm rủi ro rò rỉ thông tin
  • Đáp ứng các yêu cầu compliance

Architecture Diagram

Về sơ đồ kiến trúc:

  • Chúng ta có VPC với 4 subnet: 2 public, 2 private
  • EC2 nằm ở public đóng vai trò như bastion host, để EC2 kết nối đến DB thì chúng ta sẽ gắn Role và setting security group phù hợp. EC2 trong bài này với mục đích kết nối đến DB, cho phép chúng ta thao tác với DB thông qua command line.
  • Secret Manager lưu trữ thông tin username, password của DB, chúng ta có thể manual rotate hoặc setting sau bao lâu để rotate một lần. Thông tin ở Secret Manager được mã hoá sử dụng KMS
  • RDS đặt trong private subnet, setting Security group phù hợp
  • Lambda setting nằm trong VPC trên 2 private subnet (đảm bảo HA), setting Security Group phù hợp, đồng thời để cho phép Lambda kết nối đến Secret Manager, chúng ta sẽ gắn Role tương ứng
  • Lambda tỏng VPC kết nối ra Secret Manager ngoài VPC thông tin Interface Endpoint. Endpoint này cũng phải setting Security group phù hợp

Let’s get started!

Task Details

  1. Xây dựng VPC
  2. Setting Security Group
  3. Create Role
  4. Create RDS
  5. Create EC2
  6. Create VPC Endpoint
  7. Create Lambda
  8. Test lambda
  9. Test Rotation Credential

Prerequisites

Before starting this tutorial, you will need:

  • An AWS account
  • Lambda knowledge
  • RDS knowledge

1. Xây dựng VPC

VPC settings

  • Resources to create: VPC and more
  • Auto-generate: check
  • Name: d-vpc-cmp-001-vpc
  • IPv4 CIDR block: 10.3.0.0/16
  • Number of Availability Zones (AZs) : 2
  • Number of public subnets: 2
  • Number of private subnets: 2
  • NAT gateways ($): None
  • VPC endpoints: S3 Gateway
  • Còn lại để default

2. Setting Security Group

2.1. Security Group for EC2

  • Name: d-sg-cmp-bastion-host

  • Description: SG for EC2

  • VPC: d-vpc-cmp-001-vpc

  • Inbound rules

    TypeProtocolPort rangeSource
    SSHTCP220.0.0.0/0
  • Outbound rules

    TypeProtocolPort rangeSource
    All trafficAllAll0.0.0.0/0
  • Tags

    Keyvalue
    Named-sg-cmp-bastion-host

2.1. Security Group for Endpoint

  • Name: d-sg-cmp-secret-manager-enpoint

  • Description: SG for Endpoint

  • VPC: d-vpc-cmp-001-vpc

  • Inbound rules

    TypeProtocolPort rangeSource
    HTTPSTCP44310.3.0.0/16
  • Outbound rules

    TypeProtocolPort rangeSource
    All trafficAllAll0.0.0.0/0
  • Tags

    Keyvalue
    Named-sg-cmp-secret-manager-enpoint

2.1. Security Group for Lambda

  • Name: d-sg-cmp-lambda

  • Description: SG for Lmabda

  • VPC: d-vpc-cmp-001-vpc

  • Inbound rules

    TypeProtocolPort rangeSource
    HTTPSTCP443d-sg-cmp-secret-manager-enpoint
  • Outbound rules

    TypeProtocolPort rangeSource
    All trafficAllAll0.0.0.0/0
  • Tags

    Keyvalue
    Named-sg-cmp-lambda

2.1. Security Group for RDS

  • Name: d-sg-cmp-mysql-db

  • Description: SG for DB

  • VPC: d-vpc-cmp-001-vpc

  • Inbound rules

    TypeProtocolPort rangeSource
    MYSQL/AuroraTCP3306d-sg-cmp-lambda
    MYSQL/AuroraTCP3306d-sg-cmp-bastion-host
  • Outbound rules

    TypeProtocolPort rangeSource
    All trafficAllAll0.0.0.0/0
  • Tags

    Keyvalue
    Named-sg-cmp-mysql-db

3. Create Role

3.1. Create Role for EC2

  • Use case: EC2
  • Permissions policies:
    •  CloudWatchFullAccess
    • SecretsManagerReadWrite
  • Role name: CMPEC2AccessSecretManagerRole

3.2. Create Role for lambda

  • Use case: Lambda
  • Permissions policies:
    •  AWSLambdaVPCAccessExecutionRole
    • SecretsManagerReadWrite
  • Role name: CMPLambdaAccessRDSRole

4. Create RDS

Dưới đây là các parameter setting: Các setting mình không đề cập thì để Default

  • Choose a database creation method: Standard create
  • Engine options:
    • Engine type: MySQL
    • Engine version: MySQL 8.*
  • Templates: Free tier
  • Availibility & durablity: Default (Single DB Instance)
  • Settings
    • DB Instance identifier (DB Name): d-rds-cmp-mysql
  • Credentials Settings
    • Credentials management: Managed in AWS Secrets Manager - most secure
    • Select the encryption key: aws/secretmanager (default)
  • Instance configuration
    • Burstable classes (includes t classes): db.t3.micro
  • Connectivity:
    • Compute resource: Don’t connect to an EC2 compute resource
    • VPC: d-vpc-cmp-001-vpc
    • DB subnet group: default-vpc-***
    • Public access: No 
    • Security Group: d-sg-cmp-mysql-db
    • Availability Zone: us-east-1a
    • RDS Proxy: Uncheck
  • Database authentication
    • Database authentication options: Password authentication
  • Additional configuration:
    • Initial database name: mydb

Sau khi chúng ta tạo được RDS thì đồng thời Secret cũng được tạo trong Secret Manager.

Có thể kiểm tra tại đây

Đây chính là thông tin username và password dùng để truy cập DB. Tuy nhiên chúng ta không nhất định phải biết thông tin này, vì mỗi lần truy cập thì EC2/lambda sẽ gọi đến lấy thông tin và truy cập DB. Dù cho thông tin này có rotate (thay đổi) đi chăng nữa thì vẫn EC2/lambda vẫn kết nối dc DB. Đấy chính là điểm bảo mật.

5. Create EC2

  • Name: d-ec2-cmp-bastion-host
  • OS: Default (Amazon Linux 2023)
  • Instance type: Default (t2.micro)
  • Key pair: d-key-SAA-common (Nếu chưa có thì tạo mới)
  • Network Setting:
    • VPC: d-vpc-cmp-001-vpc
    • Subnet: d-vpc-cmp-001-subnet-public1-us-east-1a
    • Auto-assign public IP: Enable
    • Select existing security group: d-sg-cmp-bastion-host
  • Advanced details:
    • IAM instance profile: CMPEC2AccessSecretManagerRole
    • User data:
#!/bin/bash
rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2023
dnf -y localinstall  https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm
dnf -y install mysql mysql-community-client

Sau khi EC2 running, chúng ta access đến EC2 thông qua Console.

Đầu tiên là lấy thông tin access DB:

aws secretsmanager get-secret-value --secret-id '<Secret ID>' | jq .SecretString | jq fromjson

Tiếp theo chúng ta sẽ kêts nối đến DB:

mysql -h <endpoint_rds> -P 3306 -u <username> -p

DB endpoint lấy ở RDS

Nhập thông tin password

Kết quả kết nối thành công

6. Create VPC Endpoint

  • Endpoint settings
    • Name tag: d-endpoint-cmp-secret-namager
    • Type: AWS services
  • Services
    • com.amazonaws.us-east-1.secretsmanager
  • Network settings
    • VPC: d-vpc-cmp-001-vpc
    • DNS name: Enable DNS name (Checked)
  • Subnets
    • us-east-1a -> d-vpc-cmp-001-subnet-private1-us-east-1a
    • us-east-1b -> d-vpc-cmp-001-subnet-private2-us-east-1b
  • Security groups: d-sg-cmp-secret-manager-enpoint
  • Policy: Full access
  • Tags: Key: Name, Value: d-endpoint-cmp-secret-namager

7. Create Lambda

  • Author from scratch
  • Basic information
    • Function name: d-lam-vpc-cmp-app-connect-to-rds
    • Runtime: Python 3.12
    • Architecture: arm64
    • Execution role: Use an existing role -> CMPLambdaAccessRDSRole
  • Additional Configurations
    • Enable VPC
    • VPC: d-vpc-cmp-001-vpc
    • Subnet: d-vpc-cmp-001-subnet-private1-us-east-1a, d-vpc-cmp-001-subnet-private2-us-east-1b
    • Scurity group: d-sg-cmp-lambda
  • Create function

Sau khi Tạo xong Function thì cấu hình Timeout tăng lên 10s

Thêm biến môi trường cho Lambda:

  • DB_NAME: mydb
  • RDS_HOST: Lấy endpoint từ RDS
  • SECRET_NAME: Lấy Secret name trong Secret Manager

Code:

Nội dung code sẽ là

  • Lấy thôn tin kết nối DB từ Secret Manager
  • Tạo kết nối đến DB
  • Tạo Table Customer (CustID, Name) nếu như Table chưa tồn tại
  • Inser record với Name = 'Cloud Mentor Pro' vào table
  • List tất cả các record trong table Customer
"""
Activity Name: d-lam-vpc-cmp-app-connect-to-rds
Description: Get credentials and access RDS
Created by: Cloud Mentor Pro
Created on: 2024/09/06
"""

# **************************************************
# ----- Import Library
# **************************************************
import json
import logging
import traceback
import os
import sys
import pymysql
import boto3
from botocore.exceptions import ClientError

# **************************************************
# ----- Set logger
# **************************************************
logger = logging.getLogger()
for h in logger.handlers:
  logger.removeHandler(h)

h = logging.StreamHandler(sys.stdout)
FORMAT = '%(levelname)s %(asctime)s [%(filename)s:%(funcName)s:%(lineno)d] %(message)s'
h.setFormatter(logging.Formatter(FORMAT))
logger.addHandler(h)
logger.setLevel(logging.INFO)

# **************************************************
# ----- Variables
# **************************************************
rds_host = os.environ['RDS_HOST']
db_name = os.environ['DB_NAME']

secret_name = os.environ['SECRET_NAME']
region_name = "us-east-1"

# Create a Secrets Manager client
session = boto3.session.Session()
client = session.client(
    service_name='secretsmanager',
    region_name=region_name
)
    
# **************************************************
# ----- get_secret
# **************************************************
def get_secret():

    try:
        get_secret_value_response = client.get_secret_value(
            SecretId=secret_name
        )
    except ClientError as e:
        # For a list of exceptions thrown, see
        # https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
        raise e

    secret = get_secret_value_response['SecretString']
    
    return json.loads(secret)

# **************************************************
# ----- lambda_handler
# **************************************************
def lambda_handler(event, context):
    """
    This function creates a new RDS database table and writes records to it
    """
    
    log_name = f"{os.path.splitext(os.path.basename(__file__))[0]}"
    logging.info(f"[{log_name}] process start.")
    
    status = ""
    try:
        logger.info('Get RDS credentials from Secrets Manager')
        secret_dict = get_secret()
        user_name = secret_dict['username']
        password = secret_dict['password']
        
        logger.info('Connect RDS')
        conn = pymysql.connect(host=rds_host, user=user_name, passwd=password, db=db_name, connect_timeout=5)
    except pymysql.MySQLError as e:
        logger.error("ERROR: Unexpected error: Could not connect to MySQL instance.")
        logger.error(e)
        sys.exit(1)
    
    logger.info("SUCCESS: Connection to RDS for MySQL instance succeeded")
    
    try:
        Name = 'Cloud Mentor Pro'
        
        item_count = 0
        sql_string = f"insert into Customer (Name) values(%s)"
    
        with conn.cursor() as cur:
            cur.execute("create table if not exists Customer ( CustID INT NOT NULL AUTO_INCREMENT, Name varchar(255) NOT NULL, PRIMARY KEY (CustID))")
            cur.execute(sql_string, (Name))
            conn.commit()
            cur.execute("select * from Customer")
            logger.info("The following items have been added to the database:")
            for row in cur:
                item_count += 1
                logger.info(row)
        conn.commit()
    
        logger.info("Added %d items to RDS for MySQL table" %(item_count))
        status = "SUCCESS"

    except Exception as err_info:
        status = "ERROR"
        logger.error(
            f"[{log_name}] failed...\n{err_info}\n{traceback.format_exc()}")
    finally:
        logger.info(f"[{log_name}] process end. [status:{status}]")
        return {"status": status}

8. Test Lambda

Run lambda cho được kêt quả như bên dưới

Chúng ta cũng có thể thao tác với DB ở EC2 thông qua command

9. Test Rotation Credential

Thực hiện Rotation thủ công:

Mất khoảng 30s-1phút thì giá trị password đã được thay đổi

Tiếp tục chạy lại Lambda để test thử

Lambdax vẫn chạy OK, nghĩa là giá trị password trong Secret có thay đổi thế nào đi nữa, nó cũng đã được liên kết với DB, Lambda thì sẽ luôn lấy thông tin kết nối từ Secret manager.

Cuối cùng thì chúng ta sẽ setting để cho giá trị password thay đổi theo định kỳ

Clean up resources

  • Delete DB: d-rds-cmp-mysql instance không giữ lại snapshot hay backup gì cả

  • Delete EC2: d-ec2-cmp-bastion-host
  • Delete Lambda: d-lam-vpc-cmp-app-connect-to-rds
  • Delete Role:
    • CMPLambdaAccessRDSRole
    • CMPEC2AccessSecretManagerRole
  • Delete Secret

  • Delete VPC Endpoint: d-endpoint-cmp-secret-namager
  • Các thành phần phái trên Delete xong thì cuối cùng sẽ delete được VPC: d-vpc-cmp-001-vpc

KẾT

Vậy là chúng ta vừa xong bài Lab với nhiều Service và config, bài Lab này thuộc Level SAA và DVA - không đến mức quá khó.

Việc hiểu được cơ chế và cách seting Security group hay Networking, cho phép bạn tự tin hơn cho những kiến trúc sau này.

Mục đích bài này đơn giản là hiểu được cơ chế liên kết giữa Secret Manager và RDS. Nếu gặp các câu hỏi như đầu bài trong kỳ thi chứng chỉ, hy vọng các bạn sẽ vượt qua nó một cách nhẹ nhàng.

Cảm ơn các bạn đã đọc bài và hẹn ở nhưng bài viết tiếp theo của Cloud Mentor Pro.