Files
platform-core/infrastructure/security/secrets

플랫폼 Secret 계약

상태: Phase 1·Keycloak DB·Gitea OIDC·AIStor Secret 적용

이 디렉터리에는 Secret 값이나 Secret 매니페스트를 저장하지 않는다. 수동 초기 구축 스크립트가 터미널 또는 지정된 파일에서 값을 읽어 Kubernetes API로 직접 전달한다.

Phase 1 Secret 계약

Namespace 이름 타입 소비자
platform-data gitea-db-credentials kubernetes.io/basic-auth username, password CloudNativePG DatabaseRole 및 최초 initdb
gitea gitea-db-credentials kubernetes.io/basic-auth username, password Gitea
gitea gitea-admin kubernetes.io/basic-auth username, password Gitea 초기 관리자 생성

두 namespace의 gitea-db-credentials는 동일한 username=gitea와 동일한 비밀번호를 가져야 한다. Kubernetes Secret은 namespace를 넘어서 참조할 수 없기 때문에 두 객체가 필요하다. scripts/bootstrap/create-phase1-secrets.sh는 세 Secret이 모두 없을 때만 최초 생성하고, 모두 있으면 계약을 검증해 재사용하며, 일부만 존재하면 중단한다.

Keycloak-only Secret 계약

Namespace 이름 타입 소비자
platform-data keycloak-db-credentials kubernetes.io/basic-auth username, password CloudNativePG DatabaseRole
keycloak keycloak-db-credentials kubernetes.io/basic-auth username, password Keycloak
gitea gitea-keycloak-oidc Opaque key, secret Gitea OAuth2 source

두 DB Secret은 username=keycloak과 동일한 비밀번호를 가져야 하며 platform-data 객체에는 cnpg.io/reload=true 라벨이 필요하다. create-keycloak-secrets.sh는 두 DB Secret만 two-or-none으로 생성·검증한다. configure-keycloak-gitea-oidc.sh는 Keycloak의 기존 confidential client secret을 새로 회전하지 않고 Gitea namespace의 gitea-keycloak-oidc에 전달한다. key는 client ID gitea이며 secret payload는 출력·디코드·문서화하지 않는다. 세 객체는 실제 적용됐고 타입과 정확한 key 이름만 검증했다.

bash scripts/bootstrap/create-keycloak-secrets.sh --generate --execute
bash scripts/bootstrap/configure-keycloak-gitea-oidc.sh --execute

Keycloak Operator가 만든 keycloak/keycloak-initial-admin도 실제 존재한다. 이 Secret은 저장소가 렌더하는 자격 증명 manifest가 아니다. named 관리자와 MFA를 검증한 뒤 초기 관리자의 보존·비활성화 정책을 별도 운영 절차로 결정한다.

AIStor Secret 계약

Namespace 이름 타입 소비자
aistor minio-license Opaque minio.license AIStor Operator
object-storage aistor-root-configuration Opaque config.env AIStor ObjectStore

두 AIStor Secret은 실제 적용됐다. minio.license는 사용자가 지정한 읽기 가능한 절대 regular file에서 직접 읽는다. config.env에는 강한 MINIO_ROOT_USER, MINIO_ROOT_PASSWORD export를 각각 이중 따옴표 형식으로 저장한다.

export MINIO_ROOT_USER="<redacted>"
export MINIO_ROOT_PASSWORD="<redacted>"

실제 값은 이 문서나 Git에 기록하지 않는다. AIStor 전용 create-aistor-secrets.sh는 두 객체를 two-or-none으로 생성·검증하고, 현재 클러스터에서는 기존 값을 회전하지 않고 재사용한다.

기존 create-phase2-secrets.sh는 Keycloak DB 두 객체와 AIStor 두 객체를 four-or-none으로 관리한다. 현재 Keycloak DB 두 객체만 의도적으로 존재하므로 이 스크립트는 실행하지 않는다. Keycloak Secret을 삭제하거나 검사를 우회하지 않는다. 기존 four-or-none helper는 호환용으로만 남긴다. AIStor 작업에는 전용 helper만 사용하며 one-of-two 상태에서는 자동 삭제·보정·덮어쓰기 없이 중단한다.

공통 운영 규칙

  • 평문, Base64 값, 실제 Secret YAML, 라이선스 본문, 개인 키를 Git에 커밋하지 않는다.
  • 스크립트는 호출자가 bash -x를 사용해도 명령 추적을 즉시 비활성화한다.
  • 최초 생성 경로의 임시 파일은 mktemp 전용 디렉터리에 umask 077로 만들고 정상 종료·오류·신호 종료 시 삭제한다.
  • 비밀값 대신 권한이 제한된 임시 파일 경로만 kubectl 인자로 전달한다. 생성 YAML은 두 kubectl 프로세스 사이의 파이프로만 전달하며 터미널에 출력하지 않는다.
  • Secret 이름과 키만 선언형 리소스에서 참조한다.
  • gitea-admin은 Keycloak OIDC 도입 후에도 비상용 로컬 관리자 계정으로 유지한다. initialOnlyRequireReset 모드이므로 Secret 값만 바꿔도 이미 생성된 관리자 계정 비밀번호는 바뀌지 않는다.
  • Argo CD 전환 전 별도 ADR로 SOPS, Sealed Secrets 또는 외부 Secret 동기화 방식을 선택한다.
  • 초기 구축 스크립트는 DB 비밀번호, 관리자 비밀번호, AIStor root 자격 증명, 라이선스를 회전하지 않는다. 회전은 소비자 재시작과 실제 접속 검증을 포함한 별도 운영 절차로 수행한다.