기준: SOA-C02 시험 가이드오류 제보
2019년 7월 공개된 사건에서, 전직 AWS 엔지니어 Paige Thompson이 Capital One에서 1억 600만 명의 카드 신청 데이터를 탈취했다. 공격 경로는 이랬다. WAF의 SSRF 취약점으로 http://169.254.169.254/latest/meta-data/iam/security-credentials/에 접근해 EC2의 IAM 임시 자격증명을 탈취했다. 이 자격증명으로 S3 버킷의 데이터를 빼냈다. 문제의 근원 중 하나는 IMDSv1(인증 없이 메타데이터 접근) 이었다. 오늘 다루는 Session Manager는 SSH 키나 IMDSv1 없이도 인스턴스에 안전하게 접근하는 방법을 제공하고, Parameter Store는 자격증명을 코드에 하드코딩하지 않는 방법을 제공한다.
오늘은 SSM의 "자동화 심화" 3종 세트를 다룬다. Parameter Store로 설정을 중앙화하고, Session Manager로 SSH/RDP를 대체하며, Automation Runbook으로 복잡한 운영 워크플로를 자동화한다.
Parameter Store는 단순한 키-값 저장소처럼 보이지만, 운영 관점에서 매우 중요한 역할을 한다. "코드와 설정의 분리"(12-Factor App, Factor III)는 현대 애플리케이션의 기본 원칙이다. 환경변수나 설정 파일에 DB 패스워드나 API 키를 넣는 것은 버전 컨트롤에 비밀번호를 노출시키는 위험이 있다. Parameter Store는 이 문제를 해결한다.
파라미터 계층 구조의 설계 원칙:
/app-name/environment/component/key 패턴 권장
예시:
/myapp/prod/database/host
/myapp/prod/database/port
/myapp/prod/database/password ← SecureString (KMS 암호화)
/myapp/prod/api/stripe-key ← SecureString
/myapp/prod/feature/new-search ← String (Feature Flag)
/shared/global/slack-webhook-url ← String (공유 설정)
계층 구조를 사용하면 GetParametersByPath로 한 경로 아래 모든 파라미터를 한 번에 가져올 수 있다. Lambda 함수나 EC2 User Data에서 /myapp/prod/ 하위 전체를 한 번의 API 호출로 로드하는 것이 가능해진다.
Standard vs Advanced 비교:
| 항목 | Standard | Advanced |
|---|---|---|
| 최대 파라미터 수 | 10,000개/계정/리전 | 100,000개 |
| 최대 값 크기 | 4KB | 8KB |
| Parameter Policies | 불가 | Expiration, 변경 알림, NoChange 알림 |
| 처리량(TPS) | 40 TPS (기본) | 1,000 TPS (추가 요금) |
| 비용 | 무료 | $0.05/파라미터/월 + API 요금 |
Parameter Policies (Advanced 전용):
# Advanced 파라미터 생성 + 만료 정책
aws ssm put-parameter \
--name "/myapp/prod/temp-access-token" \
--value "$(openssl rand -base64 32)" \
--type SecureString \
--key-id "alias/myapp-key" \
--tier Advanced \
--policies '[
{
"Type": "Expiration",
"Version": "1.0",
"Attributes": {
"Timestamp": "2026-12-31T00:00:00Z"
}
},
{
"Type": "ExpirationNotification",
"Version": "1.0",
"Attributes": {
"Before": "14",
"Unit": "days"
}
},
{
"Type": "NoChangeNotification",
"Version": "1.0",
"Attributes": {
"After": "30",
"Unit": "days"
}
}
]'Expiration: 지정 날짜에 파라미터 자동 삭제 (임시 자격증명 관리에 유용)ExpirationNotification: 만료 N일 전 EventBridge → SNS → 담당자 알림NoChangeNotification: N일 동안 값이 변경되지 않으면 알림 (잊혀진 파라미터 감지)💡 관련 이론: Parameter Store의 계층 구조와 Policy 개념은 Linux 파일 시스템의 inode 구조와 유사하다. 파일(파라미터)에 메타데이터(Policy)를 부착하고, 디렉터리(경로 계층)로 논리적 그룹화를 한다. POSIX 표준의 "모든 것은 파일이다" 철학처럼, AWS는 "모든 설정은 Parameter"라는 철학으로 운영 설정을 통일한다. 버전 관리(모든 변경 이력 자동 보존)는 Git의 commit 히스토리와 동일한 개념으로 롤백이 가능하다.
여러 서비스와의 통합:
# CloudFormation에서 동적 참조
# template.yaml
DBPassword:
!Sub '{{resolve:secretsmanager:${SecretArn}:SecretString:password}}'
# 또는 Parameter Store에서
DBHost:
!Sub '{{resolve:ssm:/myapp/prod/database/host}}'
# EC2 User Data에서 사용
#!/bin/bash
DB_HOST=$(aws ssm get-parameter \
--name "/myapp/prod/database/host" \
--query 'Parameter.Value' --output text)
echo "DB_HOST=$DB_HOST" >> /etc/environment
# Lambda 함수에서 사용 (환경변수보다 안전)
import boto3
ssm = boto3.client('ssm')
def get_config(path):
params = ssm.get_parameters_by_path(
Path=path,
Recursive=True,
WithDecryption=True
)
return {p['Name'].split('/')[-1]: p['Value']
for p in params['Parameters']}
config = get_config('/myapp/prod/')
db_password = config['password'] # 코드에 하드코딩 없이이 두 서비스는 자주 혼동된다. 결정 기준은 명확하다.
| 기준 | Parameter Store | Secrets Manager |
|---|---|---|
| 자동 회전(Rotation) | X | O (RDS, Redshift, DocumentDB, Lambda 통합) |
| 크로스 리전 복제 | X | O |
| 크로스 계정 공유 | Advanced + Resource Policy | O (기본 지원) |
| 비용 | 무료/저렴 | $0.40/시크릿/월 + $0.05/10,000 API |
| 값 크기 | 4KB/8KB | 64KB |
| 주요 사용 사례 | 앱 설정, Feature Flag, 정적 비밀 | DB 패스워드 자동 회전, OAuth 토큰 |
의사결정 흐름:
자동 회전이 필요한가?
├── 예 → Secrets Manager
└── 아니오
│
크로스 리전 복제가 필요한가?
├── 예 → Secrets Manager
└── 아니오 → Parameter Store
│
값이 4KB 초과이거나 Policy가 필요한가?
├── 예 → Parameter Store Advanced
└── 아니오 → Parameter Store Standard (무료)
📚 사례: 가상의 사례로, 스타트업 D사가 초기에 Parameter Store SecureString에 RDS 패스워드를 저장했다. 3개월 후 보안 감사에서 "DB 패스워드 90일 회전" 요구사항이 나왔다. Parameter Store는 자동 회전 기능이 없어, 매 90일마다 수동으로 패스워드를 변경하고 Parameter를 업데이트해야 했다. 결국 Secrets Manager로 마이그레이션했고, 이후 RDS와 Lambda 통합으로 자동 회전이 구성됐다. 초기 아키텍처 선택이 중요한 이유다.
Session Manager는 SSH와 RDP를 완전히 대체하는 AWS 관리형 접속 도구다. 내부적으로는 SSM Agent가 WebSocket 기반의 Session Channel을 생성해 운영자 CLI와 인스턴스 사이에 암호화된 터널을 형성한다.
전통적 Bastion vs Session Manager 아키텍처:
전통적 SSH/Bastion 방식:
운영자 → [인터넷] → Bastion(포트 22 오픈) → SSH 키 인증 → EC2(포트 22 오픈)
↑ 공격 표면 2곳
Session Manager 방식:
운영자 → AWS API(HTTPS) → SSM Service → SSM Agent(아웃바운드 443만) → EC2
↑ 인바운드 포트 0개
Session Manager의 보안 계층:
ssm:StartSession 권한이 있는 IAM User/Role만 접속 가능aws:MultiFactorAuthPresent: true 추가idleSessionTimeout, maxSessionDuration세밀한 접속 제어 (IAM 정책):
// "dev 태그 인스턴스에는 접속 가능, prod는 불가" 정책
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ssm:StartSession",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringEquals": {
"ssm:resourceTag/Environment": ["dev", "stage"]
},
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "true"
}
}
},
{
"Effect": "Allow",
"Action": [
"ssm:DescribeSessions",
"ssm:GetConnectionStatus",
"ssm:DescribeInstanceInformation"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "ssm:TerminateSession",
"Resource": "arn:aws:ssm:*:*:session/${aws:username}-*"
}
]
}Session Manager 실전 활용:
# 기본 세션 시작
aws ssm start-session --target i-0123456789abcdef0
# 특정 명령 실행 (Interactive 없이)
aws ssm start-session \
--target i-0123456789abcdef0 \
--document-name AWS-StartInteractiveCommand \
--parameters '{"command":["sudo journalctl -u nginx --no-pager -n 50"]}'
# 포트 포워딩: 로컬 포트로 RDS 직접 접근 (RDS에 SG 인바운드 없이!)
aws ssm start-session \
--target i-0123456789abcdef0 \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{
"host": ["mydb.cluster-abc.ap-northeast-2.rds.amazonaws.com"],
"portNumber": ["5432"],
"localPortNumber": ["5432"]
}'
# 이후 로컬에서: psql -h localhost -p 5432 -U admin mydb
# 원격 호스트 포워딩 (EC2 중계 서버 활용)
aws ssm start-session \
--target i-0123456789abcdef0 \
--document-name AWS-StartPortForwardingSession \
--parameters '{"portNumber":["80"],"localPortNumber":["8080"]}'Session Manager 감사 설정:
# Session Preferences Document 업데이트 (계정 단위)
aws ssm update-document \
--name "SSM-SessionManagerRunShell" \
--content '{
"schemaVersion": "1.0",
"description": "SSM Session Manager Preferences",
"sessionType": "Standard_Stream",
"inputs": {
"s3BucketName": "session-audit-logs",
"s3KeyPrefix": "sessions",
"s3EncryptionEnabled": true,
"cloudWatchLogGroupName": "/ssm/sessions",
"cloudWatchEncryptionEnabled": true,
"kmsKeyId": "alias/ssm-sessions",
"idleSessionTimeout": "20",
"maxSessionDuration": "60",
"runAsEnabled": true,
"runAsDefaultUser": "ssm-user"
}
}' \
--document-version '$LATEST'Session Manager의 runAsEnabled와 runAsDefaultUser는 중요한 보안 기능이다. 활성화하면 모든 세션이 ssm-user(또는 지정한 사용자)로 실행된다. IAM User와 Linux OS User를 매핑할 수 있는 RunAsUser 태그도 지원한다. IAM User가 SessionManagerRunAs 태그에 ec2-user를 설정하면, 그 사용자의 세션은 ec2-user로 실행된다. 이로써 누가(IAM User) 어떤 OS 사용자로 접속했는지 세션 로그에서 추적 가능해진다.
사설 VPC에서 Session Manager:
# 3개 Interface Endpoint 생성 (인터넷 없이 Session Manager)
for service in ssm ssmmessages ec2messages; do
aws ec2 create-vpc-endpoint \
--vpc-id vpc-0123456789abcdef0 \
--vpc-endpoint-type Interface \
--service-name com.amazonaws.ap-northeast-2.$service \
--subnet-ids subnet-abc subnet-xyz \
--security-group-ids sg-ssm-endpoints \
--private-dns-enabled
done
# 이후 포트 22/3389 보안 그룹 규칙 완전 제거 가능Automation Runbook(SSM Document Type: Automation)은 여러 단계의 작업을 순서·조건·반복으로 묶어 자동화하는 워크플로 엔진이다. "EC2 패치 후 재부팅, 재부팅 후 헬스체크, 헬스체크 실패 시 롤백"처럼 사람이 수동으로 해야 했던 복잡한 작업을 코드로 표현한다.
Runbook Action 종류:
| Action | 용도 |
|---|---|
aws:runCommand | Run Command 실행 |
aws:executeAwsApi | 임의 AWS API 호출 |
aws:waitForAwsResourceProperty | 리소스 상태 대기 (polling) |
aws:assertAwsResourceProperty | 리소스 속성 검증 |
aws:createStack | CloudFormation Stack 생성 |
aws:sleep | 대기 |
aws:branch | 조건 분기 |
aws:loop | 반복 |
aws:approve | 인간 승인 대기 |
aws:invokeLambdaFunction | Lambda 호출 |
aws:changeInstanceState | EC2 상태 변경 |
실전 Runbook: 무중단 패치 시퀀스:
schemaVersion: '0.3'
description: Zero-downtime patch sequence with ELB deregistration
assumeRole: '{{ AutomationAssumeRole }}'
parameters:
InstanceId:
type: String
description: EC2 Instance ID to patch
TargetGroupArn:
type: String
description: ALB Target Group ARN
AutomationAssumeRole:
type: String
mainSteps:
# Step 1: ELB에서 제거
- name: DeregisterFromTargetGroup
action: aws:executeAwsApi
inputs:
Service: elbv2
Api: DeregisterTargets
TargetGroupArn: '{{ TargetGroupArn }}'
Targets:
- Id: '{{ InstanceId }}'
outputs:
- Name: DeregisterStatus
Selector: $.ResponseMetadata.HTTPStatusCode
# Step 2: 드레이닝 완료 대기 (최대 300초)
- name: WaitForDraining
action: aws:waitForAwsResourceProperty
timeoutSeconds: 300
inputs:
Service: elbv2
Api: DescribeTargetHealth
TargetGroupArn: '{{ TargetGroupArn }}'
Targets:
- Id: '{{ InstanceId }}'
PropertySelector: '$.TargetHealthDescriptions[0].TargetHealth.State'
DesiredValues:
- unused
- ''
# Step 3: EBS 스냅샷 생성
- name: CreateSnapshot
action: aws:executeAwsApi
inputs:
Service: ec2
Api: CreateSnapshot
VolumeId: '{{ getVolumeId.VolumeId }}'
Description: 'Pre-patch snapshot {{ InstanceId }}'
outputs:
- Name: SnapshotId
Selector: $.SnapshotId
# Step 4: 패치 적용
- name: ApplyPatches
action: aws:runCommand
inputs:
DocumentName: AWS-RunPatchBaseline
InstanceIds:
- '{{ InstanceId }}'
Parameters:
Operation:
- Install
RebootOption:
- RebootIfNeeded
# Step 5: 재부팅 완료 대기
- name: WaitForReboot
action: aws:waitForAwsResourceProperty
timeoutSeconds: 600
inputs:
Service: ec2
Api: DescribeInstanceStatus
InstanceIds:
- '{{ InstanceId }}'
PropertySelector: '$.InstanceStatuses[0].InstanceStatus.Status'
DesiredValues:
- ok
# Step 6: 애플리케이션 헬스체크
- name: ApplicationHealthCheck
action: aws:runCommand
inputs:
DocumentName: AWS-RunShellScript
InstanceIds:
- '{{ InstanceId }}'
Parameters:
commands:
- 'curl -sf http://localhost/health || exit 1'
# Step 7: ELB에 재등록
- name: RegisterToTargetGroup
action: aws:executeAwsApi
inputs:
Service: elbv2
Api: RegisterTargets
TargetGroupArn: '{{ TargetGroupArn }}'
Targets:
- Id: '{{ InstanceId }}'Automation 트리거 패턴:
| 트리거 | 사용 사례 |
|---|---|
| 수동 (콘솔/CLI) | 임시 운영, 테스트 |
| EventBridge → Automation | CloudTrail 이벤트 기반 자동 복구 |
| Config Auto Remediation | 비준수 리소스 자동 수정 |
| CloudWatch Alarm Action | 메트릭 이상 시 자동 조치 |
| Change Manager | 승인 워크플로 포함 변경 |
EventBridge로 S3 버킷 Public 설정 자동 차단:
# EventBridge Rule: S3 PutBucketAcl 이벤트 → Automation
aws events put-rule \
--name "AutoBlockS3PublicAccess" \
--event-pattern '{
"source": ["aws.s3"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventName": ["PutBucketAcl", "PutBucketPolicy"],
"requestParameters": {
"AccessControlList": {
"AccessControlPolicy": {
"Owner": [{"anything-but": ""}]
}
}
}
}
}' \
--state ENABLED
# Target: SSM Automation
aws events put-targets \
--rule "AutoBlockS3PublicAccess" \
--targets '[{
"Id": "BlockS3Public",
"Arn": "arn:aws:ssm:ap-northeast-2:123456789012:automation-definition/AWS-DisableS3BucketPublicReadWrite",
"RoleArn": "arn:aws:iam::123456789012:role/EventBridgeSSMRole",
"Input": "{\"S3BucketName\":[\"detail.requestParameters.bucketName\"]}"
}]'AppConfig는 Parameter Store의 "동적 배포" 버전이다. Parameter Store가 정적 설정 저장이라면, AppConfig는 설정 변경을 Canary/Linear 전략으로 점진 배포하고 알람 기반으로 자동 롤백한다.
AppConfig vs Parameter Store 비교:
| 항목 | AppConfig | Parameter Store |
|---|---|---|
| 배포 전략 | Canary, Linear, AllAtOnce | 없음 (즉시 적용) |
| 자동 롤백 | CloudWatch Alarm 연결 | 없음 |
| 유효성 검사 | JSON Schema, Lambda | 없음 |
| 클라이언트 캐싱 | Lambda Extension으로 자동 캐시 | 직접 구현 |
| 사용 사례 | Feature Flag, 알고리즘 파라미터 | 앱 설정, 비밀 |
Feature Flag 구현 예시:
# Lambda에서 AppConfig 사용 (Lambda Powertools 활용)
from aws_lambda_powertools.utilities.feature_flags import FeatureFlags, AppConfigStore
# AppConfig Lambda Extension이 로컬 HTTP로 캐싱된 설정 제공
store = AppConfigStore(
environment="prod",
application="OrderService",
name="FeatureFlags",
cache_seconds=30 # 30초 캐시 (실시간성과 성능 균형)
)
feature_flags = FeatureFlags(store=store)
def handler(event, context):
# Feature Flag 확인
if feature_flags.evaluate(name="new_checkout_flow", default=False):
return new_checkout_handler(event)
else:
return legacy_checkout_handler(event)AppConfig의 점진 배포 전략은 Netflix가 2012년 설계한 "Simian Army"의 "Canary Analysis" 개념과 같다. 전체 배포 전에 일부 인스턴스/사용자에게 먼저 적용하고, 메트릭을 관찰한 뒤 확대하는 방식이다. 구글의 SRE 책(2016)에서도 같은 패턴을 "gradual rollout"이라 부르며 설정 변경의 표준 방식으로 권장한다. AppConfig가 이 산업 모범 사례를 관리형 서비스로 제공하는 것이다.
Change Manager는 Automation Runbook에 RFC(Request for Change) 승인 워크플로를 추가한다. ITIL(IT Infrastructure Library) 변경 관리 프로세스의 AWS 구현이다.
# Change Manager로 변경 요청 생성
aws ssm start-change-request-execution \
--change-request-name "patch-prod-db-2026-05-26" \
--document-name "AWS-RunPatchBaseline" \
--parameters '{"InstanceId":["i-0abc123"],"Operation":["Install"]}' \
--scheduled-time "2026-05-26T02:00:00Z" \
--runbooks '[{
"DocumentName": "MyApp-SafePatchSequence",
"DocumentVersion": "$LATEST",
"Parameters": {"InstanceId":["i-0abc123"],"TargetGroupArn":["arn:aws:elasticloadbalancing:..."]}
}]' \
--tags '[{"Key":"ChangeType","Value":"Patch"},{"Key":"Environment","Value":"prod"}]'승인자는 슬랙이나 이메일(SNS → 외부 알림)로 변경 요청 알림을 받고, Change Manager 콘솔에서 승인/거부한다. 승인 후 예약된 시간에 자동으로 실행된다.
SSM 심화 자동화 통합 구조
============================================================
[Parameter Store]
/myapp/prod/db/password (SecureString, KMS)
/myapp/prod/feature/new-search (String, Feature Flag)
│
├── Lambda/EC2에서 실행 시 fetch (코드 분리)
├── CloudFormation: {{resolve:ssm:/path/to/param}}
└── AppConfig: 동적 배포 + 자동 롤백
[Session Manager]
운영자 (IAM 인증) → SSM → Agent → EC2
│ 포트 22/3389 없이
├── 모든 세션 S3/CW Logs 자동 저장
├── 포트 포워딩으로 RDS/Redis 안전 접근
└── 사설 VPC: VPC Endpoint 3개로 동작
[Automation Runbook]
EventBridge → Config Auto Remediation
CloudWatch Alarm →
Change Manager (승인) →
수동 실행 →
│
└── Runbook 실행:
Step 1: ELB Deregister
Step 2: Snapshot
Step 3: Patch
Step 4: Health Check
Step 5: ELB Register
(실패 시 자동 롤백)
선택지를 클릭하면 정답·해설이 펼쳐집니다.
문제 1
Lambda 함수가 RDS 데이터베이스에 연결하기 위한 비밀번호를 안전하게 관리해야 한다. 90일마다 자동으로 비밀번호가 교체되어야 하고, Lambda 코드 재배포 없이 새 비밀번호를 사용해야 한다. 가장 적합한 도구는?
문제 2
회사 보안 정책상 모든 EC2 접속은 감사 로그로 남아야 하고, 포트 22는 보안 그룹에서 완전히 제거해야 한다. 개발자들은 EC2에 접속하여 로그를 확인하고 디버깅해야 한다. 가장 적합한 솔루션은?
문제 3
SSM Automation Runbook에 "변경 전 운영팀 리더의 승인이 필요하다"는 요구사항이 있다. Runbook 실행 중 리더가 승인하기 전까지 다음 단계로 넘어가면 안 된다. 어떻게 구현하는가?
문제 4
운영팀이 Parameter Store에 저장된 API 키가 3개월 동안 변경되지 않은 경우 알림을 받고 싶다. 또한 만료 예정일 2주 전에도 알림을 받아야 한다. 어떻게 구현하는가?
문제 5
Config Rule이 "암호화되지 않은 EBS 볼륨"을 발견했을 때 자동으로 스냅샷을 생성하고 암호화된 볼륨으로 교체하는 작업을 자동화하려 한다. 가장 적합한 구성은?
문제 6
Session Manager를 통해 접속하려는데 사설 VPC에 인터넷 게이트웨이가 없다. 필요한 VPC Endpoint는?