인수인계 자료를 정리하다가 Android 릴리스 서명키 release.keystore 의 소재를 확인해야 했다. Android 공식 문서의 앱 서명 항목대로 Play App Signing 에 들어가지 않은 앱은 서명키를 잃으면 업데이트 능력을 잃고 Google 도 키를 되찾아 주지 못하므로 인계 목록에서 우선순위가 높은 항목이다. GitHub Actions 를 보니 몇 달째 서명 빌드가 정상적으로 돌고 있었다.
Table of contents
Open Table of contents
원인 — 사본과 원본의 구분
워크플로를 열어 보니 조직 시크릿 RELEASE_KEYSTORE 에 base64 로 인코딩된 사본이 들어 있고 빌드할 때 디코드해서 jarsigner 로 서명하고 지우는 구조였다.
- run: echo "$RELEASE_KEYSTORE" | base64 -d > release.keystore
- run: jarsigner -keystore release.keystore -storepass "$STOREPASS" ...
- run: rm -f release.keystore
그런데 이 시크릿은 값을 넣을 수는 있고 되읽을 수는 없다. 워크플로 안에서 환경변수로 주입될 뿐 콘솔에서도 gh secret list 로도 값은 조회가 안 된다. 빌드는 되고 인계는 안 되는 상태이며 동작한다는 것과 보유한다는 것이 여기서 갈린다.
조치 — 있을 법한 곳의 전수 확인
원본 파일이 어디 있는지 찾았다. 첫 가설은 빌드 서버였는데 흔한 경로를 보고 없어서 find / -name '*.keystore' -o -name '*.jks' 로 루트부터 통째로 훑었다. 확장자와 프로젝트 이름과 소스 디렉터리를 전부 조건에 넣었는데 0건이었다.
여기서 이유가 드러났다. 그 서버에는 Android 저장소 자체가 받아져 있지 않았다. 받아져 있는 것은 백엔드 저장소 몇 개뿐이었으므로 애초에 있을 수 없는 곳을 뒤진 것이다.
로컬 클론을 git status --ignored 로 미커밋 파일까지 봤고 Secrets Manager 항목 십여 개를 전부 확인했지만 없었다. 후보를 하나씩 지우고 남은 것이 조직 시크릿의 읽을 수 없는 사본과 담당자 개인 로컬의 원본 둘이었다.
탐색 종료의 판단
이 지점에서 두 가지 길이 있었는데 백업 서버와 옛 이메일 첨부와 다른 사람 장비까지 더 뒤지는 것과 탐색 종료를 선언하고 인계 항목으로 올리는 것이다. 뒤쪽을 골랐다. 대신 담당자가 원본을 못 내놓으면 새 키로 앱을 다시 올려야 하는 것을 감수했다.
[인계 필수] Android 릴리스 서명키
- 원본 keystore 파일 / keypass / storepass / alias
- 조직 인프라 어디에도 원본이 없음. 시크릿에는 되읽기 불가한 사본만 존재
- 확인 완료: 빌드 서버 전체 스캔 0건 / 로컬 클론 0건 / Secrets Manager 십여 항목 0건
없는 것을 계속 찾는 것도 회피의 한 형태다. 어디에 없는지를 실측으로 확정하는 것이 그 시점의 결과물이고 그다음은 사람에게 받는 것이다.
write-only 자산의 목록
이 건을 계기로 같은 성질의 것들을 훑었다. 넣을 수는 있고 읽을 수는 없는 자산들이다. Actions 시크릿 대부분이 그렇고 AWS_SECRET_ACCESS_KEY 는 발급할 때 한 번만 보이며 일부 콘솔의 client_secret 은 재조회가 안 되고 서명키와 인증서의 비밀키도 마찬가지다.
공통점은 지금 잘 돌아간다는 사실이 우리가 갖고 있다는 것을 보증하지 않는다는 점이다. 돌아가는 이유는 과거 어느 시점에 누가 넣었기 때문이고 그 사람과 그 원본이 지금도 접근 가능한지는 별개 사실이다.
판단 기준 — 재생성 가능성으로 하는 점검
이런 항목은 동작 확인이 아니라 재생성 가능성으로 점검하기로 했다. 지금 이 값을 처음부터 다시 만들어 낼 수 있는지 묻고 못 만들면 원본 소재를 확정한다. 원본이 조직이 관리하는 곳에 있는지 보고 개인 장비에 있으면 인계 항목으로 올린다.
마지막으로 분실하면 무슨 일이 나는지를 본다. 이것이 우선순위를 정한다. API 키는 재발급하면 되지만 release.keystore 는 안 된다.
정리
- CI가 서명 빌드를 성공시킨다고 조직이 원본 키를 가진 것이 아니다
- 되읽을 수 없는 시크릿은 빌드에 쓰이고 인계에는 못 쓴다
- 원본 소재는 추론이 아니라 실측으로 확정한다
- 뒤지기 전에 그 저장소가 그 서버에 있기는 한지부터 본다
- 후보를 다 지웠으면 탐색 종료를 선언하고 인계 항목으로 넘긴다
- 없는 것을 계속 찾는 것도 회피의 한 형태다
- 자산 점검은 동작 확인이 아니라 재생성 가능성으로 한다
- 분실 시 영구 불가한 것과 재발급 가능한 것을 구분해 우선순위를 정한다