You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Firebase App Distribution 인증을 Workload Identity Federation으로 전환 #850
changed the title [-]release(cd): Firebase App Distribution 인증을 Workload Identity Federation으로 전환[/-][+]Firebase App Distribution 인증을 Workload Identity Federation으로 전환[/+]on Aug 28, 2026
착수 시도 — FE 코드로 진행할 수 있는 구간이 남아 있지 않다 (2026-08-29 실측)
동시 착수 후보로 잡고 열어 봤으나, 선행 구성이 하나도 안 돼 있어 코드 쪽에서 더 밀 수 없다. 실측 결과를 남긴다.
이미 되어 있는 것 — 워크플로는 준비 완료다
firebase-wif-canary.yml 이 이 이슈 범위의 「별도 canary workflow_dispatch」를 이미 구현하고 있다.
id-token: write 로 short-lived credential 발급
build job 은 자격 없이 수행하고 upload job 에서만 토큰을 받도록 분리 (완료 조건의 「인증 시점을 upload 직전으로」)
deployable ref 제한 + 명시적 업로드 확인 게이트 (Restrict canary to deployable branches)
Verify WIF configuration is set 스텝이 GCP_WORKLOAD_IDENTITY_PROVIDER·GCP_FIREBASE_SERVICE_ACCOUNT 존재를 먼저 확인
정책 테스트 firebase-wif-canary-policy.test.mjs 도 함께 있다
막고 있는 것 — GCP 구성과 secret 등록
확인 항목
실측
GCP_WORKLOAD_IDENTITY_PROVIDER secret
없음 (repository secrets · release-distribution environment 둘 다)
GCP_FIREBASE_SERVICE_ACCOUNT secret
없음 (같음)
firebase-wif-canary 실행 이력
0건 — 한 번도 돈 적이 없다
release-distribution environment 의 현행 secret
FIREBASE_SERVICE_ACCOUNT_JSON 외 4건(서명 자격)
release-distribution.yml 의 인증
여전히 secrets.FIREBASE_SERVICE_ACCOUNT_JSON (L99)
즉 남은 작업은 전부 저장소 밖이다 — Google Cloud 에 workload identity pool/provider 와 최소 권한 service account 를 만들고, attribute condition 으로 repository·branch·workflow claim 을 제한하고, 그 값을 GitHub secret 으로 등록하는 일. FE 코드 변경으로 대체할 수 없고, GCP 콘솔 권한과 repository secret 쓰기 권한이 필요하다.
release-distribution.yml 전환은 그 다음이다 — 이 이슈의 완료 조건이 「canary 실패 시 기존 JSON 방식으로 되돌릴 수 있고, 성공 전에는 기존 secret 을 삭제하지 않는다」이므로, canary 가 한 번 성공하기 전에 프로덕션 배포 경로를 건드리는 것은 순서를 어기는 것이다.
다음 한 걸음
권한 보유자가 GCP pool/provider·service account 를 만들고 두 secret 을 등록하면, 그 즉시 firebase-wif-canary 를 workflow_dispatch 로 한 번 돌려 성공/rollback 을 확인하고 release-distribution.yml 전환으로 넘어갈 수 있다. 워크플로 쪽은 그 시점에 추가로 만들 것이 없다.
service account 를 새로 만들지 않았다. 기존 afternote-ci@ 의 역할이 roles/firebaseappdistro.admin하나뿐이라 「최소 권한」 조건을 이미 만족한다. 새 SA 를 만들면 권한만 같고 관리 지점만 늘어난다.
Provider 의 attribute condition 에 release-distribution.yml 을 미리 허용해 뒀다 — 프로덕션 전환 때 GCP 를 다시 건드릴 필요가 없다.
실측 결과
10. Verify provenance and attestation subject digest: success
11. Authenticate to Google Cloud with WIF: success
12. Upload the already-built APK to Firebase App Distribution: success
결정적 증거 — 같은 job 로그의 환경변수 덤프에서 FIREBASE_SERVICE_ACCOUNT_JSON: 이 빈 값이다. canary 는 장기 키를 아예 전달받지 않았고, google-github-actions/auth 가 발급한 단기 credential file 을 GOOGLE_APPLICATION_CREDENTIALS 로 물려 Gradle 플러그인이 그걸로 업로드했다. ADC/WIF 호환성 실측(범위 첫 항목)이 이것으로 끝났다.
완료 조건 대조
조건
상태
장기 JSON key 없이 승인된 develop/main 배포가 App Distribution 에 1회 성공
release-distribution.yml 을 WIF 로 전환. 이때 firebase-wif-canary-policy.test.mjs 의 WIF canary retains the production JSON rollback path(프로덕션에 FIREBASE_SERVICE_ACCOUNT_JSON 이 있어야 한다고 단언)가 함께 갱신돼야 한다 — 롤백 경로의 정의가 바뀌는 시점이므로 정책 갱신이 정상 신호다.
전환 성공 확인 후 FIREBASE_SERVICE_ACCOUNT_JSON secret 제거.
afternote-ci@ 의 USER_MANAGED 키 2개(76cfe5e5… 2026-07-28, f868d9bc… 2026-08-04) 삭제. 이게 이 이슈가 없애려던 장기 자격의 실체다.
곁가지
canary 가 40분 넘게 안 돈 이유는 WIF 문제가 아니라 8/27 부터 승인 대기로 멈춰 있던 main 배포(99f0cea9a, PR #1273 머지분)가 firebase-app-distribution-upload concurrency 를 점유하고 있었기 때문이다. 그 배포를 승인해 먼저 내보냈고(성공), 그제야 canary 가 진입했다. 8/24 이후 main 최신이 미배포 상태로 방치돼 있었다는 뜻이라 함께 적어 둔다.
google-github-actions/auth full SHA 고정 + id-token: write
✅
release-distribution.yml:170 — @7c6bc770… (v3), id-token: write 는 39행
build job 은 credential 없이, upload job 에서만 발급
✅
「토큰 수명이 짧으므로 빌드·attestation 이 끝난 업로드 직전에 발급한다」 주석과 함께 upload 직전 배치
토큰·credential path 를 로그에 남기지 않음
✅
export_environment_variables 로 액션이 주입, 워크플로가 경로를 손으로 쓰지 않는다
정책 고정
✅
firebase-wif-canary-policy.test.mjs 가 canary·production 양쪽에 FIREBASE_SERVICE_ACCOUNT_JSON 이 없음을 단언
전환 커밋은 b8e015c21 (「ci: 릴리스 배포 인증을 장기 JSON 키에서 WIF 로 전환」, 2026-08-29 12:18) 이다.
🔴 남은 것 — 코드가 아니라 운영이다
완료 조건
상태
막는 것
장기 JSON key 없이 승인된 develop/main 배포가 1회 성공
❌
WIF 전환 후 release-distribution 실행이 0회다
전환 후 secret 목록에 service account JSON 이 없다
❌
위가 선행 — 본문 방침 「성공 전에는 기존 secret 을 삭제하지 않는다」
release-distribution 최근 성공은 2026-08-27 이고, 그 시점 워크플로는 아직 JSON 방식이었다(해당 run 의 headSha 로 워크플로 원문을 꺼내 확인).
전환 커밋 b8e015c21 은 develop 에만 있고 origin/main 에는 없다.
release-distribution environment secret 에 FIREBASE_SERVICE_ACCOUNT_JSON 이 그대로 있다 — 롤백 경로를 살려 두는 것이 본문 방침이라 지금 지우면 안 된다.
.github/actions/setup-release-config/action.yml 의 firebase-service-account-json 입력도 같은 이유로 남긴다. 현재 이 입력을 넘기는 워크플로는 0곳이고(canary 는 「의도적으로 넘기지 않는다」 고 주석), JSON 방식으로 되돌릴 수 있는 유일한 경로다.
그래서 다음 행동
develop 에서 release-distribution 을 1회 실행해 WIF 로 업로드 성공을 확인한다 — environment required reviewer 승인이 필요한 운영 작업이라 코드 변경으로 대신할 수 없다.
성공하면 같은 PR 에서 ① FIREBASE_SERVICE_ACCOUNT_JSON environment secret 삭제 ② setup-release-config 의 죽은 firebase-service-account-json 입력·분기 제거 를 함께 처리하고 이 이슈를 닫는다.
[로컬 비밀 이관 2026-08-26] afternote-android-firebase-adminsdk-fbsvc-93a9fefa24.json
같은 파일 (이중 백업)
내용 검증 — type: service_account · project_id: afternote-android · client_email: firebase-adminsdk-fbsvc@afternote-android.iam.gserviceaccount.com · private_key 존재. 필요하면 이 파일로 secret 을 재등록해 JSON 방식으로 되돌릴 수 있다.
완료 조건 전량
조건
상태
canary 로 ADC/WIF 호환성 실측
✅ 0829
WIF pool/provider·최소 권한 SA 구성
✅ 0829
repository·branch·workflow claim 제한
✅ attribute condition
full SHA 고정 google-github-actions/auth + id-token: write
✅
build 는 credential 없이, upload job 에서만 발급
✅ upload 직전 배치
장기 JSON key 없이 승인된 배포 1회 성공
✅ run 33292931533 (0830)
canary 실패 시 롤백 가능·성공 전 secret 유지
✅ 성공까지 보존 후 삭제
token·credential path 를 로그에 남기지 않음
✅
전환 후 secret 목록에 JSON 없음
✅ 이번 삭제
남은 코드 정리
setup-release-config 의 죽은 firebase-service-account-json 입력·ADC 분기·output 제거는 PR #1566 이 처리한다. 그 PR 이 머지되면 이 이슈를 닫는다.
참고 — GCP 쪽 SA 키(93a9fefa…) 자체는 폐기하지 않았다. 1Password 백업으로 롤백 경로를 남겨 두는 편이 아직 낫다고 판단했다. WIF 가 몇 회 더 안정적으로 돌면 그때 GCP 에서 키를 폐기하는 것이 마지막 정리다.
GCP 쪽 service account 키(93a9fefa…)는 폐기하지 않았다. 1Password 백업(Afternote Firebase 서비스 계정 (App Distribution) 첨부 + [로컬 비밀 이관 2026-08-26] 항목, 이중)으로 JSON 방식 롤백 경로를 아직 남겨 두는 편이 낫다고 판단했다.
WIF 로 몇 회 더 배포가 안정적으로 돌면 그때 GCP 에서 키를 폐기하는 것이 마지막 정리다. 지금 이 이슈를 열어 두고 기다릴 성격은 아니라 닫는다 — 필요해지면 별건으로 연다.
Activity
Merge pull request #854 from Afternote/feat/850
[-]release(cd): Firebase App Distribution 인증을 Workload Identity Federation으로 전환[/-][+]Firebase App Distribution 인증을 Workload Identity Federation으로 전환[/+]OnlyReadOneBook commentedon Aug 29, 2026
착수 시도 — FE 코드로 진행할 수 있는 구간이 남아 있지 않다 (2026-08-29 실측)
동시 착수 후보로 잡고 열어 봤으나, 선행 구성이 하나도 안 돼 있어 코드 쪽에서 더 밀 수 없다. 실측 결과를 남긴다.
이미 되어 있는 것 — 워크플로는 준비 완료다
firebase-wif-canary.yml이 이 이슈 범위의 「별도 canary workflow_dispatch」를 이미 구현하고 있다.id-token: write로 short-lived credential 발급Restrict canary to deployable branches)Verify WIF configuration is set스텝이GCP_WORKLOAD_IDENTITY_PROVIDER·GCP_FIREBASE_SERVICE_ACCOUNT존재를 먼저 확인firebase-wif-canary-policy.test.mjs도 함께 있다막고 있는 것 — GCP 구성과 secret 등록
GCP_WORKLOAD_IDENTITY_PROVIDERsecretrelease-distributionenvironment 둘 다)GCP_FIREBASE_SERVICE_ACCOUNTsecretfirebase-wif-canary실행 이력release-distributionenvironment 의 현행 secretFIREBASE_SERVICE_ACCOUNT_JSON외 4건(서명 자격)release-distribution.yml의 인증secrets.FIREBASE_SERVICE_ACCOUNT_JSON(L99)즉 남은 작업은 전부 저장소 밖이다 — Google Cloud 에 workload identity pool/provider 와 최소 권한 service account 를 만들고, attribute condition 으로 repository·branch·workflow claim 을 제한하고, 그 값을 GitHub secret 으로 등록하는 일. FE 코드 변경으로 대체할 수 없고, GCP 콘솔 권한과 repository secret 쓰기 권한이 필요하다.
release-distribution.yml전환은 그 다음이다 — 이 이슈의 완료 조건이 「canary 실패 시 기존 JSON 방식으로 되돌릴 수 있고, 성공 전에는 기존 secret 을 삭제하지 않는다」이므로, canary 가 한 번 성공하기 전에 프로덕션 배포 경로를 건드리는 것은 순서를 어기는 것이다.다음 한 걸음
권한 보유자가 GCP pool/provider·service account 를 만들고 두 secret 을 등록하면, 그 즉시
firebase-wif-canary를 workflow_dispatch 로 한 번 돌려 성공/rollback 을 확인하고release-distribution.yml전환으로 넘어갈 수 있다. 워크플로 쪽은 그 시점에 추가로 만들 것이 없다.OnlyReadOneBook commentedon Aug 29, 2026
✅ canary 성공 — 장기 JSON 키 없이 업로드됐다 (2026-08-29)
앞선 코멘트에서 「막고 있는 것」으로 지목한 GCP 구성과 secret 등록을 마치고 canary 를 돌렸다. run 33227932789 — success.
구성한 것
github-actions(projectafternote-android/918752702102)afternote-fe— issuerhttps://token.actions.githubusercontent.comgoogle.subject·attribute.repository·attribute.repository_owner·attribute.ref·attribute.workflow_refAfternote/Afternote-FE&& ref ∈ {refs/heads/develop,refs/heads/main} && workflow_ref ∈ {firebase-wif-canary.yml@*,release-distribution.yml@*}afternote-ci@afternote-android.iam.gserviceaccount.com재사용roles/iam.workloadIdentityUser←principalSet://…/attribute.repository/Afternote/Afternote-FEGCP_WORKLOAD_IDENTITY_PROVIDER·GCP_FIREBASE_SERVICE_ACCOUNT→release-distributionenvironmentservice account 를 새로 만들지 않았다. 기존
afternote-ci@의 역할이roles/firebaseappdistro.admin하나뿐이라 「최소 권한」 조건을 이미 만족한다. 새 SA 를 만들면 권한만 같고 관리 지점만 늘어난다.Provider 의 attribute condition 에
release-distribution.yml을 미리 허용해 뒀다 — 프로덕션 전환 때 GCP 를 다시 건드릴 필요가 없다.실측 결과
결정적 증거 — 같은 job 로그의 환경변수 덤프에서
FIREBASE_SERVICE_ACCOUNT_JSON:이 빈 값이다. canary 는 장기 키를 아예 전달받지 않았고,google-github-actions/auth가 발급한 단기 credential file 을GOOGLE_APPLICATION_CREDENTIALS로 물려 Gradle 플러그인이 그걸로 업로드했다. ADC/WIF 호환성 실측(범위 첫 항목)이 이것으로 끝났다.완료 조건 대조
firebaseappdistro.admin단일FIREBASE_SERVICE_ACCOUNT_JSON그대로 유지남은 것
release-distribution.yml을 WIF 로 전환. 이때firebase-wif-canary-policy.test.mjs의WIF canary retains the production JSON rollback path(프로덕션에FIREBASE_SERVICE_ACCOUNT_JSON이 있어야 한다고 단언)가 함께 갱신돼야 한다 — 롤백 경로의 정의가 바뀌는 시점이므로 정책 갱신이 정상 신호다.FIREBASE_SERVICE_ACCOUNT_JSONsecret 제거.afternote-ci@의 USER_MANAGED 키 2개(76cfe5e5…2026-07-28,f868d9bc…2026-08-04) 삭제. 이게 이 이슈가 없애려던 장기 자격의 실체다.곁가지
canary 가 40분 넘게 안 돈 이유는 WIF 문제가 아니라 8/27 부터 승인 대기로 멈춰 있던 main 배포(
99f0cea9a, PR #1273 머지분)가firebase-app-distribution-uploadconcurrency 를 점유하고 있었기 때문이다. 그 배포를 승인해 먼저 내보냈고(성공), 그제야 canary 가 진입했다. 8/24 이후 main 최신이 미배포 상태로 방치돼 있었다는 뜻이라 함께 적어 둔다.OnlyReadOneBook commentedon Aug 30, 2026
현황 — 코드 전환은 끝났고, 남은 것은 실배포 1회다 (2026-08-30 실측)
완료된 것
google-github-actions/authfull SHA 고정 +id-token: writerelease-distribution.yml:170—@7c6bc770…(v3),id-token: write는 39행export_environment_variables로 액션이 주입, 워크플로가 경로를 손으로 쓰지 않는다firebase-wif-canary-policy.test.mjs가 canary·production 양쪽에FIREBASE_SERVICE_ACCOUNT_JSON이 없음을 단언전환 커밋은
b8e015c21(「ci: 릴리스 배포 인증을 장기 JSON 키에서 WIF 로 전환」, 2026-08-29 12:18) 이다.🔴 남은 것 — 코드가 아니라 운영이다
release-distribution실행이 0회다release-distribution최근 성공은 2026-08-27 이고, 그 시점 워크플로는 아직 JSON 방식이었다(해당 run 의headSha로 워크플로 원문을 꺼내 확인).b8e015c21은 develop 에만 있고origin/main에는 없다.release-distributionenvironment secret 에FIREBASE_SERVICE_ACCOUNT_JSON이 그대로 있다 — 롤백 경로를 살려 두는 것이 본문 방침이라 지금 지우면 안 된다..github/actions/setup-release-config/action.yml의firebase-service-account-json입력도 같은 이유로 남긴다. 현재 이 입력을 넘기는 워크플로는 0곳이고(canary 는 「의도적으로 넘기지 않는다」 고 주석), JSON 방식으로 되돌릴 수 있는 유일한 경로다.그래서 다음 행동
release-distribution을 1회 실행해 WIF 로 업로드 성공을 확인한다 — environment required reviewer 승인이 필요한 운영 작업이라 코드 변경으로 대신할 수 없다.FIREBASE_SERVICE_ACCOUNT_JSONenvironment secret 삭제 ②setup-release-config의 죽은firebase-service-account-json입력·분기 제거 를 함께 처리하고 이 이슈를 닫는다.OnlyReadOneBook commentedon Aug 30, 2026
정정 — develop 수동 배포 경로는 존재하지 않는다 (2026-08-30)
바로 앞 코멘트에서 「develop 에서
release-distribution을 1회 실행한다」 고 적었는데 그 경로가 없다. 워크플로 트리거를 실측하지 않고 쓴 것이다.workflow_dispatch는 없다. 유일한 배포 트리거는main브랜치 push 이고, 그것은 곧 develop → main 릴리스 머지다.그래서 이 이슈를 닫으려면
b8e015c21이 main 에 들어간다)release-distribution을 발동,release-distributionenvironment 승인1hyok단독FIREBASE_SERVICE_ACCOUNT_JSONenvironment secret 삭제 +setup-release-config의 죽은firebase-service-account-json입력·분기 제거b8e015c21은 develop 에만 있고origin/main에는 없으므로, 다음 릴리스 머지가 곧 이 이슈의 실검증이 된다. 별도로 배포를 한 번 더 돌릴 필요가 없다는 뜻이기도 하다.실패 시 롤백 경로(
setup-release-config의 JSON 입력 + environment secret)는 그대로 살아 있다 — 본문 방침 「성공 전에는 기존 secret 을 삭제하지 않는다」 대로다.OnlyReadOneBook commentedon Aug 30, 2026
✅ WIF 경로 실배포 성공 — 완료 조건 1번 충족 (2026-08-30)
릴리스 PR #1558 이 머지(
1d0d1757)되면서release-distribution이 WIF 경로로 처음 실행됐고 성공했다. run 33292931533스텝 전량 success — 인증 경로 확인
장기 JSON 키를 쓰지 않고
google-github-actions/auth가 발급한 단기 credential 로 업로드가 끝났다.release-distributionenvironment 승인은 일혁이 04:38 에 처리했다.완료 조건 대조
남은 것 — 롤백 경로 정리
성공했으므로 이제 지운다.
FIREBASE_SERVICE_ACCOUNT_JSONenvironment secret 삭제 (release-distribution환경).github/actions/setup-release-config/action.yml의firebase-service-account-json입력과 ADC 파일 생성 분기 제거 — 현재 이 입력을 넘기는 워크플로가 0곳인 죽은 경로다둘을 함께 처리하는 PR 로 이 이슈를 닫는다.
OnlyReadOneBook commentedon Aug 30, 2026
✅ 마지막 완료 조건 충족 — secret 삭제 (2026-08-30)
release-distributionenvironment 에서FIREBASE_SERVICE_ACCOUNT_JSON을 삭제했다.레포 secret 목록에도 service account JSON 은 0건이다.
되돌릴 수 있음을 먼저 확인했다
GitHub secret 은 값을 읽을 수 없어 삭제가 비가역이다. 그래서 지우기 전에 1Password 백업을 실측했다.
Afternote Firebase 서비스 계정 (App Distribution)(0803 생성)afternote-android-firebase-adminsdk-fbsvc-93a9fefa24.json[로컬 비밀 이관 2026-08-26] afternote-android-firebase-adminsdk-fbsvc-93a9fefa24.json내용 검증 —
type: service_account·project_id: afternote-android·client_email: firebase-adminsdk-fbsvc@afternote-android.iam.gserviceaccount.com·private_key존재. 필요하면 이 파일로 secret 을 재등록해 JSON 방식으로 되돌릴 수 있다.완료 조건 전량
google-github-actions/auth+id-token: write남은 코드 정리
setup-release-config의 죽은firebase-service-account-json입력·ADC 분기·output 제거는 PR #1566 이 처리한다. 그 PR 이 머지되면 이 이슈를 닫는다.Merge pull request #1566 from Afternote/ci/850-drop-json-rollback-path
OnlyReadOneBook commentedon Aug 30, 2026
✅ 완료 — 닫는다 (2026-08-30)
코드 정리 PR #1566 이 머지(
9d8ed59c)되어 마지막 항목까지 끝났다.google-github-actions/auth+id-token: writerelease-distribution.yml:170FIREBASE_SERVICE_ACCOUNT_JSON삭제 (레포 전체 0건)재유입 가드
firebase-wif-canary-policy.test.mjs가 지금까지 워크플로 두 개만 봤는데, composite action 도 검사 대상에 넣었다. 워크플로가 안 넘겨도 action 이 입력을 들고 있으면 경로는 살아 있기 때문이다.역검증으로 입력을 되살리면 이 테스트가 실패하는 것을 확인했다.
남겨 둔 것 하나 — GCP SA 키
GCP 쪽 service account 키(
93a9fefa…)는 폐기하지 않았다. 1Password 백업(Afternote Firebase 서비스 계정 (App Distribution)첨부 +[로컬 비밀 이관 2026-08-26]항목, 이중)으로 JSON 방식 롤백 경로를 아직 남겨 두는 편이 낫다고 판단했다.WIF 로 몇 회 더 배포가 안정적으로 돌면 그때 GCP 에서 키를 폐기하는 것이 마지막 정리다. 지금 이 이슈를 열어 두고 기다릴 성격은 아니라 닫는다 — 필요해지면 별건으로 연다.