Sử Dụng Environment Variables Trong AWS Lambda: Cấu Hình và Mã Hóa Với KMS
Bạn vừa deploy Lambda function lên production và nhận ra endpoint database đang được hardcode thẳng trong source code — điều này không chỉ là security risk mà còn khiến mỗi lần đổi môi trường phải rebuild và redeploy toàn bộ function. Bài viết này hướng dẫn cách sử dụng Lambda Environment Variables để tách cấu hình khỏi code, và cách mã hóa chúng bằng AWS KMS để bảo vệ thông tin nhạy cảm như database endpoint, credentials, hay API keys.
TL;DR — Lambda Environment Variables và KMS Encryption
| Vấn đề | Giải pháp |
|---|---|
| Hardcode config trong code | Dùng Environment Variables, đọc qua os.environ |
| Giá trị nhạy cảm lộ trong console | Bật encryption at rest bằng KMS CMK |
| Cần decrypt trong runtime | Dùng KMS Decrypt API trong function code |
| Phân quyền truy cập KMS | Gắn kms:Decrypt vào execution role |
Cơ Chế Hoạt Động Của Lambda Environment Variables
Lambda lưu environment variables dưới dạng key-value pairs gắn với function configuration — không phải với deployment package. Khi function được invoke, Lambda runtime inject các biến này vào process environment trước khi handler code chạy. Điều này có nghĩa là code chỉ cần đọc os.environ['DB_ENDPOINT'] mà không cần biết giá trị được lưu ở đâu.
Về mặt lưu trữ, Lambda luôn mã hóa environment variables at rest bằng một AWS-managed key theo mặc định. Tuy nhiên, key mặc định này là shared key — tất cả Lambda functions trong cùng account và region dùng chung. Nếu bạn cần kiểm soát chặt hơn, ví dụ audit log riêng hoặc rotation policy riêng, bạn cần chỉ định một Customer Managed Key (CMK) từ AWS KMS.
Ngoài encryption at rest, Lambda còn hỗ trợ encryption in transit — giá trị được mã hóa bằng KMS trước khi gửi đến Lambda service. Với tùy chọn này, giá trị thực sự không bao giờ xuất hiện dưới dạng plaintext trong Lambda console, và bạn phải tự gọi KMS Decrypt API trong function code để lấy giá trị gốc.
Env Vars (encrypted at rest)"] --> Invoke["Lambda Invoke Request"] Invoke --> Runtime["Lambda Runtime
inject env vars vào process"] Runtime --> Handler["Handler Code
os.environ['DB_ENDPOINT']"] Handler -->|"nếu dùng encryption in transit"| KMS["KMS Decrypt API"] KMS -->|"plaintext value"| Handler ExecRole["Execution Role"] -->|"kms:Decrypt"| KMS
- Function Configuration: Environment variables được lưu cùng function config, mã hóa at rest bằng KMS.
- Invoke: Khi có invoke request, Lambda runtime nhận config và inject variables vào process environment.
- Handler Execution: Code đọc biến qua
os.environ— nếu dùng encryption in transit, phải gọi thêm KMS Decrypt. - KMS Decrypt (tùy chọn): Execution role cần có quyền
kms:Decrypttrên CMK tương ứng.
Thiết Lập Lambda Environment Variables Qua AWS CLI
Cách nhanh nhất để kiểm tra cơ chế này là dùng CLI. Ví dụ dưới đây tạo hoặc cập nhật environment variables cho một function đã tồn tại. Lưu ý rằng --environment sẽ ghi đè toàn bộ environment variables hiện tại — không phải merge. Nếu bạn chỉ muốn thêm một biến mà giữ nguyên các biến khác, hãy lấy giá trị hiện tại trước rồi merge lại.
# Cập nhật environment variables cho Lambda function
aws lambda update-function-configuration \
--function-name my-db-function \
--environment 'Variables={DB_ENDPOINT=mydb.cluster-abc123.us-east-1.rds.amazonaws.com,DB_PORT=5432,ENVIRONMENT=production}'
# Kiểm tra environment variables hiện tại của function
aws lambda get-function-configuration \
--function-name my-db-function \
--query 'Environment'
Trong Python runtime, đọc biến như sau:
import os
def handler(event, context):
db_endpoint = os.environ['DB_ENDPOINT']
db_port = os.environ.get('DB_PORT', '5432') # fallback nếu không có
# ... kết nối database
return {'statusCode': 200}
Mã Hóa Environment Variables Bằng KMS CMK
Đây là phần mà nhiều engineer bỏ qua vì nghĩ AWS-managed key là đủ. Thực tế, nếu bạn cần audit trail riêng cho từng function, hoặc cần revoke access cho một function cụ thể mà không ảnh hưởng function khác, bạn phải dùng CMK.
Bước 1: Tạo KMS CMK
aws kms create-key \
--description 'CMK for Lambda environment variables - my-db-function' \
--key-usage ENCRYPT_DECRYPT \
--query 'KeyMetadata.KeyId' \
--output text
# Tạo alias dễ nhớ cho key
aws kms create-alias \
--alias-name alias/lambda-my-db-function \
--target-key-id <KEY_ID_từ_bước_trên>
Bước 2: Gắn KMS Key Vào Lambda Function
aws lambda update-function-configuration \
--function-name my-db-function \
--kms-key-arn arn:aws:kms:us-east-1:123456789012:key/<KEY_ID> \
--environment 'Variables={DB_ENDPOINT=mydb.cluster-abc123.us-east-1.rds.amazonaws.com,DB_PORT=5432}'
Bước 3: Cấp Quyền Cho Execution Role
Execution role của Lambda cần quyền kms:Decrypt trên CMK. Nếu thiếu quyền này, function sẽ fail ngay khi khởi động với lỗi AccessDeniedException từ KMS — không phải lỗi trong handler code.
🔽 IAM Policy cho Execution Role (click để mở rộng)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowKMSDecryptForEnvVars",
"Effect": "Allow",
"Action": [
"kms:Decrypt"
],
"Resource": "arn:aws:kms:us-east-1:123456789012:key/<KEY_ID>"
}
]
}
# Gắn inline policy vào execution role
aws iam put-role-policy \
--role-name my-db-function-execution-role \
--policy-name KMSDecryptForEnvVars \
--policy-document file://kms-decrypt-policy.json
Encryption In Transit: Khi Nào Cần Và Cách Dùng
Encryption at rest bằng CMK bảo vệ giá trị khi lưu trữ, nhưng giá trị vẫn xuất hiện dưới dạng plaintext trong Lambda console và trong response của get-function-configuration. Nếu bạn cần bảo vệ giá trị khỏi bị nhìn thấy qua console — ví dụ với database password — bạn cần dùng encryption helpers (encrypt in transit).
Với cơ chế này, bạn tự mã hóa giá trị bằng KMS trước khi lưu vào environment variable, và decrypt trong runtime. Lambda console chỉ thấy ciphertext.
- Encrypt trước khi lưu: Dùng
kms:Encryptđể mã hóa plaintext value, lưu ciphertext vào environment variable. - Runtime decrypt: Trong function code, gọi
kms:Decryptđể lấy plaintext value khi cần dùng. - Cache kết quả: Decrypt một lần khi cold start, cache trong biến module-level để tránh gọi KMS mỗi invocation.
Encrypt Giá Trị Trước Khi Lưu
# Mã hóa database password bằng KMS
aws kms encrypt \
--key-id alias/lambda-my-db-function \
--plaintext fileb://<(echo -n 'my-secret-password') \
--query 'CiphertextBlob' \
--output text
# Output là base64-encoded ciphertext — lưu giá trị này vào environment variable
# Lưu ciphertext vào environment variable
aws lambda update-function-configuration \
--function-name my-db-function \
--environment 'Variables={DB_ENDPOINT=mydb.cluster-abc123.us-east-1.rds.amazonaws.com,DB_PASSWORD_ENCRYPTED=<CIPHERTEXT_BASE64>}'
Decrypt Trong Function Code (Python)
🔽 Python handler với KMS decrypt và caching (click để mở rộng)
import os
import boto3
import base64
# Cache ở module level — chỉ decrypt một lần per cold start
_db_password = None
def get_db_password():
global _db_password
if _db_password is not None:
return _db_password
kms_client = boto3.client('kms')
encrypted_value = os.environ['DB_PASSWORD_ENCRYPTED']
response = kms_client.decrypt(
CiphertextBlob=base64.b64decode(encrypted_value)
)
_db_password = response['Plaintext'].decode('utf-8')
return _db_password
def handler(event, context):
db_endpoint = os.environ['DB_ENDPOINT']
db_password = get_db_password()
# ... kết nối database với db_endpoint và db_password
return {'statusCode': 200}
Caching plaintext trong module-level variable là đánh đổi có chủ ý: bạn giảm số lần gọi KMS (và latency tương ứng) nhưng giá trị tồn tại trong memory suốt vòng đời của execution environment. Với Lambda, đây thường là lựa chọn hợp lý hơn so với gọi KMS mỗi invocation.
Một Lỗi Hay Gặp: AccessDeniedException Khi Cold Start
Triệu chứng: function hoạt động bình thường trong môi trường dev, nhưng sau khi deploy lên production với CMK mới, CloudWatch Logs chỉ thấy AccessDeniedException ngay từ đầu — không có log nào từ handler code.
Chẩn đoán ban đầu thường sai: engineer kiểm tra VPC configuration, security group, hay network connectivity vì nghĩ function không kết nối được database. Thực ra function chưa kịp chạy đến dòng code nào — nó fail ngay khi Lambda runtime cố gắng decrypt environment variables lúc khởi tạo execution environment.
Nguyên nhân thực sự: execution role thiếu kms:Decrypt trên CMK, hoặc KMS key policy không cho phép execution role sử dụng key. Hai lớp phân quyền này — IAM policy trên role và KMS key policy — đều phải cho phép thì decrypt mới thành công.
# Kiểm tra key policy hiện tại
aws kms get-key-policy \
--key-id alias/lambda-my-db-function \
--policy-name default \
--query 'Policy' \
--output text
# Kiểm tra execution role có quyền kms:Decrypt không
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:role/my-db-function-execution-role \
--action-names kms:Decrypt \
--resource-arns arn:aws:kms:us-east-1:123456789012:key/<KEY_ID>
KMS key policy là lớp kiểm soát độc lập với IAM — ngay cả khi IAM policy cho phép, key policy có thể deny. Cả hai phải align.
So Sánh: Environment Variables vs. AWS Secrets Manager vs. SSM Parameter Store
Environment variables phù hợp cho configuration không quá nhạy cảm và không cần rotation. Với database credentials thực sự, bạn nên cân nhắc Secrets Manager hoặc SSM Parameter Store — cả hai đều hỗ trợ rotation tự động và audit trail chi tiết hơn.
| Tiêu chí | Env Variables + KMS | SSM Parameter Store | Secrets Manager |
|---|---|---|---|
| Độ phức tạp setup | Thấp | Trung bình | Trung bình |
| Automatic rotation | Không | Không (tự implement) | Có (native) |
| Audit trail | CloudTrail KMS events | CloudTrail SSM events | CloudTrail Secrets Manager events |
| Latency khi đọc | Không có (inject lúc start) | Network call | Network call |
| Phù hợp cho | Non-sensitive config, endpoints | Config có versioning | Credentials cần rotation |
Wrap-Up: Sử Dụng Lambda Environment Variables Đúng Cách
Với Lambda Environment Variables, nguyên tắc cơ bản là: dùng AWS-managed key cho config thông thường, chuyển sang CMK khi cần audit trail riêng hoặc kiểm soát access theo function, và dùng encryption in transit khi giá trị không được phép xuất hiện dưới dạng plaintext trong console. Với database credentials thực sự nhạy cảm cần rotation, Secrets Manager là lựa chọn phù hợp hơn.
Tài liệu tham khảo chính thức:
- AWS Lambda — Using environment variables
- AWS Lambda — Encrypting environment variables
- AWS KMS — Key policies
Glossary — Thuật Ngữ Chính
| Thuật ngữ | Giải thích |
|---|---|
| CMK (Customer Managed Key) | KMS key do bạn tạo và quản lý, có key policy riêng, rotation policy riêng |
| Execution Role | IAM role gắn với Lambda function, xác định quyền mà function code có thể sử dụng |
| Encryption at rest | Mã hóa dữ liệu khi lưu trữ — environment variables luôn được mã hóa at rest bởi Lambda |
| Encryption in transit (context Lambda) | Mã hóa giá trị bằng KMS trước khi lưu vào env var, decrypt thủ công trong runtime |
| Cold start | Lần đầu tiên Lambda khởi tạo execution environment — đây là thời điểm runtime inject environment variables |
Nhận xét
Đăng nhận xét