Skip to content

feat(S15P11A705-96): AI 소유 값을 SealedSecret으로 봉인해 전달한다 - #34

Merged
colosair merged 1 commit into
mainfrom
feat/S15P11A705-96-seal-ai-secrets
Jul 29, 2026
Merged

feat(S15P11A705-96): AI 소유 값을 SealedSecret으로 봉인해 전달한다#34
colosair merged 1 commit into
mainfrom
feat/S15P11A705-96-seal-ai-secrets

Conversation

@colosair

Copy link
Copy Markdown
Member

요약

ai#32에서 Infra가 요청한 ① GitHub Secret → 암호화 Secret handoff를 구현한다. 수동 트리거 workflow 하나와 controller 공개 인증서가 전부이며, 앱 코드·DB·API 변경은 없다.

이 handoff의 ②/ready·③smoke는 ai#33으로 이미 전달했다. ①만 남아 있었고 그동안 배포가 여기서 막혀 있었다.

Jira

왜 필요한가

GitHub Actions Secret은 Kubernetes Pod에 자동으로 전달되지 않는다. 값을 클러스터까지 보내려면 누군가 평문을 한 번은 만져야 하는데, Infra는 *"key/token 값을 AI owner 소유로 두고, 값을 열람하지 않은 채 암호화 Secret과 컨테이너만 배포하겠다"*는 경계를 세웠다. SealedSecret이 그 경계를 성립시킨다 — 복호화는 클러스터 안의 controller만 할 수 있다.

변경 사항

파일 무엇을 · 왜
.github/workflows/seal-ai-secrets.yml 봉인 workflow. workflow_dispatch 수동 트리거 — Secret 회전은 사람이 결정하는 사건이고 push마다 돌 이유가 없다
deploy/sealed-secrets/pinlog-dev-cert.pem Infra가 kube-system/sealed-secrets-controller에서 가져다 준 공개 인증서. 공개키라 커밋해도 안전하다
docs/implements/2026-07-29-sealed-secret-handoff.md 설계 결정과 검증 범위
docs/implements/README.md · docs/WORKLOG.md 색인 각 1행

설계 결정

kubeseal --raw — 평문 YAML을 만들지 않는다

흔한 경로는 kubectl create secret generic --dry-run=client -o yaml | kubeseal인데, 이 파이프의 중간 산출물이 평문 base64 Secret YAML이다. 파이프 안에만 있다 해도 한 단계 실수(리다이렉트·tee·디버깅용 cat)로 파일이 된다.

--raw는 값 하나를 받아 암호문 한 줄을 돌려준다. 평문은 셸 변수와 파이프에만 존재하고, 변수 확장은 셸 내부에서 끝나므로 ps로도 보이지 않는다.

기각한 대안: kubectl 경로가 manifest를 손으로 조립하지 않아도 되어 간단하지만, 위 이유로 택하지 않았다. 조립 비용은 echo 몇 줄이다.

배포 전에 실패시킬 수 있는 것은 배포 전에

앱이 기동 시 fail-fast로 거르는 검사를 봉인 시점에 미리 돌린다.

GMS_BASE_URL                 /gmsapi/ 포함        embedding·judge 양쪽이 요구
PINLOG_EMBEDDING_DIMENSION   정수
PINLOG_EMBEDDING_PROFILE     나머지 셋을 부분 문자열로 포함 (config.py 와 같은 규칙)

profile이 어긋난 채 배포되면 기존 임베딩이 전부 조회 대상에서 빠진다. 조용히 검색 결과만 비므로 배포 후에 알아채기 가장 어려운 종류다.

EMBEDDING 넷은 비밀이 아닌데 왜 같은 경로인가

모델명·차원·거리함수·프로필 식별자이고 정본은 공개 문서 P32에 있다. ai#32에서 Infra도 이 넷을 *"non-secret ConfigMap 명시 주입"*으로 분류했다.

그럼에도 Secret 경로로 다루는 것은 Infra가 주입 경로를 하나로 요구했기 때문이다. 값이 공개라는 사실과 "어디서 주입되는가"는 별개이고, 앱이 이 넷에 기본값을 두지 않으므로 누락 시 Pod이 아예 뜨지 않는다 — 경로를 나누면 그 실패가 두 곳에서 날 수 있다.

테스트 / 검증

  • 인증서 지문 대조openssl x509 -noout -fingerprint -sha256이 Infra 제공 값(4A:C5:…:A2)과 일치
  • 인증서 유효기간-checkend 0 통과 (2026-07-20 ~ 2036-07-17)
  • kubeseal 릴리스 실재 확인v0.27.1 및 asset 이름 두 개를 GitHub API로 조회
  • workflow YAML 파싱 — 7 steps
  • 셸 블록 문법bash -n 5개 전부 통과
  • 실제 봉인 미실행 — Actions Secret 등록 후에만 가능하다. 봉인이 controller가 복호화할 수 있는 암호문을 만드는지는 Infra가 클러스터에 적용해 봐야 확정된다

리뷰 포인트

  1. encryptedData 검증을 두 방향으로 건다. ⑴ 모든 값이 Ag로 시작하는 100자 이상 base64인가 ⑵ 평문이 새지 않았는가 — 단 12자 이상만 본다. 1536·cosine 같은 짧은 값은 base64 암호문에 우연히 포함돼 거짓 양성을 만들고, 그 길이대는 ⑴이 덮는다
  2. heredoc을 쓰지 않았다. 이 스크립트는 workflow YAML의 블록 스칼라 안이라 들여쓰기가 곧 블록 경계다. heredoc 본문을 왼쪽 끝에 붙이면 workflow YAML이 끊기고, 들여쓰면 그 공백이 산출 파일에 들어가 SealedSecret이 깨진다. 둘 다 겪고 echo 조립으로 바꿨다
  3. scope는 strict — 암호문이 pinlog-dev/ai-owner-secrets 조합에 묶인다. 유출된 manifest가 다른 네임스페이스에서 복호화되는 경로가 없다

미결 / 후속

  • PINLOG_AI_INFRA_PR_TOKEN — Infra 요구 목록에 있으나 이 앱이 읽지 않는다(레포 전체 참조 0건). GitOps 레포에 PR을 여는 용도로 보이며, 그렇다면 전달이 artifact가 아니라 PR이 된다. 대상 레포·권한 범위·만료·커밋 경로를 확인한 뒤 workflow를 그쪽으로 바꾼다
  • 회전 절차 — 값을 갱신하고 다시 돌리면 새 manifest가 나온다. 클러스터의 기존 Secret 교체 시점은 Infra 몫이고 합의되지 않았다
  • prod — 이 인증서는 pinlog-dev 전용이다. prod는 별도 controller·인증서가 필요하다

🤖 Generated with Claude Code

GitHub Actions Secret 은 Kubernetes Pod 에 자동 전달되지 않는다. Infra 가
"값을 열람하지 않은 채 암호화 Secret 만 배포"하는 경계를 세웠으므로(ai#32),
공개키로 봉인한 manifest 만 넘기는 경로를 만든다.

kubeseal --raw 로 값 하나씩 봉인한다. 흔히 쓰는
kubectl create secret --dry-run | kubeseal 경로는 중간 산출물이 평문 base64
Secret YAML 이라, 한 단계 실수로 파일이 될 수 있다. --raw 는 평문이 셸 변수와
파이프에만 존재한다.

앱이 기동 시 fail-fast 로 거르는 검사(/gmsapi/ 형식, profile 정합)를 봉인
시점으로 앞당긴다. profile 이 어긋난 채 배포되면 기존 임베딩이 조회 대상에서
전부 빠지는데, 조용히 결과만 비므로 배포 후에는 알아채기 어렵다.

controller 인증서는 SHA-256 지문으로 고정한다. 엉뚱한 공개키로 봉인하면
복호화 실패가 배포 시점에야 드러난다.

EMBEDDING 넷은 비밀이 아니다(정본 P32). Infra 가 주입 경로를 하나로 요구해
같은 경로로 다루며, 그 판단 근거는 리포트에 남겼다.

검증: 인증서 지문 Infra 제공값과 일치 · 유효기간 확인 · kubeseal 릴리스 실재
확인 · workflow YAML 파싱 · 셸 블록 bash -n 5개 통과.
실제 봉인은 미실행 — Actions Secret 등록 후에만 가능하다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@colosair
colosair merged commit 299f6a6 into main Jul 29, 2026
2 checks passed
@colosair
colosair deleted the feat/S15P11A705-96-seal-ai-secrets branch July 29, 2026 06:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant