--- id: wiki-2026-0508-secret-management title: Secret Management category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Secrets Management, Credential Management, Vault] duplicate_of: none source_trust_level: A confidence_score: 0.95 verification_status: applied tags: [security, devsecops, credentials, kms] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: multi framework: vault-aws-kms --- # Secret Management ## 매 한 줄 > **"매 secret 은 매 git 에 절대 — 매 vault 에"**. Secret management 는 매 API key, DB password, certificate, signing key 의 매 lifecycle (issue, store, rotate, revoke, audit) 의 매 centralized control. 2026 현재 매 HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, Doppler, Infisical 가 매 dominant; 매 SPIFFE/SPIRE workload identity, 매 short-lived (15min) tokens 가 매 long-lived API key 를 매 replace. ## 매 핵심 ### 매 Anti-secrets - Hardcoded in source. - Plain in `.env` committed. - Shared via Slack DM. - Long-lived (years) static API keys. ### 매 Pillars - **Encryption at rest**: KMS-backed. - **Encryption in transit**: TLS-only. - **Access control**: RBAC + audit log. - **Rotation**: automated (DB pwd, KMS key). - **Workload identity**: 매 service ≠ user — 매 ephemeral token 의 매 cloud IAM. - **Detection**: 매 git pre-commit (gitleaks, trufflehog) + 매 GitHub secret scanning. ### 매 응용 1. App → DB: dynamic creds. 2. CI → cloud: OIDC federation, no static keys. 3. K8s pod → AWS: IRSA / Workload Identity. 4. Cross-service: SPIFFE SVID. ## 💻 패턴 ### Vault dynamic DB cred ```bash vault write database/roles/app-readonly \ db_name=postgres-prod \ creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \ default_ttl=1h max_ttl=24h # App requests cred vault read database/creds/app-readonly # username: v-token-app-readonly-x9a..., password: A1b2C3..., lease_id: ..., lease_duration: 3600 ``` ### GitHub Actions OIDC → AWS (no static keys) ```yaml permissions: id-token: write contents: read jobs: deploy: runs-on: ubuntu-latest steps: - uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::123:role/github-deploy aws-region: us-east-1 - run: aws s3 sync ./build s3://prod-bucket/ ``` ### Pre-commit secret scan ```yaml # .pre-commit-config.yaml repos: - repo: https://github.com/gitleaks/gitleaks rev: v8.18.0 hooks: - id: gitleaks ``` ### App-side fetch with caching ```typescript import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager"; const sm = new SecretsManagerClient({}); const cache = new Map(); async function getSecret(name: string): Promise { const cached = cache.get(name); if (cached && cached.expires > Date.now()) return cached.value; const res = await sm.send(new GetSecretValueCommand({ SecretId: name })); const value = JSON.parse(res.SecretString!); cache.set(name, { value, expires: Date.now() + 5 * 60_000 }); return value; } ``` ### K8s External Secrets Operator ```yaml apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: { name: db-creds } spec: refreshInterval: 1h secretStoreRef: { name: vault-backend, kind: ClusterSecretStore } target: { name: db-creds } data: - secretKey: password remoteRef: { key: database/creds/app, property: password } ``` ### Rotation Lambda ```typescript export async function rotateApiKey(event) { const step = event.Step; if (step === "createSecret") { const newKey = await crypto.randomUUID(); await sm.putSecretValue({ SecretId: event.SecretId, ClientRequestToken: event.ClientRequestToken, SecretString: newKey, VersionStages: ["AWSPENDING"] }); } else if (step === "setSecret") { /* configure target */ } else if (step === "testSecret") { /* test */ } else if (step === "finishSecret") { await sm.updateSecretVersionStage({ SecretId: event.SecretId, VersionStage: "AWSCURRENT", MoveToVersionId: event.ClientRequestToken }); } } ``` ## 매 결정 기준 | 상황 | Tool | |---|---| | 매 multi-cloud, 매 self-host | HashiCorp Vault | | 매 AWS-only | Secrets Manager + Parameter Store | | 매 dev-friendly UX | Doppler / Infisical | | 매 K8s | External Secrets Operator + cloud KMS | | 매 workload-to-workload | SPIFFE/SPIRE | **기본값**: Cloud-native (Secrets Manager) + OIDC for CI + ESO for K8s. ## 🔗 Graph - 부모: [[DevSecOps_Framework]] · [[Application Security]] - 변형: [[KMS]] · [[PKI]] - 응용: [[CI_CD_Pipeline]] · [[보안_및_시스템_신뢰성_표준|Zero-Trust Architecture]] - Adjacent: [[보안_및_시스템_신뢰성_표준|OWASP Top 10]] · [[OAuth 2.0]] ## 🤖 LLM 활용 **언제**: Secret-scanner triage (매 actual secret vs 매 test fixture?), rotation runbook generation, IAM policy synthesis from natural-language requirement. **언제 X**: 매 secret 자체를 매 LLM context 에 매 넣지 마. 매 leak risk. ## ❌ 안티패턴 - **`.env` in git**: 매 even private repo — 매 contributor leak. - **Long-lived keys**: 매 5-year IAM access key — 매 incident blast-radius huge. - **Shared service account**: 매 audit trail 의 매 useless. - **Plain ENV var visible to all containers**: 매 sidecar / multi-tenant — 매 leak. ## 🧪 검증 / 중복 - Verified (NIST SP 800-57, OWASP ASVS V6, CIS Benchmarks). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — OIDC federation + workload identity 2026 |