콘텐츠로 이동

시크릿 관리

이 페이지는 시크릿이 어디에 있고, 어떻게 회전하는가를 정리한다. 실제 시크릿 값(password, API key)은 본 페이지에 적지 않으며 — 위치 포인터만 제공한다. 값을 확인하려면 해당 yml 파일 또는 외부 dashboard를 직접 열어볼 것.

⚠️ plaintext 커밋 경고: application-prod.yml / application-local.yml의 여러 시크릿이 현재 평문 커밋되어 있다. Git history에 남아 있어 회전이 필수이지만, user가 이미 enumerated 항목들은 accept한 상태(feedback_accepted_repo_secrets.md 참조). 신규 시크릿 추가 시에는 절대 평문 커밋 금지.

1. 시크릿 카테고리 한눈에

flowchart TB
    subgraph CODE["코드 레포 (커밋)"]
        YML_P["application-prod.yml
(평문 — 회전 필요)"] YML_L["application-local.yml
(dev/local 공용)"] YML_O["application-oauth.yml
(naver/kakao/google)"] end subgraph EXT["외부 dashboard / vault"] TOSS["Toss Payments
console"] OAI["OpenAI
dashboard"] LF["Langfuse
cloud.langfuse.com"] NCP["Naver Cloud
console"] TOSS_PROD["Toss live keys
(미수령)"] end subgraph LIGHTSAIL["Lightsail box (런타임 only)"] ENV_BE["/etc/semugpt/backend.env
(prod, mode 0600)"] ENV_FE["/etc/semugpt/frontend.env
(prod, mode 0600)"] SLACK["/etc/semugpt/slack-webhook.url
(mode 0600)"] SSH_KEY["~/.ssh/aws-semugpt
(개발자 Mac)"] end YML_P -.회전.-> ENV_BE TOSS_PROD -.set in.-> ENV_BE OAI -.regen-> ENV_BE LF -.regen-> ENV_BE

2. 시크릿 카테고리별 정리

2.1 JWT 서명 키

항목
위치 application-prod.yml:52 (jwt.secret), application-local.yml:50
환경변수 override JWT_SECRET (Spring @Value binding)
현재 상태 local과 prod 동일 값 평문 커밋 — 회전 필요
회전 절차 새 secret 생성 → /etc/semugpt/backend.envJWT_SECRET 갱신 → sudo systemctl restart semugpt-backend. 모든 기존 세션이 로그아웃됨 (refresh token도 무효)

⚠️ Hard cutover 후 회전 권장. 현재 legacy backend도 동일 secret을 쓰는 상태라 두 곳을 함께 갱신해야 함.

2.2 Toss Payments

환경 위치
Local (test keys) application-local.yml:78-83 tosspayments.client-key / secret-key / billing.client-key / billing.secret-key. test_gck_... / test_gsk_... / test_ck_... / test_sk_... 4종
Production (live keys) application-prod.yml:61-66 ${TOSS_CLIENT_KEY} / ${TOSS_SECRET_KEY} / ${TOSS_BILLING_CLIENT_KEY} / ${TOSS_BILLING_SECRET_KEY}환경변수만 정의, live 값은 미주입
Production live keys 자체 ⚠️ 미수령 클라이언트(세림세무법인 김창진/김진우)에게 제공 요청 중

회전: 1. Toss Payments console에서 새 키 발급 2. /etc/semugpt/backend.env(backend secret) + /etc/semugpt/frontend.env(client key — NEXT_PUBLIC_TOSS_CLIENT_KEY)에 반영 3. sudo systemctl restart semugpt-backend semugpt-frontend 4. Toss console에서 webhook URL을 https://api-new.semugpt.co.kr/...로 등록 (현재 pending — Phase 11 작업)

2.3 OpenAI

항목
위치 application-prod.yml:47-48 (openai.token), application-local.yml:45-46
환경변수 override OPENAI_TOKEN
현재 상태 local과 prod 동일 평문 키 커밋 — 회전 필요
사용처 RAG(임베딩) + 답변 생성 + HyDE + intent router 등 7개 LLM call site
회전 OpenAI dashboard에서 새 token 발급 → backend env 갱신 → restart

⚠️ Token이 유출된 채로 history에 남아 있으므로 hard cutover 시 반드시 회전. 회전 시 데이터 파이프라인 (packages/data-pipeline)도 함께 갱신해야 함 (CLI 실행 시 사용).

2.4 Langfuse

환경 위치
Local application-local.yml:53-57 pk-lf-15fc00c9-... / sk-lf-087b4f9f-... 평문 커밋
Production application-prod.yml:70-77 ${LANGFUSE_PUBLIC_KEY} / ${LANGFUSE_SECRET_KEY} — env vars만 정의

Langfuse는 7개 LLM 프롬프트의 model/temperature/maxTokens config를 관리한다 (rag-final-answer, hyde-generator, intent-router, query-rewrite, keyword-extraction 등 — CLAUDE.md "Langfuse 프롬프트 모델 설정" 참조). langfuse.enabled=false이거나 키 미주입 시 코드 fallback(하드코딩 prompt + default model)으로 silently 동작.

회전: Langfuse cloud → Settings → API Keys → regenerate → /etc/semugpt/backend.env 갱신 → restart.

2.5 Naver Cloud SENS (SMS)

항목
위치 application-prod.yml:38-44, application-local.yml:36-42 (local/prod 동일 값)
키 이름 ncp.access-key, ncp.secret-key
Service ID ncp.sens.sms.service-id = ncp:sms:kr:315481964654:semu-gpt
Calling number 028302220
구현체 SensMessageSender (@Component 활성). SnsMessageSender//@Component 주석으로 비활성
회전 NCP console → IAM → access key regenerate → backend env 갱신 → restart

⚠️ AWS SNS 아님 주의. 과거 위키에서 SMS 구현체를 AWS SNS로 잘못 인용한 사례가 있어 (CLAUDE.md Prior incidents 참조), application.ymlaws.sns.enabled=true는 별개 설정.

2.6 AWS credentials

사용 패턴 자격증명 비고
개발자 CLI aws --profile ob ... (SSO) semugpt 인프라 작업의 표준 진입점. 토큰 만료 1-12h → aws sso login --profile ob
Backend 런타임 (IAM access key, legacy) application-{local,prod}.ymlspring.cloud.aws.credentials.access-key / secret-key (AKIAQLD6ZXDVRTY2FHER / +kCL8z...) 평문 커밋 — 회전 필요. SDK가 직접 호출(SNS 등). 신규 plan에서 미사용이지만 deactivate 전에 다른 consumer 점검 필요
Frontend 배포 한정 IAM aws --profile semugpt-frontend S3 + CloudFront 권한만. 같은 계정(023888247019)의 별도 IAM 세션
023888247019_AdministratorAccess SSO 수동 세션 토큰 ob fallback 용
Bootalk prod aws --profile bootalk-prod ⚠️ 다른 회사 계정 (296797354236), semugpt와 무관. 혼동 주의

회전 (IAM key): 1. aws --profile ob iam create-access-key --user-name <user> 2. application-prod.yml 평문 라인을 env var ref로 바꾸고 /etc/semugpt/backend.env에 새 키 주입 3. 기존 key AKIAQLD6ZXDVRTY2FHER deactivate → 72시간 모니터링 후 delete 4. Bootalk 다른 프로젝트(예: ../semugpt-backend의 SNS) 영향 확인

2.7 RDS MySQL

항목
엔드포인트 tax-gpt.cl2zydns6yrm.ap-northeast-2.rds.amazonaws.com:3306
사용자 admin
비밀번호 selim3400!! (legacy app yml + 새 application-prod.yml:14 평문 커밋)
접근 ⚠️ PubliclyAccessible=true + SG sg-09b20a06663fcfa010.0.0.0/0:3306 개방
회전 aws --profile ob rds modify-db-instance --db-instance-identifier tax-gpt --master-user-password <new>

⚠️ 회전 시점에 legacy backend와 new backend의 env를 동시에 갱신해야 함. 그렇지 않으면 한쪽이 connection 실패. Hard cutover 후 회전 권장.

⚠️ RDS SG는 hard cutover 후 잠글 예정 (0.0.0.0/0 제거 + 사무실 IP + Lightsail VPC + default VPC만 허용). 현재는 보안 우려 상태.

Local MySQL은 별개 — docker-compose.dev.yml의 root/selim3400이며 dev box / 로컬 모두 동일.

2.8 Elasticsearch

환경 위치
Local application-local.yml:29-33 localhost:9200, elastic / uiti0701!
Production application-prod.yml:31-35 3.39.210.101:9200, elastic / uiti0701! (현재 동일 password)

uiti0701!! 문자가 shell parsing을 깨므로 개발 환경 페이지의 "ES password 주의" 섹션 참조 (printf + base64 패턴 또는 %21 URL-encode).

2.9 Lightsail SSH

항목
키 파일 (개발자 Mac) ~/.ssh/aws-semugpt
SSH alias ssh semugpt-aws (dev) / ssh semugpt-prod (prod, Phase 3 후)
Lightsail key pair name semugpt-aws (dev/prod 공용 — --key-pair-name semugpt-aws로 prod도 같은 키 사용)
허용 SSH IP (dev) 49.175.194.2/32 (사무실 only)
허용 SSH IP (prod, 예정) 동일 (49.175.194.2/32)

⚠️ SSH key 자체는 git에 커밋되지 않음. 인수 시 별도 안전 채널로 전달 필요 — 접근 권한 목록 참조.

2.10 Slack incoming webhook (Lightsail)

항목
위치 /etc/semugpt/slack-webhook.url on Lightsail dev (and prod, 예정)
권한 mode 0600, root-only
Git 커밋 ❌ 절대 안 됨 — Lightsail box에만 존재
Mention prefix /etc/semugpt/slack-mention.txt (현재 <!here> @주우철)
사용처 /usr/local/bin/disk-alarm.sh + /usr/local/bin/health-monitor.sh
회전 Slack workspace → Apps → Incoming Webhooks → revoke + regenerate → echo "..." | sudo tee /etc/semugpt/slack-webhook.url

2.11 OAuth (Naver / Kakao / Google)

항목 위치
Naver application-oauth.yml:9-10 (client-secret: FCEfIxQ4m1, client-id: AAIEDPGvImaOXuCCxGRx)
Kakao application-oauth.yml:16-17 (client-id: 5a146993d53d52171a002a1d87bf40a2)
Google application-oauth.yml:22-24 (client-secret: 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, client-id: 708060257306-...)

⚠️ 현재 미사용 — auth 코드에 OAuth2 도메인은 있으나 활성 라우트가 없다. (auth 페이지 "관련 문서"의 "알려진 이슈" 참조) 회전 시 각 provider console에서 secret regenerate. 활성화 전 어차피 redirect URI를 prod URL로 갱신해야 하므로 hard cutover 시점에 함께.

2.12 Cloudflare Tunnel (dev only)

항목
Tunnel ID 078d8083-eba8-4df2-8026-ebafcb53666b (semugpt-backend)
Cert 위치 (Lightsail) /etc/cloudflared/cert.pem
Config /etc/cloudflared/config.yml (ingress 정의)
회전 Cloudflare dashboard → Zero Trust → Tunnels → 새 tunnel 생성 + cert 교체 + systemctl reload cloudflared

⚠️ prod에는 cloudflared tunnel 사용 불가 (Route 53이라 zone-on-Cloudflare 요건 미충족). prod는 ALB로 직접 받음.

3. 회전 우선순위

Hard cutover 시점에 반드시 회전해야 할 시크릿 (Git history에 평문 노출됨):

Priority 시크릿 영향
P0 RDS master password 전 사용자 데이터
P0 OpenAI token 토큰 도용 시 비용 발생 + RAG 중단
P0 JWT secret 도용 시 임의 사용자 위장
P1 NCP SENS keys 도용 시 임의 SMS 발송
P1 AWS IAM access key AKIAQLD6ZXDVRTY2FHER 도용 시 AWS 리소스 접근 (semugpt-backend SNS 등)
P1 Langfuse keys 도용 시 prompt 변조 가능
P2 ES password 외부 노출은 없지만 dev/prod 동일
P2 OAuth secrets 활성 라우트 없으므로 즉시 회전 의무 약함

4. 환경변수 override 메커니즘

application-prod.yml의 평문 값은 Spring @Value binding 덕에 환경변수로 override 가능하다 (verified, spec Section 10). 즉:

# /etc/semugpt/backend.env
SPRING_DATASOURCE_PASSWORD=<new password>
OPENAI_TOKEN=<new token>
JWT_SECRET=<new secret>
LANGFUSE_PUBLIC_KEY=<...>
LANGFUSE_SECRET_KEY=<...>
TOSS_CLIENT_KEY=<live>
TOSS_SECRET_KEY=<live>
TOSS_BILLING_CLIENT_KEY=<live>
TOSS_BILLING_SECRET_KEY=<live>
CORS_ALLOWED_ORIGINS=https://semugpt.co.kr,https://www.semugpt.co.kr,https://new.semugpt.co.kr

systemd unit이 EnvironmentFile=/etc/semugpt/backend.env 지시문으로 로드 → JVM 시작 시 주입 → Spring이 yml hardcoded 값을 override.

⚠️ Frontend는 빌드 타임에 NEXT_PUBLIC_* env가 번들에 박힌다 — .env.local 변경 시 빌드 재실행 필요.

5. 시크릿 추가 시 안전 패턴

새 시크릿(예: 새 외부 SaaS) 추가 시:

  1. application-prod.yml에는 env var ref만 쓴다: myservice.api-key: "${MYSERVICE_API_KEY}"
  2. application-local.yml에는 dev/test 키 또는 placeholder. 절대 prod 값을 commit 하지 말 것
  3. /etc/semugpt/backend.env에 실제 값 주입 (Lightsail prod) — 권한 0600, root 소유
  4. 신규 시크릿은 접근 권한 목록에 등록 — 인수 시점에 누락 방지

알려진 이슈 / 개선 예정

  • Git history에 평문 시크릿 다수 — 회전 외에는 해결 불가 (히스토리 rewrite는 협업 흐름 깨짐). user-accepted (feedback_accepted_repo_secrets.md)
  • secret manager 부재 — AWS Secrets Manager / SSM Parameter Store 미사용. 작은 팀에 과한 인프라라 도입 보류
  • application-prod.yml의 IAM access key 평문 — 회전 + env var ref 전환 필요 (TODO)
  • Toss live keys 미수령 — 클라이언트(김창진/김진우) 측 manual 작업
  • application-oauth.yml의 OAuth secret 평문 — 활성 라우트 없어 즉시 위험은 낮음. 활성화 시 회전 필수
  • Backend Sentry DSN 부재 — 시크릿이라기보다 모니터링 갭. 모니터링 페이지 참조
  • 인증서 만료 모니터링 부재 — ACM cert는 자동 갱신이지만 만료(2026-08-13) 임박 시 알림 부재. CloudWatch alarm 또는 외부 cron 도입 검토 필요

관련 문서

  • 개발 환경 — dev secret 위치 (Lightsail box + cloudflared cert)
  • 운영 배포 — pre-cutover 시 secret rotation 시점 (Phase 11)
  • 접근 권한 목록 — 인수 시 권한·키 인계 체크리스트
  • 1차 출처: 레포 루트 CLAUDE.md의 "Production Infrastructure" 섹션 + Spec Section 11a (secret rotation)