diff --git a/docs/release/distribution.md b/docs/release/distribution.md index 7f5d60858..fc9e34a46 100644 --- a/docs/release/distribution.md +++ b/docs/release/distribution.md @@ -79,7 +79,7 @@ PR 유형과 파일 단위 이력은 사용자 흐름의 의미를 완전히 표 기존 `release-distribution.yml`의 `develop` 수동 배포(`workflow_dispatch`)는 #1029에서 제거했다. 도착지와 산출물 버전이 main 경로와 같아 실익이 PR 생성 한 단계뿐이었던 반면, release keystore와 service account를 임의 ref에 노출하는 표면이었다. -예외적으로 [`firebase-wif-canary.yml`](../../.github/workflows/firebase-wif-canary.yml)은 `develop` 또는 `main`에서 보호 Environment 승인과 명시적 확인을 받은 뒤 실제 APK를 올리는 **수동 인증 호환성 검증 경로**다. 일반 릴리스나 임의 브랜치 배포 수단으로 사용하지 않으며, production JSON 자격을 WIF로 교체하기 전 판정 절차는 [WIF canary runbook](firebase-wif-canary.md)을 따른다. +예외적으로 [`firebase-wif-canary.yml`](../../.github/workflows/firebase-wif-canary.yml)은 `develop` 또는 `main`에서 보호 Environment 승인과 명시적 확인을 받은 뒤 실제 APK를 올리는 **수동 인증 호환성 검증 경로**다. 일반 릴리스나 임의 브랜치 배포 수단으로 사용하지 않는다. production `release-distribution.yml`도 #1463부터 같은 WIF 경로로 인증하고, 장기 JSON 키 경로는 #1566에서 걷었다. canary 실행 조건은 [WIF canary runbook](firebase-wif-canary.md)을 따른다. `develop` → `main` 릴리스 PR 본문에 다음 섹션을 채운다. @@ -106,15 +106,14 @@ CI가 사용하는 GitHub Secrets (Settings → Secrets and variables → Action | `RELEASE_STORE_PASSWORD` | keystore 비밀번호 | | `RELEASE_KEY_ALIAS` | key alias (`afternote-release`) | | `RELEASE_KEY_PASSWORD` | key 비밀번호 | -| `FIREBASE_SERVICE_ACCOUNT_JSON` | App Distribution Admin 권한 부여된 service account JSON 원문 | | `KAKAO_NATIVE_APP_KEY` · `GOOGLE_WEB_CLIENT_ID` · `GOOGLE_SERVICES_JSON_B64` | release distribution·WIF canary·dependency audit가 쓰는 실서비스 앱 설정 | -| `GCP_WORKLOAD_IDENTITY_PROVIDER` · `GCP_FIREBASE_SERVICE_ACCOUNT` | 수동 WIF canary 전용 OIDC 설정 ([runbook](firebase-wif-canary.md)) | +| `GCP_WORKLOAD_IDENTITY_PROVIDER` · `GCP_FIREBASE_SERVICE_ACCOUNT` | release distribution과 WIF canary가 Firebase 업로드 직전에 쓰는 WIF 설정. 서비스 계정 `afternote-ci` 는 이 두 워크플로에만 묶여 있다([Play release runbook](../play-release.md) 사전 준비 3번) | > base64 인코딩: `base64 -i ~/afternote-release.jks | pbcopy` (macOS) PR 검증용 lint·unit-test·screenshot은 repository secret 대신 `.github/actions/setup-ci-config`가 만드는 결정적 CI 전용 placeholder를 사용한다. 이 fixture는 -배포에 사용할 수 없으며, production `release-distribution.yml`은 계속 승인된 환경의 JSON 자격을 사용한다. WIF canary 성공만으로 이를 제거하지 않는다. +배포에 사용할 수 없으며, production `release-distribution.yml`은 승인된 환경에서 WIF로 받은 단기 자격으로만 업로드한다. ### 배포 provenance: 이 APK 가 어느 commit·run 에서 나왔는지 (#851) diff --git a/docs/release/firebase-wif-canary.md b/docs/release/firebase-wif-canary.md index 1d7627d63..0450f1cfe 100644 --- a/docs/release/firebase-wif-canary.md +++ b/docs/release/firebase-wif-canary.md @@ -1,30 +1,33 @@ # Firebase App Distribution WIF canary -이 문서는 기존 `FIREBASE_SERVICE_ACCOUNT_JSON` 인증을 즉시 교체하지 않고, 별도 수동 -canary에서 GitHub OIDC와 Google Cloud Workload Identity Federation 호환성을 먼저 확인하는 -절차다. 저장소 workflow만 준비하며 Google Cloud·GitHub Settings 변경과 실제 업로드 실행은 -별도 승인 범위다. +이 문서는 수동 canary에서 GitHub OIDC와 Google Cloud Workload Identity Federation으로 +Firebase App Distribution 업로드를 확인하는 절차다. 처음에는 장기 JSON 키를 WIF로 바꾸기 전의 +호환성 검증으로 만들었고, 전환은 끝났다. production `release-distribution.yml`은 #1463부터 +WIF로 인증하고, `FIREBASE_SERVICE_ACCOUNT_JSON` 롤백 경로는 #1566에서 걷었다. ## 현재 안전 경계 -- production `release-distribution.yml`은 기존 JSON 인증을 그대로 사용한다. +- production `release-distribution.yml`도 같은 WIF 설정으로 인증한다. JSON 키 경로는 없다. - canary는 `develop` 또는 `main`의 수동 실행과 `release-distribution` Environment 승인을 모두 요구한다. - signed APK는 인증 전에 빌드하고 같은 runner에서 바로 업로드한다. public Actions artifact로 게시하지 않는다. - WIF 인증은 업로드 직전에만 수행한다. provider·service account 값, token, 생성된 ADC 상세는 workflow summary나 별도 artifact에 기록하지 않는다. -- canary 실패 시 production workflow나 기존 JSON secret을 변경·삭제하지 않는다. +- canary 실패만으로 production workflow의 인증 설정을 바꾸지 않는다. -## 별도 승인이 필요한 외부 준비 +## 외부 준비 -다음 항목은 이 PR에서 만들거나 수정하지 않는다. +전환 때 갖춘 Google Cloud 쪽 준비다. provider와 서비스 계정 바인딩의 현재 값은 +[Play release runbook](../play-release.md) 사전 준비 3번에 있다. 1. GitHub OIDC용 workload identity pool/provider와 Firebase App Distribution 전용 최소 권한 service account를 준비한다. 2. provider attribute mapping에 `repository`, `repository_owner`, `ref`, `workflow_ref`, `environment` claim을 포함한다. -3. attribute condition은 최소한 다음을 동시에 제한한다. +3. attribute condition은 처음에 다음을 동시에 제한하도록 설계했다. 지금 condition은 `workflow_ref` + 허용 목록에 `release-distribution.yml`과 `release-play-internal.yml`도 들어 있다. 현재 값의 정본은 + Play release runbook 사전 준비 3번이다. - repository: `Afternote/Afternote-FE` - owner: `Afternote` - ref: `refs/heads/develop` 또는 `refs/heads/main` @@ -32,7 +35,11 @@ canary에서 GitHub OIDC와 Google Cloud Workload Identity Federation 호환성 - environment: `release-distribution` 4. 보호된 Environment에 `GCP_WORKLOAD_IDENTITY_PROVIDER`와 `GCP_FIREBASE_SERVICE_ACCOUNT`를 masked secret으로 등록한다. -5. 허용 branch canary 성공뿐 아니라 다른 branch·fork·workflow의 credential 발급 거부도 +5. Firebase 서비스 계정 `afternote-ci`의 `roles/iam.workloadIdentityUser`는 저장소 전체가 아니라 + `release-distribution.yml@refs/heads/main`, `firebase-wif-canary.yml@refs/heads/main`, + `firebase-wif-canary.yml@refs/heads/develop` 셋의 `attribute.workflow_ref`에만 준다. 같은 provider를 + Play 배포 워크플로도 통과하므로, 저장소 단위로 묶으면 그 워크플로가 Firebase 계정을 빌릴 수 있다. +6. 허용 branch canary 성공뿐 아니라 다른 branch·fork·workflow의 credential 발급 거부도 Google Cloud audit evidence로 확인한다. 공식 근거: @@ -42,14 +49,14 @@ canary에서 GitHub OIDC와 Google Cloud Workload Identity Federation 호환성 - [Firebase App Distribution CI/CD와 WIF credential configuration](https://firebase.google.com/docs/app-distribution/best-practices-distributing-android-apps-to-qa-testers-with-ci-cd) - [google-github-actions/auth](https://github.com/google-github-actions/auth) -## 전환 판정 +## 전환 판정 (완료) -JSON 경로 제거는 아래가 모두 PASS인 별도 후속 변경에서만 한다. +#1463·#1566 전환 때 쓴 기준이다. 인증 구성을 다시 바꿀 때도 같은 기준으로 확인한다. - 승인된 `develop` 또는 `main` canary APK가 WIF ADC로 1회 업로드된다. - Firebase release의 source SHA와 실행 URL이 해당 canary run과 일치한다. - 허용하지 않은 branch·fork·workflow의 credential 발급이 거부된다. - 대상 service account 권한이 Firebase App Distribution 업로드 최소 범위로 검토된다. -- 실패 canary 뒤 기존 production JSON 경로가 정상 동작한다. +- 실패 canary 뒤 production 배포 경로가 정상 동작한다. -하나라도 확인되지 않으면 상태는 `UNVERIFIED`이며 기존 JSON secret을 유지한다. +하나라도 확인되지 않으면 상태는 `UNVERIFIED`이며 production 인증 구성을 바꾸지 않는다.