Tại Sao Nên Dùng Secrets Manager Thay Vì Hardcode Mật Khẩu?
Repo private không có nghĩa là an toàn — đây là sai lầm mà nhiều team backend mắc phải khi hardcode DB password vào source code. Một lần merge nhầm branch, một lần export log, hay một nhân viên rời công ty là đủ để credential bị lộ. Bài này giải thích tại sao AWS Secrets Manager là lựa chọn đúng đắn và cách nó xử lý automatic password rotation mà không cần downtime.
TL;DR — Secrets Manager vs. Hardcoding
| Tiêu chí | Hardcode trong code | AWS Secrets Manager |
|---|---|---|
| Lộ credential khi repo bị clone | ✗ Có nguy cơ cao | ✓ Không lưu trong code |
| Rotation tự động | ✗ Thủ công, dễ bỏ sót | ✓ Tích hợp sẵn với RDS |
| Audit trail | ✗ Không có | ✓ CloudTrail log mọi lần truy cập |
| Kiểm soát truy cập theo IAM | ✗ Không thể | ✓ Resource-based policy + IAM |
| Tập trung quản lý nhiều môi trường | ✗ Rải rác trong config file | ✓ Một nơi, phân quyền rõ ràng |
Tại Sao Repo Private Vẫn Không Đủ An Toàn?
Câu hỏi kinh điển: 'Repo của tôi private, chỉ team nội bộ truy cập được, vậy hardcode password có sao không?' Câu trả lời ngắn: có sao, và sao theo nhiều hướng khác nhau.
Thứ nhất, git history không bao giờ quên. Dù bạn xóa credential ở commit sau, nó vẫn tồn tại trong lịch sử commit và có thể được khôi phục bằng git log hoặc git show. Thứ hai, CI/CD pipeline thường export environment variable vào build log — và build log đôi khi được lưu ở nơi có quyền truy cập rộng hơn repo. Thứ ba, developer clone repo về máy local, máy đó có thể bị mất, bị hack, hoặc được dùng chung.
Quan trọng hơn: credential tĩnh không có expiry. Nếu bị lộ hôm nay, nó vẫn hợp lệ cho đến khi ai đó nhớ ra và đổi thủ công. Với Secrets Manager, rotation có thể được lập lịch tự động — credential bị lộ sẽ hết hạn theo chu kỳ đã định.
Secrets Manager Hoạt Động Như Thế Nào?
Secrets Manager lưu trữ secret dưới dạng key-value JSON, mã hóa bằng AWS KMS. Ứng dụng không đọc password từ config file mà gọi API GetSecretValue tại runtime. Với RDS, Secrets Manager còn tích hợp sẵn rotation Lambda — khi rotation được kích hoạt, Lambda sẽ tạo password mới trên RDS, cập nhật secret, và ứng dụng tự động lấy giá trị mới trong lần gọi API tiếp theo.
- Ứng dụng gọi GetSecretValue tại runtime thay vì đọc từ biến môi trường tĩnh.
- Secrets Manager trả về JSON chứa username và password hiện tại, đã được mã hóa bằng KMS.
- Khi rotation được kích hoạt (theo lịch hoặc thủ công), Lambda rotation function tạo password mới trên RDS instance.
- Secret được cập nhật với giá trị mới — ứng dụng lấy giá trị mới trong lần gọi tiếp theo mà không cần restart.
Hướng Dẫn Thiết Lập Secrets Manager Cho RDS — Từng Bước
Phần này đi qua toàn bộ flow: tạo secret đúng loại, cấp quyền IAM cho ứng dụng, đọc secret trong code, và bật rotation.
Bước 1: Tạo Secret Qua AWS Console (Bắt Buộc Với RDS)
Với RDS, bạn phải tạo secret qua AWS Management Console và chọn đúng loại 'Credentials for RDS database'. Đây là bước quan trọng — chỉ khi tạo đúng loại này, Secrets Manager mới liên kết secret với RDS instance và rotation Lambda mới hoạt động được. Nếu tạo secret dạng 'Other type of secret' bằng CLI rồi cố bật rotation cho RDS, bước đó sẽ thất bại.
Các bước thực hiện trên Console:
- Vào AWS Management Console → Secrets Manager → Store a new secret.
- Chọn loại secret: 'Credentials for Amazon RDS database'.
- Nhập username và password của DB user.
- Chọn RDS instance mà secret này sẽ quản lý.
- Đặt tên secret, ví dụ:
prod/myapp/db. - Ở bước 'Configure rotation', có thể bỏ qua lúc này — sẽ bật ở Bước 4.
- Hoàn tất wizard và lưu secret.
Sau khi tạo xong, lấy Secret ARN từ Console để dùng ở các bước tiếp theo.
Bước 2: Cấp Quyền IAM Cho Ứng Dụng
Ứng dụng cần quyền secretsmanager:GetSecretValue để đọc secret. Nếu secret được mã hóa bằng customer-managed KMS key (không phải AWS managed key mặc định), cần thêm quyền kms:Decrypt trên key đó. Áp dụng least privilege — chỉ cấp quyền trên đúng secret ARN, không dùng Resource: '*'.
🔽 Xem IAM Policy mẫu
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowGetSecret",
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/myapp/db-*"
}
]
}
Lưu ý: ARN của secret trong Secrets Manager có suffix ngẫu nhiên 6 ký tự (ví dụ prod/myapp/db-AbCdEf). Dùng wildcard -* ở cuối để policy vẫn hoạt động sau khi secret bị xóa và tạo lại với suffix mới.
Gán policy này vào IAM role của EC2 instance, ECS task, hoặc Lambda function — không gán vào IAM user và không nhúng access key vào code.
Bước 3: Đọc Secret Trong Ứng Dụng
Thay vì đọc password từ biến môi trường tĩnh, ứng dụng gọi GetSecretValue tại thời điểm khởi tạo connection pool. Ví dụ Python:
🔽 Xem code Python mẫu
import boto3
import json
def get_db_credentials(secret_name: str, region_name: str) -> dict:
client = boto3.client('secretsmanager', region_name=region_name)
response = client.get_secret_value(SecretId=secret_name)
secret = json.loads(response['SecretString'])
return secret # {'username': '...', 'password': '...'}
creds = get_db_credentials('prod/myapp/db', 'us-east-1')
conn = connect_to_db(
host=creds['host'],
user=creds['username'],
password=creds['password'],
database=creds['dbname']
)
Một điểm thực tế: gọi GetSecretValue mỗi lần tạo connection sẽ tạo ra latency và cost không cần thiết. Trong production, cache giá trị secret trong memory với TTL ngắn hơn rotation interval. AWS SDK cũng cung cấp Secrets Manager caching client cho Java và Python để xử lý việc này.
Bước 4: Bật Automatic Rotation
Vì secret đã được tạo đúng loại 'Credentials for RDS database' ở Bước 1, rotation Lambda đã được Secrets Manager chuẩn bị sẵn. Bây giờ có thể bật rotation bằng CLI:
aws secretsmanager rotate-secret \
--secret-id prod/myapp/db \
--rotation-rules AutomaticallyAfterDays=30 \
--region us-east-1
Lệnh này kích hoạt rotation ngay lập tức và sau đó lặp lại mỗi 30 ngày. Kiểm tra trạng thái rotation:
aws secretsmanager describe-secret \
--secret-id prod/myapp/db \
--query '{RotationEnabled: RotationEnabled, LastRotatedDate: LastRotatedDate, NextRotationDate: NextRotationDate}' \
--region us-east-1
Nếu RotationEnabled trả về false sau khi chạy lệnh trên, kiểm tra lại xem secret có được tạo đúng loại 'Credentials for RDS database' trên Console chưa — đây là nguyên nhân phổ biến nhất khiến rotation không được bật.
Một Lỗi Hay Gặp Trong Thực Tế — Và Tại Sao Khó Debug
Symptom: Sau khi bật rotation, ứng dụng bắt đầu báo lỗi authentication failed vào đúng ngày rotation chạy, dù code không thay đổi gì.
Misdiagnosis đầu tiên: team nghĩ rotation Lambda bị lỗi và tắt rotation đi. Nhưng CloudWatch log của Lambda cho thấy rotation thành công — password trên RDS đã được đổi, secret đã được cập nhật.
Actual cause: ứng dụng đang cache connection pool với password cũ. Connection pool không bao giờ bị invalidate, nên nó tiếp tục dùng credential cũ cho đến khi connection bị đóng tự nhiên. Với connection pool có idle timeout dài, điều này có thể kéo dài hàng giờ sau rotation.
Fix: implement logic reconnect khi gặp authentication error — catch exception, gọi lại GetSecretValue để lấy credential mới, và tạo lại connection. Secrets Manager caching client xử lý pattern này tự động bằng cách refresh cache khi phát hiện secret version mới.
Rotation không phải là magic wand — nó chỉ đổi password trên RDS và cập nhật secret. Ứng dụng vẫn phải tự biết khi nào cần lấy lại credential mới.
Xác Minh Quyền Truy Cập Và Audit
Mọi lần gọi GetSecretValue đều được ghi vào CloudTrail. Để xem ai đã truy cập secret trong 24 giờ qua:
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=GetSecretValue \
--start-time $(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%SZ) \
--region us-east-1
Đây là thứ hardcoding không bao giờ cho bạn — visibility đầy đủ về ai truy cập credential, từ IP nào, lúc mấy giờ.
Rotation trigger"] --> Lambda["Rotation Lambda Function"] Lambda --> RDS["RDS Instance
Đổi password"] Lambda --> SM["Secrets Manager
Cập nhật secret version"] SM --> App["Ứng dụng
GetSecretValue"] App --> Cache["SDK Cache
TTL refresh"] SM --> CT["CloudTrail
Audit log"] style Scheduler fill:#f0a500,color:#000 style Lambda fill:#ff9900,color:#000 style RDS fill:#3f8624,color:#fff style SM fill:#1a73e8,color:#fff style CT fill:#6c757d,color:#fff
- Rotation Lambda được Secrets Manager gọi theo lịch đã định.
- Lambda tạo password mới trực tiếp trên RDS instance.
- Lambda cập nhật secret với giá trị mới trong Secrets Manager.
- Ứng dụng lấy credential mới trong lần gọi
GetSecretValuetiếp theo — hoặc ngay lập tức nếu dùng caching client với refresh logic. - CloudTrail ghi lại toàn bộ sự kiện rotation và truy cập.
Tại Sao Secrets Manager Là Lựa Chọn Đúng Đắn Cho Production
Hardcoding credential vào code — dù repo private — là kỹ thuật nợ bảo mật tích lũy theo thời gian. Secrets Manager giải quyết ba vấn đề cùng lúc: lưu trữ an toàn với mã hóa KMS, kiểm soát truy cập theo IAM với audit trail đầy đủ, và rotation tự động để giới hạn thời gian tồn tại của credential bị lộ.
Điểm mấu chốt khi thiết lập: luôn tạo secret cho RDS qua AWS Console với loại 'Credentials for RDS database'. Đây không phải tùy chọn — đây là điều kiện để rotation hoạt động đúng. Sau đó, đảm bảo ứng dụng có logic xử lý credential refresh khi rotation xảy ra, không chỉ đọc secret một lần lúc khởi động.
Tài Nguyên Tiếp Theo
- AWS Secrets Manager — Rotating secrets
- AWS Secrets Manager caching client
- Authentication and access control for Secrets Manager
Glossary — Thuật Ngữ Chính
| Thuật ngữ | Giải thích |
|---|---|
| Secret rotation | Quá trình tự động thay thế credential cũ bằng credential mới theo lịch định kỳ, không cần downtime. |
| GetSecretValue | API call của Secrets Manager để ứng dụng lấy giá trị secret tại runtime. |
| KMS | AWS Key Management Service — dịch vụ quản lý khóa mã hóa, dùng để mã hóa secret trong Secrets Manager. |
| Rotation Lambda | Lambda function do Secrets Manager quản lý, thực hiện các bước đổi password trên RDS và cập nhật secret. |
| Resource-based policy | Policy gắn trực tiếp vào secret, kiểm soát principal nào được phép truy cập — bổ sung cho IAM identity policy. |
Nhận xét
Đăng nhận xét