
AWS Secrets Manager는 데이터베이스 자격증명, API 키, OAuth 토큰 및 기타 비밀 정보의 전체 수명 주기를 중앙에서 관리할 수 있는 완전 관리형 서비스입니다. 하드코딩된 자격증명을 제거하고 보안을 강화하며 규정 준수를 간소화할 수 있습니다.
Amazon RDS는 AWS Secrets Manager와의 네이티브 통합을 제공하여 데이터베이스 자격증명 관리를 더욱 안전하고 편리하게 만듭니다.
✅ 보안 강화
✅ 자동 로테이션
✅ 운영 효율성
RDS 데이터베이스를 처음 생성할 때 Secrets Manager와 직접 통합할 수 있습니다.
1단계: RDS 콘솔에서 데이터베이스 생성
AWS 콘솔에서 RDS 서비스로 이동:
RDS 콘솔 → Databases → Create database
2단계: 자격증명 설정에서 Secrets Manager 선택
Credential Settings 섹션에서:
3단계: 데이터베이스 생성 완료
나머지 설정을 구성하고 데이터베이스를 생성하면, AWS가 자동으로:
기존 RDS 데이터베이스의 자격증명을 Secrets Manager에 저장하려면:
aws secretsmanager create-secret \
--name prod/myapp/database \
--description "Production RDS MySQL credentials" \
--secret-string '{
"engine": "mysql",
"host": "mydb.c9akciq32.us-east-1.rds.amazonaws.com",
"username": "admin",
"password": "MySecurePassword123!",
"dbname": "myapplication",
"port": 3306
}' \
--tags Key=Environment,Value=Production Key=Application,Value=MyApp
import boto3
import json
# Secrets Manager 클라이언트 생성
client = boto3.client('secretsmanager', region_name='us-east-1')
# 비밀 정보 구성
secret_data = {
'engine': 'mysql',
'host': 'mydb.c9akciq32.us-east-1.rds.amazonaws.com',
'username': 'admin',
'password': 'MySecurePassword123!',
'dbname': 'myapplication',
'port': 3306
}
# 비밀 생성
response = client.create_secret(
Name='prod/myapp/database',
Description='Production RDS MySQL credentials',
SecretString=json.dumps(secret_data),
Tags=[
{'Key': 'Environment', 'Value': 'Production'},
{'Key': 'Application', 'Value': 'MyApp'}
]
)
print(f"Secret ARN: {response['ARN']}")
Secrets Manager에 저장된 자격증명을 애플리케이션에서 안전하게 가져와 사용하는 방법을 알아보겠습니다.
import boto3
import json
from botocore.exceptions import ClientError
def get_database_credentials(secret_name, region_name='us-east-1'):
"""
AWS Secrets Manager에서 데이터베이스 자격증명을 가져옵니다.
Args:
secret_name: Secrets Manager의 비밀 이름
region_name: AWS 리전
Returns:
dict: 데이터베이스 자격증명 정보
"""
# Secrets Manager 클라이언트 생성
session = boto3.session.Session()
client = session.client(
service_name='secretsmanager',
region_name=region_name
)
try:
# 비밀 값 가져오기
get_secret_value_response = client.get_secret_value(
SecretId=secret_name
)
except ClientError as e:
# 에러 처리
if e.response['Error']['Code'] == 'DecryptionFailureException':
raise Exception("복호화 실패")
elif e.response['Error']['Code'] == 'InternalServiceErrorException':
raise Exception("내부 서비스 오류")
elif e.response['Error']['Code'] == 'InvalidParameterException':
raise Exception("잘못된 파라미터")
elif e.response['Error']['Code'] == 'InvalidRequestException':
raise Exception("잘못된 요청")
elif e.response['Error']['Code'] == 'ResourceNotFoundException':
raise Exception(f"비밀을 찾을 수 없습니다: {secret_name}")
else:
# SecretString 파싱
if 'SecretString' in get_secret_value_response:
secret = get_secret_value_response['SecretString']
return json.loads(secret)
else:
raise Exception("SecretString이 없습니다")
# 사용 예제
credentials = get_database_credentials('prod/myapp/database')
# 데이터베이스 연결
import pymysql
connection = pymysql.connect(
host=credentials['host'],
user=credentials['username'],
password=credentials['password'],
database=credentials['dbname'],
port=credentials['port']
)
print("데이터베이스 연결 성공!")
const AWS = require('aws-sdk');
// Secrets Manager 클라이언트 생성
const client = new AWS.SecretsManager({
region: 'us-east-1'
});
async function getDatabaseCredentials(secretName) {
try {
const data = await client.getSecretValue({
SecretId: secretName
}).promise();
if ('SecretString' in data) {
return JSON.parse(data.SecretString);
} else {
// Binary secret 처리
let buff = Buffer.from(data.SecretBinary, 'base64');
return JSON.parse(buff.toString('ascii'));
}
} catch (err) {
console.error('Error retrieving secret:', err);
throw err;
}
}
// 사용 예제
(async () => {
const credentials = await getDatabaseCredentials('prod/myapp/database');
const mysql = require('mysql2/promise');
const connection = await mysql.createConnection({
host: credentials.host,
user: credentials.username,
password: credentials.password,
database: credentials.dbname,
port: credentials.port
});
console.log('데이터베이스 연결 성공!');
})();
import com.amazonaws.services.secretsmanager.AWSSecretsManager;
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
import com.amazonaws.services.secretsmanager.model.*;
import com.google.gson.Gson;
import java.util.Map;
public class DatabaseCredentials {
public static Map<String, String> getSecret(String secretName) {
String region = "us-east-1";
// Secrets Manager 클라이언트 생성
AWSSecretsManager client = AWSSecretsManagerClientBuilder.standard()
.withRegion(region)
.build();
String secret;
GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest()
.withSecretId(secretName);
GetSecretValueResult getSecretValueResult = null;
try {
getSecretValueResult = client.getSecretValue(getSecretValueRequest);
} catch (DecryptionFailureException e) {
throw e;
} catch (InternalServiceErrorException e) {
throw e;
} catch (InvalidParameterException e) {
throw e;
} catch (InvalidRequestException e) {
throw e;
} catch (ResourceNotFoundException e) {
throw e;
}
if (getSecretValueResult.getSecretString() != null) {
secret = getSecretValueResult.getSecretString();
Gson gson = new Gson();
return gson.fromJson(secret, Map.class);
}
return null;
}
public static void main(String[] args) {
Map<String, String> credentials = getSecret("prod/myapp/database");
System.out.println("Host: " + credentials.get("host"));
System.out.println("Database: " + credentials.get("dbname"));
}
}
정기적인 자격증명 로테이션은 보안 모범 사례입니다. Secrets Manager는 RDS 데이터베이스에 대한 자동 로테이션을 지원합니다.
1단계: Secrets Manager 콘솔로 이동
Secrets Manager → Secrets → 비밀 선택
2단계: 로테이션 구성
3단계: Lambda 함수 선택
# 로테이션 활성화
aws secretsmanager rotate-secret \
--secret-id prod/myapp/database \
--rotation-lambda-arn arn:aws:lambda:us-east-1:123456789012:function:SecretsManagerRDSMySQLRotation \
--rotation-rules AutomaticallyAfterDays=30
자동 로테이션은 다음 단계로 진행됩니다:
이 과정은 다운타임 없이 진행되며, 애플리케이션은 자동으로 새 자격증명을 사용하게 됩니다.
AWS Secrets Manager와 RDS를 함께 사용할 때 다음 모범 사례를 따르세요:
IAM 정책을 사용하여 필요한 최소 권한만 부여:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/myapp/database-*"
}
]
}
프라이빗 서브넷의 리소스가 인터넷을 거치지 않고 Secrets Manager에 액세스할 수 있도록 VPC 엔드포인트 구성:
aws ec2 create-vpc-endpoint \
--vpc-id vpc-1234567890abcdef0 \
--service-name com.amazonaws.us-east-1.secretsmanager \
--route-table-ids rtb-1234567890abcdef0
사용자 지정 KMS 키를 사용하여 비밀 암호화:
# KMS 키 생성
aws kms create-key --description "Secrets Manager encryption key"
# 비밀 생성 시 KMS 키 지정
aws secretsmanager create-secret \
--name prod/myapp/database \
--kms-key-id arn:aws:kms:us-east-1:123456789012:key/abcd1234-a123-456a-a12b-a123b4cd56ef \
--secret-string file://credentials.json
비밀에 태그를 추가하여 관리 및 비용 추적 간소화:
aws secretsmanager tag-resource \
--secret-id prod/myapp/database \
--tags Key=Environment,Value=Production Key=CostCenter,Value=Engineering
모든 Secrets Manager API 호출을 CloudTrail로 모니터링:
# CloudWatch Logs로 이벤트 필터링
aws logs put-metric-filter \
--log-group-name /aws/cloudtrail/my-trail \
--filter-name SecretsManagerAccess \
--filter-pattern '{ $.eventName = "GetSecretValue" }' \
--metric-transformations metricName=SecretAccessCount,metricNamespace=CustomMetrics,metricValue=1
보안 정책에 따라 자격증명을 정기적으로 로테이션:
여러 버전을 유지하여 롤백 가능성 확보:
# 이전 버전으로 롤백
client.update_secret_version_stage(
SecretId='prod/myapp/database',
VersionStage='AWSCURRENT',
RemoveFromVersionId='current-version-id',
MoveToVersionId='previous-version-id'
)
AWS Secrets Manager와 RDS의 통합은 데이터베이스 자격증명을 안전하게 관리하는 강력한 솔루션을 제공합니다. 자동 로테이션, 중앙 집중식 관리, 그리고 세밀한 액세스 제어를 통해 보안을 강화하고 운영 효율성을 높일 수 있습니다.
✨ 보안: 평문 자격증명 제거 및 암호화된 저장
✨ 자동화: 수동 개입 없는 자격증명 로테이션
✨ 통합: AWS 서비스와의 원활한 연동
✨ 감사: CloudTrail을 통한 완전한 추적성
✨ 효율성: 중앙 집중식 관리로 복잡성 감소
이제 여러분의 RDS 데이터베이스에 AWS Secrets Manager를 적용하여 보안과 운영 효율성을 모두 향상시켜 보세요!
Tags: #AWS #SecretsManager #RDS #보안 #자격증명관리 #클라우드보안 #DevSecOps