Files
2nd/10_Wiki/Topic_Programming/Coding/DevOps_Secrets_Rotation_Automation.md
T
Antigravity Agent 9148c358d0 docs(10_Wiki): 위키 전체 재구성 — Topic_* 폴더를 4개 카테고리로 통합 + 대규모 중복 제거
Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를
Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류.

- 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로
  자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거,
  동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거.
- 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming,
  Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business,
  Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로,
  나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는
  title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백).
  원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지.
- 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서.
- 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는
  지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지.
- Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경.
- 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
2026-07-05 00:33:48 +09:00

5.0 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
devops
secrets
rotation
security
vibe-coding
language applicable_to
TS / AWS / Vault
DevOps
secret rotation
AWS Secrets Manager
HashiCorp Vault
KMS
key rotation

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 표준.

🔗 관련 문서