정적 Secret
API key, DB password, OAuth client secret처럼 사람이 만들고 오래 유지하는 값이다. 유출되면 교체 전까지 계속 유효하다.
외부 SaaS key, legacy DB 계정, 자동 TTL 발급을 지원하지 않는 연동분류: Layer 3 - AWS 인프라 & 보안
Secrets Management는 API 키, DB 비밀번호, 토큰, private key처럼 알면 권한을 얻는 값을 코드와 분리해 저장하고, 필요한 워크로드에만 주입하고, 유출되면 빠르게 교체할 수 있게 만드는 체계다.
여기서 secret(시크릿)은 “민감한 문자열”보다 좁고 실용적인 개념이다. secret은 시스템이 다른 시스템에 자신을 증명하거나, 암호화된 데이터를 풀거나, 보호된 API를 호출하는 데 쓰는 값이다. 그래서 secret이 유출되면 단순 정보 노출이 아니라 권한 위임 사고가 된다.
처음 읽을 때 붙잡을 문장은 이것이다.
secret은 저장할 값이 아니라 접근 권한이다.Secrets Management는 그 권한의 저장 위치, 접근 주체, 교체 방법, 감사 기록을 설계하는 일이다.핵심 용어를 먼저 정의한다.
| 용어 | 첫 정의 | 처음 잡을 경계 |
|---|---|---|
| Secret | 알면 인증·인가·복호화가 가능한 값이다. DB password, API key, OAuth client secret, private key가 여기에 속한다. | 공개 설정값이나 feature flag와 다르다. 노출되면 권한이 생기는 값이어야 한다. |
| Static Secret | 만료 시간이 없거나 길게 유지되는 secret이다. | 유출되면 사람이 revoke 또는 rotate하기 전까지 계속 살아 있다. |
| Dynamic Secret | 요청 시점에 짧은 TTL(Time To Live)로 발급되고 만료되는 secret이다. | 저장보다 발급·만료가 핵심이다. Vault DB credential, AWS STS 세션이 대표 예다. |
| Blast Radius | secret 하나가 유출됐을 때 영향을 받는 리소스·환경·시간 범위다. | 강한 secret일수록 권한 범위와 수명이 짧아야 한다. |
| KMS | Key Management Service. 암호화 키를 만들고 보호하며 복호화 권한을 IAM으로 통제하는 서비스다. | secret 저장소가 아니라 key 계층이다. |
| Envelope Encryption | 데이터를 직접 master key로 암호화하지 않고, 데이터 키로 암호화한 뒤 그 데이터 키를 KMS로 보호하는 방식이다. | KMS가 모든 원문 데이터를 저장하는 구조가 아니다. |
| Rotation | secret 값을 새 값으로 바꾸고, 소비자가 새 값을 쓰도록 전환하는 절차다. | ”새 값을 만들기”가 아니라 구버전과 신버전의 전환 안정성까지 포함한다. |
| Version Stage | Secrets Manager에서 버전에 붙는 라벨이다. AWSCURRENT, AWSPREVIOUS, AWSPENDING이 대표다. | 버전 ID와 stage는 다르다. 앱은 보통 stage를 통해 현재 버전을 읽는다. |
| Parameter Store | AWS Systems Manager의 설정 저장소다. SecureString으로 단순 secret 저장도 가능하다. | 자동 rotation과 rich secret lifecycle은 Secrets Manager 쪽이 강하다. |
| Vault | HashiCorp의 멀티클라우드·온프레미스 secret 관리 시스템이다. | AWS 서비스가 아니라 별도 운영 대상이다. 동적 secret에 강하다. |
secret 사고는 “나중에 지우면 되는 실수”가 아니다. git에 한 번 올라간 secret은 commit history, fork, mirror, CI log, local clone에 남을 수 있다. 파일에서 지우는 git rm은 현재 파일만 지울 뿐 이미 push된 history와 외부 clone을 되돌리지 못한다.
GitGuardian State of Secrets Sprawl 2025는 2024년 공개 GitHub에서 노출된 secret을 23.8M 건, 전년 대비 25% 증가로 보고했고, 2022년에 노출된 secret 중 상당수가 수년 뒤에도 유효했다고 설명한다. 이 수치는 “정확한 최신 카운트”로 외우기보다 위험의 방향을 잡는 데 쓴다. 별도의 honeypot 실험들에서도 공개 저장소에 올라간 cloud key에 자동 스캐너가 수 분 내 접근을 시도하는 패턴이 반복적으로 관찰됐다. 발견이 늦어질수록 secret은 공격자에게 재사용 가능한 자산이 된다.
환경변수는 코드와 설정을 분리하는 데 유용하지만, secret 관리의 최종 해법은 아니다. 환경변수는 프로세스 시작 시점에 주입되는 평문 값이고, 접근 감사나 자동 rotation을 자체로 제공하지 않는다.
대표 노출 경로는 다음과 같다.
| 노출 경로 | 왜 문제가 되는가 | 먼저 잡을 개념 |
|---|---|---|
.env 파일 커밋 | local 편의 파일이 git history에 남는다. | 코드와 secret 분리, secret scanner |
| CI/CD 로그 | 배포 스크립트가 값을 echo하거나 실패 로그에 환경 전체가 찍힌다. | 로그 마스킹, OIDC, 런타임 조회 |
| 프로세스 환경 | 디버그 셸, crash dump, 런타임 dump가 환경변수를 평문으로 노출할 수 있다. | 런타임 주입, 최소 권한, 관측 범위 제한 |
| 컨테이너 오케스트레이터 spec | 잘못 저장하면 Pod spec, task definition, 백업에 값이 남는다. | ARN 참조, Secret reference |
| 장기 key 재사용 | 한 서비스의 key가 여러 서비스·환경에서 같이 쓰이면 유출 시 blast radius가 커진다. | 환경별 분리, static vs dynamic secret |
| rotation 없는 운영 | 유출된 secret을 언제 바꿔야 하는지 몰라 사실상 영구 권한이 된다. | rotation, version stage, 감사 로그 |
환경변수 자체를 금지하자는 뜻은 아니다. ECS secrets 필드처럼 저장소에는 ARN만 두고, 컨테이너 시작 시점에 플랫폼이 값을 가져와 환경변수로 넣는 패턴은 흔히 쓴다. 중요한 차이는 Task Definition이나 git에는 secret 값이 아니라 참조만 남고, 값을 가져오는 주체와 시간이 IAM·CloudTrail로 관리된다는 점이다.
secret은 언제 주입되는지도 중요하다.
빌드타임 주입: source + secret -> build artifact 결과: 이미지, 번들, sourcemap, layer cache에 값이 남을 수 있다.
런타임 주입: artifact + IAM role -> secret store 조회 -> process memory 결과: 코드와 이미지에는 값이 없고 실행 주체만 접근한다.민감 정보는 기본적으로 런타임에 주입한다. 빌드 산출물은 registry, CDN, 브라우저, 캐시, artifact store로 퍼지기 때문에 한 번 들어간 값을 회수하기 어렵다. 공개 클라이언트 번들에 들어가는 값은 secret이 될 수 없다. 브라우저에서 볼 수 있는 값은 “숨겨진 값”이 아니라 “공개 식별자”로 설계해야 한다.
secret 관리는 세 단계를 거쳐 진화했다. 각 단계의 한계를 보면 Secrets Manager, Parameter Store, Vault가 단순 “안전한 메모장”이 아니라는 점이 보인다.
초기에는 DB password나 API key를 config 파일이나 source code에 직접 적는 일이 많았다. 작은 팀, 단일 서버, 수동 배포에서는 빠르게 동작한다. 그러나 git과 CI/CD가 들어오면 이 방식은 바로 깨진다.
app.ts const dbPassword = "prod-password";
git commit -> local clone -> remote repository -> CI log -> fork / mirror / artifact문제는 삭제가 어렵다는 점이다. 현재 파일에서 문자열을 지워도 이전 commit에는 남는다. secret이 이미 외부로 나갔다면 history rewrite만으로는 충분하지 않고, 원 secret 자체를 revoke 또는 rotate해야 한다.
.env와 환경변수로 코드와 설정을 분리한다12-factor App의 config 분리 원칙은 큰 개선이었다. 코드는 repo에 두고, 환경별 값은 .env, deployment setting, container environment로 주입한다.
하지만 .env와 환경변수는 다음 문제를 남긴다.
Secrets Manager, Parameter Store, Vault 같은 중앙화 secret store는 다음 메커니즘을 추가한다.
| 이전 한계 | 중앙화 관리가 추가하는 메커니즘 |
|---|---|
| git history에 평문 저장 | 코드에는 secret 값 대신 ARN, path, logical name만 남긴다. |
| 읽기 감사 부재 | GetSecretValue 같은 조회 호출을 CloudTrail 또는 audit log에 남긴다. |
| 무제한 접근 | IAM policy, resource policy, KMS decrypt 권한으로 접근 주체를 좁힌다. |
| 회전 시 중단 위험 | version stage와 rotation workflow로 구버전·신버전 전환을 관리한다. |
| 정적 key의 긴 노출창 | STS, OIDC, Vault dynamic credential처럼 TTL이 있는 자격증명으로 피해 시간을 줄인다. |
| 환경별 secret 혼용 | /prod/service/name, /dev/service/name 같은 경로와 role을 분리해 blast radius를 좁힌다. |
이 lineage가 이 문서 전체의 중심이다. secret 관리는 보관 위치를 바꾸는 일이 아니라 secret 값, 접근 주체, 암호화 키, 네트워크 경로, rotation 상태를 분리해서 각각 좁히는 일이다.
Secret 조회는 아래 다섯 질문으로 분해하면 된다.
1. 어떤 secret인가? static인가 dynamic인가, prod인가 dev인가, 어떤 서비스 소유인가
2. 누가 읽는가? ECS Task Role, Lambda Role, GitHub Actions OIDC 세션, 사람 SSO 세션
3. 어떤 저장소에서 읽는가? Secrets Manager, Parameter Store, Vault, S3+KMS, 외부 SaaS
4. 어떤 key로 보호되는가? AWS managed key, customer managed KMS key, Vault Transit key
5. 어디서 실패할 수 있는가? IAM deny, KMS decrypt deny, VPC Endpoint 없음, rotation pending, cache staleAPI key, DB password, OAuth client secret처럼 사람이 만들고 오래 유지하는 값이다. 유출되면 교체 전까지 계속 유효하다.
외부 SaaS key, legacy DB 계정, 자동 TTL 발급을 지원하지 않는 연동요청 시점에 발급되고 TTL이 끝나면 만료되는 값이다. 유출되더라도 피해 시간이 TTL 안으로 제한된다.
AWS STS 세션, CI/CD OIDC, Vault DB credential, 단기 workload identity정적 secret은 없앨 수 있으면 줄이는 것이 좋지만, 현실에서는 외부 API key, legacy DB password, vendor credential 때문에 계속 남는다. 따라서 정적 secret은 “절대 유출되지 않게”가 아니라 다음 네 조건으로 관리한다.
동적 secret은 설계가 더 안전하지만 운영 복잡도가 있다. Vault Database Secrets Engine처럼 접속 때마다 DB 계정을 만들고 TTL 후 삭제하는 구조는 blast radius를 크게 줄인다. 대신 발급 시스템의 가용성, TTL 갱신, connection pool과의 상호작용을 같이 설계해야 한다.
같은 “DB password”라도 blast radius는 설계에 따라 달라진다.
| 설계 | 유출 시 영향 범위 | 개선 방향 |
|---|---|---|
root DB 계정 하나를 모든 서비스가 공유 | 모든 DB, 모든 테이블, 모든 환경 | 서비스별 DB user, 읽기/쓰기 권한 분리 |
| prod와 staging이 같은 password 사용 | staging 유출이 prod 사고로 번짐 | 환경별 secret path와 IAM role 분리 |
| CI, ECS, 사람 노트북이 같은 AWS Access Key | 어떤 경로에서 유출됐는지 추적 불가, 계정 전체 위험 | OIDC/Role 세션, workload role, 사람 SSO 분리 |
| Vault가 1시간 TTL DB credential 발급 | 해당 계정의 권한과 TTL 시간 안으로 제한 | connection pool TTL과 revoke 실패 감시 |
| Secrets Manager + alternating user rotation | 구버전 연결과 신버전 연결을 나눠 무중단 전환 가능 | AWSPREVIOUS 유효 기간과 앱 캐시 TTL을 맞춘다 |
좋은 secret 설계는 secret 값을 더 복잡하게 만드는 것이 아니라 하나의 secret이 해킹됐을 때 어디까지 갈 수 있는지 설명 가능하게 만드는 것이다.
KMS(Key Management Service)는 secret을 저장하는 서비스가 아니라 key를 보호하고 key 사용 권한을 평가하는 서비스다. Secrets Manager는 저장소이고, KMS는 그 저장소 안의 값을 암호화·복호화하는 key 계층이다.
Envelope Encryption은 다음 흐름으로 이해하면 된다.
flowchart TD
App["워크로드가 Secret 조회 요청"] --> SM["Secrets Manager"]
SM --> IAM{"secretsmanager:GetSecretValue 허용?"}
IAM -->|No| Deny["AccessDenied"]
IAM -->|Yes| KMS["KMS Decrypt 요청"]
KMS --> KMSIAM{"kms:Decrypt 허용?"}
KMSIAM -->|No| KMSDeny["KMS AccessDenied"]
KMSIAM -->|Yes| DataKey["암호화된 데이터 키 복호화"]
DataKey --> Plain["데이터 키로 Secret 값 복호화"]
Plain --> Return["평문 Secret 반환"] 저장 형태를 단순화하면 아래와 같다.
Secrets Manager 안의 저장물 encrypted_secret_value encrypted_data_key metadata / version stage
KMS가 보호하는 것 master key data key decrypt 권한왜 굳이 이렇게 나누는가? KMS가 모든 데이터를 직접 암호화하면 호출 지연과 비용이 커지고, 대용량 데이터 처리에도 맞지 않는다. 그래서 실제 데이터는 데이터 키로 암호화하고, 데이터 키만 KMS master key로 감싼다. 이 구조가 “envelope”이라는 이름의 이유다.
처음에는 KMS 내부 구현보다 이 경계를 기억하면 된다.
GetSecretValue 권한이 있어도 복호화가 실패한다.KMS key 선택은 처음에는 단순하게 본다. 기본 AWS managed key로도 작은 서비스의 secret 암호화는 시작할 수 있다. 고객 관리 KMS key(CMK)는 key policy를 직접 통제해야 하거나, key rotation·분리 감사·삭제 대기 기간 같은 컴플라이언스 요구가 있을 때 검토한다. CMK를 쓰면 보안 제어가 늘어나는 대신 key policy 오류로 secret 조회가 막히는 실패면도 함께 늘어난다.
IAM prerequisite에서 배운 것처럼 AWS 요청은 단일 Allow 하나로 끝나지 않는다. secret 조회도 여러 계층을 통과한다.
ECS Task 또는 Lambda -> IAM Role 세션 -> Role Permission Policy -> Permission Boundary / SCP / Session Policy -> Secret Resource Policy -> KMS Key Policy + kms:Decrypt -> VPC Endpoint 또는 NAT 경로작은 policy 예시를 보자.
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret" ], "Resource": "arn:aws:secretsmanager:ap-northeast-2:123456789012:secret:prod/myapp/*" }, { "Effect": "Allow", "Action": "kms:Decrypt", "Resource": "arn:aws:kms:ap-northeast-2:123456789012:key/key-id" } ]}이 예시는 넓은 secretsmanager:*가 아니라 읽기에 필요한 API만 허용한다. Resource도 전체 계정이 아니라 prod/myapp/*로 좁힌다. 단, 실제 운영에서는 secret 이름 규칙, CMK 사용 여부, resource policy, 조직 SCP까지 함께 본다.
반례가 중요하다.
| 착각 | 실제 결과 | 먼저 확인할 것 |
|---|---|---|
GetSecretValue만 있으면 충분하다 | customer managed KMS key를 쓰면 kms:Decrypt가 없어 실패할 수 있다. | KMS key policy, role policy |
| Execution Role에 앱 권한을 붙이면 된다 | 앱 코드가 SDK로 조회하는 경우 Task Role 권한이 필요하다. | ECS Task Role vs Execution Role |
| Role policy simulator가 Allow면 끝이다 | SCP, permission boundary, resource policy Deny가 Allow를 덮을 수 있다. | effective permission 계층 |
secret 이름이 prod/*면 격리된다 | 이름은 권한 경계가 아니다. policy가 Resource: "*"면 경로 분리가 무력해진다. | ARN resource scope, tag condition, account boundary |
ECS에서는 두 Role을 구분해야 한다.
| Role | secret과의 관계 | 대표 실패 신호 |
|---|---|---|
| Execution Role | ECS 플랫폼이 task 시작 시 secrets 필드의 값을 가져와 컨테이너에 주입할 때 쓴다. | task가 시작 전 unable to retrieve secret으로 실패한다. |
| Task Role | 컨테이너 안의 애플리케이션 코드가 SDK로 GetSecretValue를 호출할 때 쓴다. | 앱 로그에서 AccessDeniedException 또는 KMS deny가 발생한다. |
AWS Secrets Manager는 secret 값, 버전, rotation 상태, IAM 접근을 함께 관리하는 서비스다. 이 문서에서는 “콘솔에서 secret 만들기”보다 아래 네 가지를 이해하는 것이 더 중요하다.
Secrets Manager는 값을 버전으로 보관하고, 버전에는 stage label을 붙인다.
Version ID Stageabc123 AWSCURRENTdef456 AWSPREVIOUSghi789 AWSPENDING앱이 보통 읽는 것은 AWSCURRENT다. Rotation 중에는 새 값이 AWSPENDING으로 만들어지고, 테스트가 끝나면 AWSCURRENT로 승격된다. 직전 값은 AWSPREVIOUS로 남아 rollback이나 기존 connection 유예에 쓰인다.
중요한 경계는 “stage가 곧 값”이 아니라는 점이다. stage는 특정 version ID를 가리키는 라벨이다. rotation이 실패하면 값이 바뀌지 않았는데도 새 pending version이 남을 수 있고, 앱은 계속 기존 AWSCURRENT를 읽어 정상처럼 보일 수 있다.
새 값을 만들고 AWSPENDING 버전에 저장한다.
대상 DB나 외부 시스템에 새 값을 실제로 반영한다.
새 값으로 접속 또는 인증이 되는지 확인한다.
AWSPENDING을 AWSCURRENT로 승격하고 기존 값은 AWSPREVIOUS로 남긴다.
왜 Lambda가 등장하는가? rotation은 secret 저장소만 바꾸는 일이 아니다. DB password라면 실제 DB 계정 password도 바꿔야 하고, 외부 SaaS key라면 해당 SaaS API에서 새 key를 발급해야 한다. 이 대상 시스템별 작업을 Lambda가 수행한다.
DB password rotation에서 가장 헷갈리는 부분은 “값을 바꾸면 기존 연결은 어떻게 되나”다.
| 전략 | 동작 방식 | 장점 | 경계 |
|---|---|---|---|
| Single User | 기존 DB user의 password를 새 값으로 바꾼다. | 단순하다. | 기존 connection pool, 캐시된 password가 끊길 수 있다. |
| Alternating User | user_a와 user_b를 번갈아 AWSCURRENT로 올린다. | 무중단 전환에 유리하다. | DB 계정 2개와 권한 동기화가 필요하다. |
Alternating User 흐름은 다음처럼 읽는다.
현재 상태: user_a password = AWSCURRENT user_b password = 대기
rotation: 1. user_b password를 새로 만든다. 2. user_b로 접속 테스트한다. 3. user_b를 AWSCURRENT로 승격한다. 4. user_a는 AWSPREVIOUS로 남긴다.
다음 rotation: user_a를 새 값으로 바꾸고 다시 AWSCURRENT로 승격한다.이 패턴은 기존 connection이 잠깐 구 user로 남아 있어도 서비스가 바로 끊기지 않게 만든다. 대신 앱의 secret cache TTL과 DB connection pool 갱신 정책이 rotation 주기와 맞아야 한다.
AWSPENDING이 오래 남아 있으면 rotation이 “진행 중”처럼 보이지만 사실상 멈춘 상태일 수 있다.
정상: abc123 -> AWSCURRENT def456 -> AWSPREVIOUS
고착: abc123 -> AWSCURRENT def456 -> AWSPREVIOUS ghi789 -> AWSPENDING대표 원인은 다음이다.
setSecret 단계에서 DB 접속 실패testSecret 단계에서 새 password 인증 실패PutSecretValue, UpdateSecretVersionStage, kms:Decrypt 중 필요한 권한이 없음학습 포인트는 “pending label을 지우는 명령”보다 rotation은 저장소, 대상 시스템, 네트워크, IAM이 모두 맞아야 완료된다는 점이다. AWSCURRENT가 그대로라서 앱이 정상 동작하더라도 rotation이 멈췄다면 보안 상태는 점점 오래된 secret으로 회귀한다.
Secrets Manager는 managed service이지만 무한 저장소가 아니다. quota와 비용은 설계 판단을 바꾼다. 아래 수치는 AWS 공식 quota와 pricing page를 기준으로 학습 감각을 잡기 위한 값이며, 실제 설계 전에는 공식 문서를 다시 확인해야 한다.
| 항목 | 기준값 | 학습 포인트 |
|---|---|---|
| Secret value 크기 | 65,536 bytes | TLS chain, private key, 큰 JSON을 한 secret에 다 넣으면 실패한다. 큰 blob은 S3 + SSE-KMS로 분리한다. |
| Versions per secret | 100개 | 너무 자주 PutSecretValue를 호출하면 24시간 미만 버전이 쌓여 quota에 닿을 수 있다. |
GetSecretValue | 10,000 RPS | 대규모 부팅 storm에서 SDK 직접 호출이 throttle될 수 있다. |
BatchGetSecretValue | 100 RPS | batch는 호출 횟수를 줄이지만 quota가 훨씬 낮다. 항상 더 빠른 선택이 아니다. |
| Secrets per region | 500,000 | 큰 조직은 환경 x 서비스 x credential cross-product가 커질 수 있다. |
| 저장 비용 | secret당 월 $0.40 | secret 수가 많고 rotation이 필요 없으면 Parameter Store와 비교한다. |
| API 호출 비용 | 10,000 호출당 $0.05 | request path에서 매 요청 조회하면 비용과 latency가 늘어난다. client cache를 둔다. |
GetSecretValue vs BatchGetSecretValue여러 secret을 한 번에 읽고 싶으면 batch API가 매력적으로 보인다. 하지만 quota 때문에 사용처를 구분해야 한다.
| 상황 | 더 나은 선택 | 이유 |
|---|---|---|
| 단일 서비스가 부팅 시 5-20개 secret을 읽음 | BatchGetSecretValue 검토 | 호출 횟수와 bootstrap latency를 줄일 수 있다. |
| 수천 task가 동시에 올라오는 배포 | 분산 bootstrap + cache | batch quota가 낮아 batch 자체가 throttle 지점이 될 수 있다. |
| request마다 같은 secret을 읽음 | 앱 메모리 cache 또는 sidecar | secret은 request path가 아니라 refresh path에서 읽는 편이 안전하다. |
| secret 하나만 가끔 조회 | GetSecretValue | 단순하고 quota가 높다. |
작은 수치 예시를 보자.
상황: ECS task 2,000개가 동시에 시작 각 task가 secret 10개를 읽음
개별 호출: 20,000 GetSecretValue 요청 GetSecretValue quota 10,000 RPS라면 분산·retry 없이는 throttle 가능
batch 호출: 2,000 BatchGetSecretValue 요청 Batch quota 100 RPS라면 더 오래 throttle될 수 있음
결론: "batch가 항상 빠르다"가 아니라, 부팅 분산과 cache가 먼저다.자동 rotation이나 배포 스크립트가 PutSecretValue를 너무 자주 호출하면 version이 빠르게 쌓인다. Secrets Manager는 짧은 시간 안에 생긴 버전을 바로 정리하지 못하므로, 100개에 가까워지면 다음 rotation이 실패할 수 있다.
이 실패는 더 위험하다. 앱은 계속 AWSCURRENT를 읽기 때문에 장애가 없고, 보안팀은 rotation이 정상이라고 착각할 수 있다.
증상: 앱은 정상 RotationFailed 이벤트만 증가 secret version count가 100에 근접
의미: 새 version을 만들 수 없어 rotation이 멈춘 상태
먼저 볼 개념: rotation 주기, PutSecretValue 호출 빈도, version stage 정리Secrets Manager가 좋은 기본값이어도 모든 값을 넣는 곳은 아니다.
| 조건 | 위험 | 대안 또는 보완 |
|---|---|---|
| secret 하나를 초당 수천-수만 번 읽음 | API throttle, latency, 비용 증가 | client cache, sidecar cache, startup load 후 TTL refresh |
| 64KB를 넘는 인증서 bundle 또는 binary blob | value size 제한 | S3 + SSE-KMS에 blob 저장, Secrets Manager에는 S3 URI와 짧은 key만 |
| 분 단위 이하 rotation이 필요함 | version quota와 소비자 cache가 따라가기 어려움 | STS, OAuth token service, Vault dynamic credential |
| 공개 설정값까지 모두 secret으로 저장 | 비용과 권한 관리 복잡도 증가 | Parameter Store String, app config service |
| 멀티클라우드·온프레미스 중심 | AWS 종속성과 운영 경계 불일치 | Vault 또는 cloud-neutral secret platform |
Secrets Management는 AWS Secrets Manager 하나로 끝나지 않는다. 같은 문제를 비용, 기능, 운영 위치에 따라 다르게 푸는 도구들이 있다.
AWS Systems Manager Parameter Store는 계층형 path로 설정값을 저장하는 서비스다. 값 유형은 크게 세 가지다.
| 유형 | 암호화 | 대표 사용처 |
|---|---|---|
| String | 없음 | region, domain, feature flag처럼 공개 가능한 설정 |
| StringList | 없음 | 쉼표로 구분된 짧은 목록 |
| SecureString | KMS | rotation이 단순하거나 드문 secret |
Standard와 Advanced는 크기, 비용, 정책 기능에서 다르다.
| 항목 | Standard | Advanced |
|---|---|---|
| 비용 | 무료 티어 중심 | 파라미터당 월 비용 |
| 최대 크기 | 4KB | 8KB |
| 파라미터 정책 | 없음 | TTL, 알림 등 정책 가능 |
| 처리량 감각 | 낮은 TPS 설계에 적합 | 더 높은 TPS가 필요할 때 |
Parameter Store는 “설정 저장소”로 먼저 이해하는 편이 좋다. SecureString은 작은 secret을 저렴하게 저장하는 방법이지만, Secrets Manager처럼 rotation workflow와 secret version stage를 중심으로 설계된 서비스는 아니다.
계층형 path는 IAM scope와 함께 쓸 때 강해진다.
/prod/api/db-password/prod/api/jwt-secret/prod/cache/redis-url/staging/api/db-password/dev/api/db-password이름만 나눈다고 보안 경계가 생기는 것은 아니다. IAM policy가 /prod/*와 /dev/*를 실제로 다르게 허용해야 한다.
Vault는 AWS 밖에서도 쓸 수 있는 secret management platform이다. AWS만 쓰는 작은 시스템에서는 Secrets Manager가 더 단순할 수 있지만, 다음 조건에서는 Vault가 의미를 갖는다.
Vault의 대표 학습 포인트는 dynamic secret이다.
앱이 DB 접근 요청 -> Vault가 DB에 임시 user를 생성 -> TTL 1시간 credential 반환 -> TTL 만료 또는 revoke 시 DB user 삭제이 구조는 유출 피해 창을 줄인다. 하지만 Vault 자체가 중요한 인프라가 되므로 HA 구성, unseal, audit log, policy, lease renewal을 운영해야 한다. unseal과 lease renewal은 첫 회독에서 구현까지 알 필요는 없고, “Vault를 살려 두고 발급한 자격증명의 수명을 관리하는 운영 심화 용어”로 표시해 두면 충분하다. 즉 Vault는 “더 안전한 저장소”가 아니라 “더 강한 control plane”이다.
flowchart TD
Start["저장할 값이 생김"] --> Public{"노출되어도 권한이 생기지 않는 설정값인가?"}
Public -->|Yes| PSString["Parameter Store String 또는 app config"]
Public -->|No| Rotate{"자동 rotation 또는 version stage가 필요한가?"}
Rotate -->|Yes| SM["AWS Secrets Manager"]
Rotate -->|No| Multi{"멀티클라우드/온프레미스 통합이 필요한가?"}
Multi -->|Yes| Vault["Vault 검토"]
Multi -->|No| Dynamic{"TTL 기반 동적 발급이 필요한가?"}
Dynamic -->|Yes| VaultSTS["Vault dynamic secret 또는 STS/OIDC"]
Dynamic -->|No| Cost{"secret 수가 많고 비용이 민감한가?"}
Cost -->|Yes| PSSecure["Parameter Store SecureString"]
Cost -->|No| SM 실무 판단은 보통 이렇게 정리된다.
| 조건 | 우선 후보 | 이유 |
|---|---|---|
| DB password rotation 필요 | Secrets Manager | Lambda rotation, version stage, managed integration |
| 단순 API key 저장, rotation 드묾 | Parameter Store SecureString 또는 Secrets Manager | 비용과 audit 요구를 비교한다. |
| 공개 설정값 | Parameter Store String | secret으로 취급하면 비용과 권한 관리만 늘어난다. |
| GitHub Actions 배포 AWS credential | OIDC + AssumeRole | 저장 secret을 없애고 실행 시점 임시 세션을 쓴다. |
| 멀티클라우드 DB 동적 credential | Vault | provider 독립 policy와 lease를 제공한다. |
| 대용량 private key bundle 또는 binary data | S3 + SSE-KMS + secret reference | Secrets Manager value size 제한을 피한다. |
secret을 저장하는 것만큼 중요한 질문은 값이 애플리케이션에 어떻게 도착하는가다.
secrets 필드ECS에서는 Task Definition의 secrets 배열에 Secrets Manager ARN 또는 Parameter Store parameter ARN을 적을 수 있다. 값 자체가 아니라 참조를 적는다.
{ "containerDefinitions": [ { "name": "api", "image": "123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/api:latest", "secrets": [ { "name": "DB_PASSWORD", "valueFrom": "arn:aws:secretsmanager:ap-northeast-2:123456789012:secret:prod/myapp/db-password-AbCdEf" }, { "name": "JWT_SECRET", "valueFrom": "arn:aws:ssm:ap-northeast-2:123456789012:parameter/prod/api/jwt-secret" } ] } ]}이때 동작 주체는 ECS 플랫폼이다.
Task 시작 -> ECS agent / Fargate control plane -> Execution Role로 secret 조회 -> 컨테이너 환경변수에 값 주입 -> 앱은 process.env.DB_PASSWORD로 읽음장점은 애플리케이션 코드가 Secrets Manager SDK를 몰라도 된다는 점이다. 단점은 secret이 컨테이너 시작 시점에 환경변수로 고정된다는 점이다. rotation이 일어나도 이미 떠 있는 프로세스의 process.env가 자동으로 바뀌지는 않는다.
이 경로에서 ECS는 secret 참조를 먼저 해석한 뒤 컨테이너를 시작한다. ARN, JSON key, version stage, Execution Role 권한, 네트워크 경로 때문에 값을 가져오지 못하면 일반적으로 Task가 시작 또는 리소스 초기화 단계에서 중지된다. 플랫폼이 실패를 빈 문자열로 바꿔 환경변수에 넣거나 애플리케이션 기본값을 골라 주는 동작은 아니다.
앱이 SDK로 직접 조회하는 패턴도 있다.
import { GetSecretValueCommand, SecretsManagerClient,} from "@aws-sdk/client-secrets-manager";
const client = new SecretsManagerClient({ region: "ap-northeast-2" });
async function loadDbSecret() { const response = await client.send( new GetSecretValueCommand({ SecretId: "prod/myapp/db-password", }), );
if (!response.SecretString) { throw new Error("SecretString is empty"); }
return JSON.parse(response.SecretString);}이 패턴은 rotation 후 TTL 기반 refresh를 구현할 수 있다. 대신 앱 코드가 AWS SDK, retry, cache, IAM Task Role 권한, bootstrap 실패를 책임진다. SDK 조회가 실패하면 호출은 예외로 끝나며, fallback은 애플리케이션이 catch에서 유효 기간이 남은 last-known-good cache를 쓰는 식으로 명시적으로 설계해야 한다. 필수 credential을 빈 문자열이나 dev 기본값으로 대체하면 장애가 다른 시스템으로 번지므로, 허용한 fallback 조건이 아니면 부팅을 실패시키는 편이 안전하다.
| 기준 | ECS secrets 주입 | 앱 SDK 런타임 조회 |
|---|---|---|
| 코드 복잡도 | 낮다. 앱은 환경변수만 읽는다. | 높다. SDK, retry, cache, parsing이 필요하다. |
| 필요한 Role | Execution Role 중심 | Task Role 중심 |
| rotation 반영 | task 재시작 또는 재배포 필요 | cache TTL에 따라 반영 가능 |
| 장애 위치 | 앱 실행 전 Task 시작·리소스 초기화 단계에서 중지 | 앱 bootstrap 또는 request path의 SDK 예외 |
| fallback 주체 | ECS가 빈 값 fallback을 제공하지 않는다. | 앱이 last-known-good 사용 조건과 만료 시 실패를 명시한다. |
| 적합한 경우 | 단순 서버 앱, 배포 때 secret 갱신 허용 | 장시간 실행 프로세스, rotation 후 빠른 반영 필요 |
| 조심할 점 | process env dump, task 재시작 전략, stopped task 확인 | 매 요청 조회 금지, cache stale, throttle, IAM/KMS 실패, 조용한 기본값 |
secret cache는 필요하다. 하지만 rotation보다 오래 살아 있는 cache는 조용한 실패를 만든다.
rotation 주기: 30일앱 cache TTL: 1시간결과: 최대 1시간 안에 새 AWSCURRENT를 다시 읽는다.
rotation 주기: 30일앱 cache TTL: 프로세스 생존 기간결과: task가 재시작될 때까지 기존 password를 계속 쓴다. AWSPREVIOUS 유예가 끝나면 DB 인증 실패가 산발적으로 발생한다.cache TTL은 “짧을수록 안전”만으로 정하지 않는다. TTL이 너무 짧으면 API 호출 비용과 throttle 위험이 커진다. TTL이 너무 길면 rotation 반영이 늦다. 보통은 rotation 주기보다 충분히 짧고, 장애 시 Secrets Manager 일시 실패를 견딜 정도의 cache를 둔다.
아래처럼 AWS Access Key를 코드에 넣고 push했다고 가정하자.
T+0m commit pushT+1m public repo scanner가 key 문자열 발견 가능T+3m 공격자가 STS GetCallerIdentity 또는 S3 ListBuckets 시도 가능T+10m 개발자가 파일에서 key 삭제 후 새 commitT+11m 이전 commit, fork, CI log에는 key가 계속 남음여기서 중요한 판단은 “파일을 지웠는가”가 아니라 “key가 아직 유효한가”다.
잘못된 대응: git rm .env commit 끝
올바른 사고: 1. key를 즉시 비활성화하거나 교체한다. 2. CloudTrail에서 그 key가 사용된 흔적을 본다. 3. 같은 key가 다른 서비스에 재사용됐는지 찾는다. 4. history rewrite와 secret scanner를 재발 방지로 처리한다.history cleanup은 필요하지만 사고 차단의 첫 단계가 아니다. 유출된 static secret은 이미 복사됐다고 보고 revoke/rotate가 먼저다.
Private Subnet의 Fargate Task가 Secrets Manager secret을 주입받아야 한다고 하자.
Task Definition: secrets: DB_PASSWORD -> arn:aws:secretsmanager:...
네트워크: Private Subnet NAT Gateway 없음 Secrets Manager Interface Endpoint 없음
결과: task가 RUNNING 이전 단계에서 secret retrieval 실패이 실패는 애플리케이션 코드가 실행되기 전이다. 따라서 앱 로그에 아무것도 없을 수 있고, 애플리케이션의 process.env.DB_PASSWORD || fallback도 실행되지 않는다. 먼저 볼 곳은 ECS service event, stopped reason, VPC route, Secrets Manager endpoint다.
증상: ResourceInitializationError unable to retrieve secret no such host 또는 timeout
의미: Execution Role 권한 또는 네트워크 경로가 task 시작 전 막힘
먼저 확인: 1. executionRoleArn에 secret 조회 권한이 있는가 2. CMK 사용 시 kms:Decrypt가 있는가 3. Private Subnet에서 NAT 또는 com.amazonaws.<region>.secretsmanager endpoint가 있는가 4. endpoint security group이 task ENI에서 443을 허용하는가VPC prerequisite에서 배운 것처럼 Private Subnet은 이름만으로 AWS API에 접근할 수 없다. Secrets Manager는 AWS 서비스지만, Interface Endpoint나 NAT 경로가 있어야 한다.
상황을 단순화해 보자.
T+0 앱이 부팅하면서 DB password를 process.env에 저장T+30d Secrets Manager rotation 성공T+30d+ AWSCURRENT가 새 password로 바뀜T+30d+ 앱 프로세스는 여전히 이전 password를 메모리에 보관T+30d+ 기존 DB connection은 잠시 유지T+30d+ connection 재생성 시점부터 인증 실패 발생이 문제는 rotation 실패가 아니라 소비자 갱신 실패다. Secrets Manager는 새 값을 만들었지만, 애플리케이션이 그 값을 다시 읽지 않았다.
대응 방향은 세 가지다.
| 방향 | 효과 | 경계 |
|---|---|---|
| task 재배포 | 새 task가 새 환경변수로 시작한다. | 배포 타이밍과 연결 drain을 관리해야 한다. |
| SDK runtime refresh | TTL마다 새 AWSCURRENT를 읽는다. | cache, retry, IAM, throttle를 구현한다. |
| Alternating User rotation | 기존 user와 새 user를 번갈아 써 연결 끊김을 줄인다. | DB 계정 2개와 권한 동기화가 필요하다. |
Secrets Manager value limit은 65,536 bytes다. DB password 하나에는 충분하지만, 아래처럼 큰 bundle을 넣으면 실패할 수 있다.
secret JSON: private_key: 3KB certificate: 2KB intermediate_chain: 12KB 여러 vendor credential과 PEM bundle: 수십 KB 추가 metadata와 escape 문자
결과: 64KB를 넘으면 PutSecretValue가 거부된다.이 경우 secret 저장소를 크게 쓰기보다 데이터를 분리한다.
S3 object: s3://secure-bucket/certs/service-a.pem SSE-KMS 적용
Secrets Manager: cert_s3_uri short-lived decrypt/use credential학습 포인트는 “Secrets Manager는 작은 secret lifecycle 관리에 강하고, 대용량 암호화 객체 저장소가 아니다”라는 점이다.
긴 runbook을 외우기보다 증상, 의미, 먼저 볼 개념을 연결한다.
| 증상 | 의미 | 먼저 볼 개념 |
|---|---|---|
AccessDeniedException on GetSecretValue | role이 secret 조회를 허용받지 못했다. | IAM Role, Resource ARN, SCP/Boundary, resource policy |
AccessDeniedException on kms:Decrypt | secret 조회는 허용됐지만 KMS key 사용이 막혔다. | KMS key policy, kms:Decrypt, CMK ownership |
ECS unable to retrieve secret | task 시작 전에 secret 주입이 실패했다. | Execution Role, VPC Endpoint/NAT, endpoint SG |
| 앱은 정상 부팅했지만 DB 인증이 rotation 후 산발적으로 실패 | 앱이 이전 secret을 cache하고 있다. | cache TTL, AWSPREVIOUS 유예, connection pool |
AWSPENDING이 오래 남음 | rotation workflow가 중간 단계에서 멈췄다. | Rotation Lambda, set/test 단계, stage label |
RotationFailed만 있고 앱 장애는 없음 | 앱은 기존 AWSCURRENT를 쓰지만 rotation 보안 상태가 멈췄다. | CloudTrail/EventBridge alarm, version count |
ValidationException on PutSecretValue | value size, JSON 형식, version quota 같은 저장 조건을 넘었다. | 64KB limit, version count, update frequency |
secret scanner가 .env leak을 알림 | 저장 방식보다 원 credential의 유효성이 문제다. | revoke/rotate, CloudTrail, git history cleanup |
BatchGetSecretValue에서 throttle | batch 호출이 부팅 storm의 병목이 됐다. | API quota, startup fan-out, cache |
| prod 서비스가 staging credential을 사용 | path, environment, IAM scope가 서로 맞지 않는다. | naming convention, env separation, task definition |
| CloudTrail 호출자가 예상 Role이 아님 | secret policy보다 credential 공급 경로가 틀렸다. | Task Role vs Execution Role, profile, OIDC trust |
| SDK 응답 JSON에 예상 key가 없는데도 앱이 정상 부팅 | 앱 parser나 명시적 fallback이 빈 값·기본 credential을 허용했다. | response schema, config validation, fallback 만료 조건 |
이 표의 목표는 명령을 외우는 것이 아니다. secret 문제는 대개 네 계층 중 하나에서 막힌다.
ECS valueFrom에서 JSON secret의 특정 key만 꺼낼 때는 ARN 뒤에 key/stage/version을 붙이는 문법을 쓴다. 예를 들어 ...:secret:prod/db-AbCdEf:password::는 JSON secret에서 password key의 AWSCURRENT 값을 주입한다. 이 플랫폼 주입 경로에서 key 이름이나 참조를 틀리면 ECS가 값을 해석하지 못해 Task가 일반적으로 시작 단계에서 중지되며, 빈 값이 주입된 뒤 앱 fallback으로 넘어가는 것이 아니다. 반대로 앱이 SDK로 secret JSON 전체를 읽는 경로에서는 key 존재 여부와 fallback을 앱 코드가 판단하므로, schema validation과 fallback 만료 조건으로 조용한 기본값 사용을 막아야 한다.
Terraform으로 secret 값을 직접 관리할 때도 주의한다. aws_secretsmanager_secret 리소스 자체는 괜찮지만 aws_secretsmanager_secret_version.secret_string에 평문을 넣으면 Terraform state에 값이 남을 수 있다. IaC에는 secret 컨테이너와 권한을 두고, 실제 값은 안전한 주입 경로를 따로 설계하는 편이 낫다.
권한: IAM, KMS, boundary, resource policy
네트워크: NAT, Interface Endpoint, endpoint security group
버전: AWSCURRENT, AWSPREVIOUS, AWSPENDING, rotation workflow
소비자: env injection, SDK cache, connection pool, config validation좋은 secret 이름은 사람이 보기 좋은 label을 넘어서 IAM scope의 단위가 된다.
권장: /prod/payments/db/password /prod/payments/stripe/client-secret /staging/payments/db/password /dev/payments/db/password
피하기: /shared/db /prod/password /test-secret이름 설계의 기준은 세 가지다.
| 축 | 질문 | 예시 |
|---|---|---|
| 환경 | dev, staging, prod가 분리되는가? | /prod/..., /staging/... |
| 서비스 | 어떤 workload가 소유하고 읽는가? | /prod/payments/... |
| secret 종류 | DB, JWT, vendor API, certificate처럼 용도가 드러나는가? | /db/password, /stripe/key |
이름만으로는 충분하지 않다. IAM policy가 이 경로를 실제로 사용해야 한다.
{ "Effect": "Allow", "Action": "secretsmanager:GetSecretValue", "Resource": "arn:aws:secretsmanager:ap-northeast-2:123456789012:secret:prod/payments/*"}secret 접근 권한은 넓게 주기 쉽다. 하지만 prod/* 전체 접근은 한 서비스 침해가 prod 전체 침해로 커지는 경로다.
| 넓은 설계 | 좁힌 설계 | 실패 신호 |
|---|---|---|
모든 ECS Task Role에 prod/* 허용 | 서비스별 role에 /prod/service-name/*만 허용 | 한 task 침해로 다른 서비스 DB 접근 가능 |
| 모든 개발자에게 prod secret read 허용 | break-glass role, approval, CloudTrail 알림 | 사람 노트북 침해가 prod credential 유출로 연결 |
| GitHub repo secret에 AWS Access Key 저장 | OIDC trust policy로 특정 repo/ref만 assume 허용 | fork나 로그에 장기 key가 남음 |
| KMS key 하나를 모든 secret에 공유 | 민감도나 조직 경계에 따라 CMK 분리 | 한 key policy 실수가 여러 도메인에 영향 |
rotation은 자주 할수록 무조건 좋은 것이 아니다. 주기가 짧아질수록 소비자가 새 값을 따라가는 능력, version quota, 대상 시스템 rate limit이 중요해진다.
| secret 종류 | rotation 감각 | 주의할 점 |
|---|---|---|
| DB password | 30-90일 같은 정기 rotation을 많이 검토 | connection pool, alternating user, maintenance window |
| 외부 SaaS API key | provider가 rotation API를 지원하는지 확인 | 수동 콘솔 발급이면 자동화보다 사용처 문서화가 먼저일 수 있다. |
| AWS workload credential | 장기 key보다 STS/OIDC로 없애는 방향 | 저장된 secret이 아니라 실행 시점 세션으로 전환한다. |
| JWT signing key | key id, multiple active keys, token TTL 필요 | 단순 overwrite는 기존 token 검증 실패를 만들 수 있다. |
| TLS private key | 인증서 만료, 배포 위치, reload 방식 확인 | 큰 chain은 Secrets Manager size limit보다 배포 구조가 중요하다. |
rotation의 목표는 “날짜가 됐으니 바꾸기”가 아니라 유출 피해 창을 줄이는 것이다. 소비자가 새 값을 못 따라가면 rotation은 보안 개선이 아니라 장애 트리거가 된다.
git-secrets, trufflehog, GitHub secret scanning 같은 도구는 커밋 전후에 secret 패턴을 찾는다. 하지만 scanner가 있다고 secret을 커밋해도 된다는 뜻은 아니다.
scanner의 역할은 세 가지다.
한계도 있다.
따라서 scanner는 prevention이면서 detection이다. 사고 차단은 revoke/rotate와 권한 축소가 담당한다.
아래 절차는 첫 회독의 핵심이 아니다. 본문을 읽은 뒤 실제 작업에서 확인 순서를 잡을 때만 펼쳐본다.
목표는 “문자열을 이동”이 아니라 “유효한 이전 credential을 폐기하고, 새 접근 경로를 IAM으로 통제”하는 것이다.
발견
password, secret, AKIA, .env 패턴을 찾는다.분류
등록
kms:Decrypt 권한을 함께 설계한다.주입 경로 전환
secrets 필드 또는 앱 SDK runtime 조회 중 하나를 고른다.폐기
재발 방지
GetSecretValue와 rotation failure를 알린다.secret 유출 대응에서 가장 큰 실수는 “조용히 값만 바꾸고 끝내기”다. 교체는 필요하지만, 사용 흔적과 blast radius를 같이 봐야 한다.
T+0 유출 감지 - scanner alert, CloudTrail anomaly, 직접 발견
T+1m 차단 - AWS Access Key 비활성화 - DB password rotation - OAuth token revoke
T+5m 영향 범위 확인 - CloudTrail에서 해당 principal/key 사용 흔적 확인 - 접근된 S3, RDS, Secrets Manager, IAM API 식별 - 같은 secret 재사용처 확인
T+30m 재발 방지 - history와 artifact 재스캔 - scanner rule 추가 - IAM scope 축소 - rotation과 alarm 상태 확인CloudTrail snapshot을 먼저 볼지, 즉시 revoke할지는 상황에 따라 trade-off가 있다. 공격이 진행 중이면 차단이 우선이고, 차단 전후로 가능한 한 빨리 사용 흔적을 보존한다. 중요한 것은 “교체했으니 안전”으로 끝내지 않는 것이다.
NIST SP 800-61 같은 incident response framework는 이 흐름을 조직 프로세스 관점에서 더 깊게 다룬다. 첫 회독의 hard prerequisite는 아니고, 보안 운영 절차를 만들 때의 심화 자료로 보면 된다.
실제 운영에서는 콘솔, IaC, CloudTrail, SIEM을 함께 본다. 아래 명령은 개념을 확인하기 위한 최소 예시다.
# secret version stage 확인aws secretsmanager describe-secret \ --secret-id prod/myapp/db-password \ --query 'VersionIdsToStages'# secret version 수 감각 확인aws secretsmanager list-secret-version-ids \ --secret-id prod/myapp/db-password \ --query 'length(Versions)'# 특정 secret 조회 이벤트 확인aws cloudtrail lookup-events \ --lookup-attributes AttributeKey=EventName,AttributeValue=GetSecretValue# git history에서 .env 계열 파일 흔적 확인git log --all --full-history -- "**/.env*"# 실제 환경에서는 값 대신 필요한 변수의 존재 여부만 확인grep -z '^DB_PASSWORD=' /proc/1/environ >/dev/null 2>&1case $? in 0) printf 'DB_PASSWORD: present\n' ;; 1) printf 'DB_PASSWORD: missing\n' ;; *) printf 'DB_PASSWORD: unreadable\n' ;;esac
# 값 노출 성질은 격리된 로컬 셸에서 명백한 가짜 값으로만 확인DEMO_SECRET='fake-local-only' sh -c 'printf "%s\n" "$DEMO_SECRET"'운영 프로세스에서 env, printenv, /proc/<pid>/environ의 전체 내용을 출력하지 않는다. 확인 결과와 로그에는 변수명과 존재 여부만 남긴다.
이 명령들은 runbook의 전부가 아니다. 출력에서 봐야 하는 질문은 항상 같다.
| 비교 항목 | 구분 |
|---|---|
| Secret vs 설정값 | secret은 알면 권한이 생기는 값이고, 설정값은 공개되어도 권한이 생기지 않는 값이다. |
| Secrets Manager vs KMS | Secrets Manager는 secret 저장소, KMS는 암호화 key와 decrypt 권한 계층이다. |
| Secret value vs ARN | value는 실제 평문 secret이고, ARN은 secret을 가리키는 참조다. |
| IAM Allow vs 실제 허용 | IAM Allow가 있어도 KMS, SCP, boundary, resource policy가 막을 수 있다. |
| Task Role vs Execution Role | Task Role은 앱 코드의 AWS 호출, Execution Role은 ECS가 task 시작에 필요한 호출이다. |
AWSCURRENT vs version ID | AWSCURRENT는 현재 version을 가리키는 stage label이고, version ID는 실제 버전 식별자다. |
| Rotation vs 재배포 | rotation은 secret 값을 바꾸는 일이고, 재배포는 소비자가 새 값을 읽게 만드는 한 방법이다. |
| Parameter Store vs Secrets Manager | Parameter Store는 설정 저장소 성격이 강하고, Secrets Manager는 rotation과 secret lifecycle에 강하다. |
| Static secret vs Dynamic secret | static은 오래 사는 값이고, dynamic은 요청 시점에 짧게 발급되는 값이다. |
| Scanner vs 대응 | scanner는 발견을 돕지만, 유출된 credential을 무효화하지 않는다. |
.env, password, secret, AKIA 같은 문자열이 현재 파일과 history에 남아 있는지 확인한다.environment와 secrets가 어떻게 구분되어 있는지 본다.executionRoleArn과 taskRoleArn이 각각 무엇을 허용하는지 IAM policy를 비교한다.AWSCURRENT, AWSPREVIOUS, AWSPENDING stage가 어떻게 붙어 있는지 확인한다.GetSecretValue 이벤트를 찾아 호출 principal이 예상한 Role인지 확인한다.secrets 필드 기준.GetSecretValue 런타임 호출 예제와 필요 권한.AWSPENDING 고착과 rotation 실패 진단.