AWS Secrets Manager: Python Lambda에서 비밀을 설정하고 가져오는 방법
Source: Dev.to
민감한 정보(예: 데이터베이스 비밀번호, API 키, 토큰 등)를 관리하는 것은 안전한 클라우드 애플리케이션을 구축하는 데 있어 중요한 부분입니다. 소스 코드나 설정 파일에 비밀을 하드코딩하는 것은 흔히 발생하는 안티패턴으로, 보안 취약점을 초래합니다.
AWS Secrets Manager는 런타임에 비밀을 동적으로 저장하고 검색할 수 있는 안전하고 확장 가능하며 감사 가능한 방법을 제공합니다. 이 글에서는 다음 내용을 다룹니다:
- AWS Secrets Manager가 무엇인지
- 비밀을 생성하고 저장하는 방법
- 필요한 IAM 권한
- Python AWS Lambda에서 비밀을 가져오는 방법
- 모범 사례
1. AWS Secrets Manager란 무엇인가?
AWS Secrets Manager는 다음을 도와주는 관리형 서비스입니다:
- 비밀(자격 증명, API 키, 토큰)을 안전하게 저장
- AWS KMS를 사용하여 비밀을 암호화
- IAM을 통해 접근 제어
- 지원되는 서비스에 대해 비밀을 자동으로 교체
- 런타임에 프로그래밍 방식으로 비밀을 검색
일반적인 사용 사례:
- 데이터베이스 자격 증명 (RDS, Aurora)
- 타사 API 키
- JWT 서명 비밀
- OAuth 클라이언트 비밀
2. AWS Secrets Manager에서 비밀 생성
단계 1: AWS Secrets Manager 열기
- AWS 콘솔에 로그인합니다.
- Secrets Manager 로 이동합니다.
- Store a new secret 를 클릭합니다.
단계 2: 비밀 유형 선택
맞춤형 키/값 쌍을 저장하려면 Other type of secret 을 선택합니다. 예시:
DB_USERNAME = admin
DB_PASSWORD = StrongPassword@123
DB_HOST = mydb.cluster-xyz.us-east-1.rds.amazonaws.com
Secrets Manager는 이러한 값을 암호화된 JSON 형태로 저장합니다.
단계 3: 암호화 구성
- 기본 AWS 관리형 KMS 키를 선택하거나, 또는
- 보다 엄격한 규정 준수를 위해 고객 관리형 KMS 키를 선택합니다.
단계 4: 비밀 이름 지정
예시와 같이 명확하고 환경을 구분할 수 있는 이름을 지정합니다:
myapp/dev/databasemyapp/staging/databasemyapp/prod/database
이러한 명명 전략은 실수로 다른 환경에 접근하는 것을 방지합니다.
단계 5: 검토 및 생성
Store 를 클릭합니다. 이제 비밀이 안전하게 저장되었습니다.
3. Lambda에 대한 IAM 권한
Lambda 함수는 비밀을 읽을 수 있는 권한이 있어야 합니다.
IAM 정책 예시
다음 정책을 Lambda 실행 역할에 연결하십시오:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:myapp/dev/database*"
}
]
}
중요: Resource를 "*" 대신 특정 비밀에만 제한하도록 항상 설정하십시오.
4. Python Lambda에서 비밀 가져오기
단계 1: Python 종속성
AWS Lambda에는 이미 boto3와 botocore가 포함되어 있습니다. 추가 라이브러리는 필요하지 않습니다.
단계 2: 비밀을 가져오는 Python 코드
import json
import boto3
from botocore.exceptions import ClientError
def get_secret(secret_name, region_name="us-east-1"):
client = boto3.client(
service_name="secretsmanager",
region_name=region_name
)
try:
response = client.get_secret_value(SecretId=secret_name)
except ClientError as e:
raise RuntimeError(f"Unable to retrieve secret: {e}")
# Secrets are usually stored as JSON strings
if "SecretString" in response:
return json.loads(response["SecretString"])
else:
# Binary secrets (rare case)
return response["SecretBinary"]
단계 3: Lambda 핸들러에서 비밀 사용
def lambda_handler(event, context):
secret_name = "myapp/dev/database"
secrets = get_secret(secret_name)
db_user = secrets["DB_USERNAME"]
db_password = secrets["DB_PASSWORD"]
db_host = secrets["DB_HOST"]
# Example usage
print(f"Connecting to DB at {db_host} with user {db_user}")
return {
"statusCode": 200,
"body": "Secrets fetched successfully"
}
5. 성능 고려 사항 (중요)
GetSecretValue에 대한 각 호출은 네트워크 호출입니다. 지연 시간과 API 사용량을 줄이기 위해 모듈 수준에서 비밀을 캐시하세요:
_cached_secrets = None
def get_cached_secret(secret_name):
global _cached_secrets
if _cached_secrets is None:
_cached_secrets = get_secret(secret_name)
return _cached_secrets
6. 환경 기반 비밀 관리
환경 변수를 사용하여 어떤 비밀이 로드될지 제어합니다:
import os
secret_name = os.environ["SECRET_NAME"] # e.g., myapp/dev/database
secrets = get_cached_secret(secret_name)
이렇게 하면:
- DEV / STAGE / PROD 전반에 동일한 코드베이스
- 환경별 비밀
- 보다 안전한 배포
7. Security and Best Practices
- 절대로 비밀을 하드코딩하지 마세요.
- 최소 권한 IAM 정책을 사용하세요.
- 환경별로 별도의 비밀을 유지하세요.
- 지원되는 경우 자동 회전을 활성화하세요.
- 성능을 위해 Lambda 내부에 비밀을 캐시하세요.
- 로그를 신중하게 남기세요—비밀 값을 절대 로그에 남기지 마세요.
- 매우 민감한 데이터는 SSM Parameter Store보다 Secrets Manager를 선호하세요.