5.1 KiB
5.1 KiB
id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
| id | title | category | status | source_trust_level | verification_status | created_at | updated_at | tags | tech_stack | applied_in | aliases | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| devops-secrets-rotation-automation | Secrets Rotation — 자동 회전 / 무중단 | Coding | draft | B | conceptual | 2026-05-09 | 2026-05-09 |
|
|
|
Secrets Rotation
Secret 영원히 같으면 leak 시 영원히 노출. 자동 회전 + 무중단 (dual-secret window). AWS Secrets Manager / HashiCorp Vault / Kubernetes External Secrets.
📖 핵심 개념
- Static secret: 수동 회전 — 잊혀짐.
- Dynamic secret: 매 요청마다 발급 (Vault dynamic creds).
- Dual-secret window: 새 secret 활성, 기존도 N분 유효.
- Lease: 짧은 시간만 유효, 갱신 필요.
💻 코드 패턴
AWS Secrets Manager rotation
// Lambda rotation function (4-step)
export const handler = async (event: RotationEvent) => {
const { Step, SecretId, ClientRequestToken } = event;
switch (Step) {
case 'createSecret': return createSecret(SecretId, ClientRequestToken);
case 'setSecret': return setSecret(SecretId, ClientRequestToken); // DB 에 새 password 적용
case 'testSecret': return testSecret(SecretId, ClientRequestToken); // 새 password 로 연결 확인
case 'finishSecret': return finishSecret(SecretId, ClientRequestToken); // AWSCURRENT 로 promote
}
};
# Terraform
resource "aws_secretsmanager_secret_rotation" "db" {
secret_id = aws_secretsmanager_secret.db.id
rotation_lambda_arn = aws_lambda_function.rotate.arn
rotation_rules { automatically_after_days = 30 }
}
App 측 — refresh 주기
class SecretCache {
private cached: { value: string; fetchedAt: number } | null = null;
private ttlMs = 60_000;
async get(): Promise<string> {
if (!this.cached || Date.now() - this.cached.fetchedAt > this.ttlMs) {
const v = await fetchFromSecretsManager(this.secretId);
this.cached = { value: v, fetchedAt: Date.now() };
}
return this.cached.value;
}
}
Dual-credential window
-- DB 에 user app_v1, app_v2 둘 다 존재
-- v1 active 동안 v2 만들고 → v2 로 새 deploy → v1 비활성
CREATE USER app_v2 WITH PASSWORD 'new';
GRANT ALL ON DATABASE app TO app_v2;
-- 새 pod 들 v2 사용 시작
-- 1시간 후
DROP USER app_v1;
Vault dynamic creds
// app 이 매번 짧은 lease 의 creds 받음
const r = await vault.read('database/creds/readonly');
const { username, password, lease_id, lease_duration } = r.data;
setTimeout(() => vault.renew(lease_id), lease_duration * 0.7 * 1000);
Kubernetes External Secrets
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata: { name: db-secret }
spec:
refreshInterval: 1h
secretStoreRef: { name: aws-secrets, kind: ClusterSecretStore }
target: { name: db, creationPolicy: Owner }
data:
- secretKey: url
remoteRef: { key: app/db, property: url }
ESO 가 Secret 자동 sync — 회전되면 1h 안에 pod 에 반영. Pod restart 또는 reloader 로 새 값 로드.
App restart on secret change
# stakater/reloader 추가
metadata:
annotations:
reloader.stakater.com/auto: "true"
KMS key rotation
resource "aws_kms_key" "main" {
description = "App data"
deletion_window_in_days = 30
enable_key_rotation = true # 매년 자동 회전
}
API key 회전 패턴
// 사용자 API key — 새거 발급 시 24h 둘 다 활성
async function rotateApiKey(userId: string): Promise<string> {
const old = await db.apiKeys.find(userId);
const newKey = generate();
await db.apiKeys.insert({ userId, key: newKey, status: 'active' });
await db.apiKeys.update(old.id, { status: 'sunset', expiresAt: now() + 24 * H });
return newKey;
}
🤔 의사결정 기준
| 종류 | 솔루션 |
|---|---|
| Cloud (AWS) | Secrets Manager + 자동 rotation lambda |
| Cloud (GCP) | Secret Manager + Cloud Functions |
| K8s | External Secrets Operator |
| Self-hosted | HashiCorp Vault |
| Static creds 만 | Doppler / 1Password Connect |
| Dynamic / short-lived | Vault dynamic secrets |
| Key encryption | KMS / Cloud KMS / Vault Transit |
❌ 안티패턴
- Rotation 수동: 잊혀짐. 자동.
- 새 secret 즉시 강제 — old 비활성: 아직 transition 중인 pod 다운.
- Secret env var 만 — restart 필요: ESO + Reloader.
- Repo 에 commit (
.env.prod): leak. .gitignore + secret scan. - Logging 시 secret 출력: 마스킹.
- 단일 user 모든 service 공유: 한 leak = 전체.
- 회전 불가능한 client (mobile app): refresh token 쓰고 짧은 access.
- Terraform state 안 secret 평문: state encrypt + 권한 제한.
🤖 LLM 활용 힌트
- Secrets Manager / Vault + 자동 rotation lambda.
- App = 짧은 cache + 회전 가능 구조.
- ESO + Reloader = K8s 표준.