diff --git a/agent-roadmap/phase/security-distribution/PHASE.md b/agent-roadmap/phase/security-distribution/PHASE.md index e350695..f1e209f 100644 --- a/agent-roadmap/phase/security-distribution/PHASE.md +++ b/agent-roadmap/phase/security-distribution/PHASE.md @@ -14,6 +14,9 @@ AppSok을 개인용 도구에서 사내 표준 도구로 확장하기 위한 배 완료, 검토중, 진행중, 계획, 스케치 순서로 두어 아래로 갈수록 미래 작업에 가까워지게 정렬한다. 스케치 Milestone은 아직 구현 가능한 계획이 아니므로 계획 Milestone보다 아래에 둔다. +- [계획] Jenkins 얇은 빌드와 artifact 다운로드 + - 경로: `agent-roadmap/phase/security-distribution/milestones/jenkins-thin-artifact-ci.md` + - 요약: OTO 없이 Jenkins job만 얇게 구성해 AppSok 인증 ZIP을 빌드하고 Jenkins artifact 링크로 바로 다운로드할 수 있게 한다. - [계획] macOS 배포와 sandbox 검증 - 경로: `agent-roadmap/phase/security-distribution/milestones/macos-distribution.md` - 요약: signing/notarization/MDM 후보와 sandbox 상태에서 Jenkins/Keychain/ADB가 동작하는지 검증한다. diff --git a/agent-roadmap/phase/security-distribution/milestones/jenkins-thin-artifact-ci.md b/agent-roadmap/phase/security-distribution/milestones/jenkins-thin-artifact-ci.md new file mode 100644 index 0000000..af7390e --- /dev/null +++ b/agent-roadmap/phase/security-distribution/milestones/jenkins-thin-artifact-ci.md @@ -0,0 +1,83 @@ +# Milestone: Jenkins 얇은 빌드와 artifact 다운로드 + +## 위치 + +- Roadmap: `agent-roadmap/ROADMAP.md` +- Phase: `agent-roadmap/phase/security-distribution/PHASE.md` + +## 목표 + +OTO 없이 Jenkins job만 얇게 구성해 AppSok 인증 ZIP을 반복 빌드하고, Jenkins artifact 링크로 바로 다운로드할 수 있게 한다. Jenkins에는 빌드 지식을 최소한으로 두고, 현재 프로젝트의 기존 인증 빌드 스크립트를 실행하는 thin launcher 역할만 부여한다. + +## 상태 + +[계획] + +## 승격 조건 + +- 없음 + +## 구현 잠금 + +- 상태: 잠금 +- 결정 필요: 아래 체크리스트 + - [x] Jenkins job 생성/갱신은 사용자 제공 Jenkins API credential을 사용한다. 원격 SOPS 저장과 Jenkins API 인증 확인을 완료했다. + - [ ] artifact 다운로드를 Jenkins 로그인 사용자에게만 허용할지, 별도 공개 다운로드 경로를 마련할지 결정한다. + - [ ] build trigger를 수동 실행만으로 시작할지, Git push webhook 또는 polling까지 포함할지 결정한다. + - [ ] job 이름과 artifact 보관 개수 기본값을 확정한다. 기본 후보는 `appsok-macos-certified`, 최근 20개 build/artifact 보관이다. + +## 범위 + +- AppSok 전용 Jenkins job 생성 또는 갱신 +- `macbook-ultra` Jenkins node에서 기존 인증 빌드 스크립트 실행 +- 최종 ZIP과 checksum을 Jenkins archived artifact로 노출 +- 최신 성공 빌드 artifact 다운로드 URL 확인 +- Jenkins credential/API token을 tracked 파일에 남기지 않는 운영 절차 + +## 기능 + +### Epic: [job] Jenkins thin job + +Jenkins job은 AppSok 빌드 스크립트를 호출하고 artifact를 보관하는 얇은 launcher로 유지한다. + +- [ ] [job-config] `macbook-ultra` node, AppSok SCM, `main` branch, shell build step을 포함한 Jenkins job config를 만든다. +- [ ] [script-call] job build step이 `./scripts/build-certified-macos.sh`를 호출하고 secret 원문을 job config나 repository에 남기지 않는다. +- [ ] [artifact-copy] `AppSok-certified.zip`과 `sha256` 값을 Jenkins workspace의 archive 대상 경로로 모은다. +- [ ] [archive] Jenkins archived artifact 설정으로 ZIP과 checksum을 보관한다. +- [ ] [retention] build log와 artifact 보관 정책을 AppSok 1차 운영 기준에 맞춘다. + +### Epic: [access] 빌드 실행과 다운로드 확인 + +사용자가 Jenkins에서 빌드를 실행하고 최신 성공 artifact를 확인할 수 있게 한다. + +- [ ] [trigger] 1차 trigger 방식을 적용한다. +- [ ] [download-url] `lastSuccessfulBuild/artifact/.../AppSok-certified.zip` 다운로드 URL을 확인하고 문서화한다. +- [ ] [auth-policy] Jenkins artifact 다운로드 권한 정책을 결정 사항에 맞게 반영한다. +- [ ] [smoke] Jenkins build를 1회 실행하고 archived ZIP과 checksum을 다운로드 가능한 상태로 검증한다. 검증: Jenkins 최신 성공 빌드의 artifact URL이 인증된 사용자 기준으로 ZIP 응답을 반환한다. + +## 완료 리뷰 + +- 상태: 없음 +- 요청일: 없음 +- 완료 근거: 모든 기능 Task와 Task 안에 명시된 검증 충족 후 기록한다. +- 리뷰 필요: + - [ ] 사용자가 완료 결과를 확인했다 + - [ ] archive 이동을 승인했다 +- 리뷰 코멘트: 없음 + +## 범위 제외 + +- OTO runner 또는 OTO Server를 Jenkins 대체 실행 주체로 도입하는 작업 +- workspace 공통 Jenkins 가이드 또는 별도 agent skill 작성 +- Jenkins plugin, Auth Broker, 중앙 감사 서버 구현 +- 공용 Jenkins API token을 앱이나 repository에 내장하는 방식 +- AppSok 앱 코드에서 Jenkins artifact 소비 흐름을 변경하는 작업 + +## 작업 컨텍스트 + +- 관련 경로: `scripts/build-certified-macos.sh`, `docs/macos-certified-build.md`, `agent-roadmap/phase/security-distribution/` +- 표준선(선택): Jenkins는 1차로 thin launcher와 artifact archive만 담당하고, 빌드 절차의 source of truth는 AppSok repository의 스크립트와 문서에 둔다. +- 표준선(선택): Jenkins API token, SSH credential, Apple credential, Keychain password 원문은 tracked 파일, task log, 최종 응답에 기록하지 않는다. +- 선행 작업: macOS 빌드 CI와 인증 빌드 +- 후속 작업: macOS 배포와 sandbox 검증, 향후 OTO 기반 CI/CD 마이그레이션 후보 +- 확인 필요: Jenkins API token 또는 reload 권한, artifact 다운로드 권한 정책, trigger 방식, job 이름과 보관 정책 diff --git a/secrets/appsok.ci.sops.json b/secrets/appsok.ci.sops.json index 46e6353..0a0421a 100644 --- a/secrets/appsok.ci.sops.json +++ b/secrets/appsok.ci.sops.json @@ -1,18 +1,21 @@ { - "keychain_password": "ENC[AES256_GCM,data:dbdUnU08,iv:qiMCxcUuB5cU5dF+If44SlnyV7F8z9QH99xBbAYujUc=,tag:5vJ2wPtgZV1vymi5nlwAsQ==,type:str]", - "apple_id": "ENC[AES256_GCM,data:zQFNWgCLUAcOQLMWCKyNiabOM0rOVcEN,iv:pIAwYUz11v5I3iR3ooP4B2s0ZQO4tbpaC/RDPfZa12A=,tag:X7DK3DGB2EwbSO4bchmWxw==,type:str]", - "app_specific_password": "ENC[AES256_GCM,data:kF14kJqVBb7/R6CdnGWEUgEd1w==,iv:rUsHkTp7ou+xgHRfBT8CvYJ8wQfOCdGa+JdmCtPNjIw=,tag:nT06ha1S6toRommwIbBgxg==,type:str]", - "notary_profile": "ENC[AES256_GCM,data:8GuRGFIMKEGMq+wHgg==,iv:N3Cg0i138Brc3RdUSzX6ZFnUGzxZEcBf8gmUPdayxqU=,tag:Q7cFKyTgWvT29bkW9czabA==,type:str]", - "team_id": "ENC[AES256_GCM,data:vdQ+EGiKNzBfPw==,iv:6/X2mZEv0oFlTUAnXKJpSZuNAELe0Op+zJm9ZITfIVA=,tag:LdBWE9y0sMO5jB/RehNKUA==,type:str]", + "keychain_password": "ENC[AES256_GCM,data:ulrv+g/E,iv:vqS4g+GSZu8E38XvSFuzRzMcuQoTluD7CCdwnRGiHLY=,tag:TWNkSagck75+4FanQQrHTA==,type:str]", + "apple_id": "ENC[AES256_GCM,data:1bDyzRIfG79q5OAo70sRoTf9sOsSW0G8,iv:++cKhT1X5yh7XiZGnOm2Q6iITmdLCV25xT7BTMd5s20=,tag:OLfRMO77wxNddMSWtRmpzA==,type:str]", + "app_specific_password": "ENC[AES256_GCM,data:z4Kv7Bi+2R/USTSZbk6ESkDjUg==,iv:HauauFN7y3z9CeLpvpNr/Id0L0h4SqPhknEtgeDVwVE=,tag:qJgj4OrrAO3zstUMKC86oQ==,type:str]", + "notary_profile": "ENC[AES256_GCM,data:xq5iHMRjxj7yFKsStA==,iv:zsDcxv5cI+hEjoydIsgb1Ud8OFBZ5VJf8n1RrYDFBp4=,tag:DDiv9p58l2N7pXHl/GS9/w==,type:str]", + "team_id": "ENC[AES256_GCM,data:mx4yG0XYctO4HQ==,iv:4UR73U+mOpt+r63NHAjZ59bJxBnL+Bam0br0KEo5wTc=,tag:AdFx8eloqfR1kscwOs3okw==,type:str]", + "jenkins_url": "ENC[AES256_GCM,data:VlqZN3Ot0HbTe5YINvOTiH+i5IFl,iv:3bvQDu+ZhsI4ni3k7cpaZTH3141lwgj0Jobf91yo3NA=,tag:+wXbONTkX7CsCo8+f9XlVQ==,type:str]", + "jenkins_username": "ENC[AES256_GCM,data:OlfHPQ==,iv:E26Sph5O6frAW4s2L5h/zfmidABxew3jNcqkT+wNpoE=,tag:XAwNsbTRPxuacLTbJBLTjA==,type:str]", + "jenkins_api_token": "ENC[AES256_GCM,data:4vX4p3O501E4ew7rzK0YcV3KQAN44ifQ6ImVhqXjma7sGQ==,iv:NmmarZOiKUJynmP7EveClLl1XTbv5bvKa9kiiqdmWGc=,tag:BfCc5pbSXQr25ynTHX35vA==,type:str]", "sops": { "age": [ { - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmOFBGQlcyMlZaS2ltVzNr\nQ0ZqZU43UW11cHQrRGtmT2Y2WS9kLyt2ZUFBCkVaZW5QZTdWeGJob2U0WVR0emJO\ndzY1ZUZTNFRzUFBRcmhvdCttYWp2MUkKLS0tIE9ncGprRXZRQTBzUzIwcjlwR2cx\nNUpQNGZYVk14bm9zMkdnNEg3N3FXcUkKD8r/akXXQ6ZVWiKBA62RlB9kB8mXUgBd\nCreJtxzJG4A+9Xla6UdzJPZYSP3IY5JPQoWUtzc0xXbBHfCSmpWoww==\n-----END AGE ENCRYPTED FILE-----\n", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHckdoaWc5Zkh4eDU3aU42\nUXBqU3BEMzcyT0JiTlJUV0FWdnZZUEZkYmpnCnZHNmFpTEFUOEtOTFFibVdFU2Zi\ncnFRSjRZZms4UXptWXFSa1BzTGVZMkkKLS0tIFhHRGV3aXNDdWF3V0R2MkFTajgr\nSXQzZDFGeDZ3dy9MT0NoMTdMYXJLWUEKahLHUzDvofFCzBY+UOyx1IVb4zBRlwZW\nwEAf2nHNNx4vtEK5m2mlS/8xZrw2LIaXQgazpRwlNVZe/k3mplgEWQ==\n-----END AGE ENCRYPTED FILE-----\n", "recipient": "age1l4fylw9cvw49urz84pa02gjupnpnpz8qp6k0t7225wt8lukkg3cs6qflph" } ], - "lastmodified": "2026-06-14T07:00:35Z", - "mac": "ENC[AES256_GCM,data:UjOl2IcvEm9P+EldqwW5kj3vkUFmW5zylLXgSjZYDKvAZIHHW5JwgerKDqw+90AdV9dl5vIIReXRWiHepQQugkofJmsAddHRhK5DI8Xam9X1cC87S28phAp5iQm1xMF55jF2gsuQ5FVtDZ2W5ZX3epGxhLLUXk2xK+mtyUj9i9w=,iv:d+Gc6HA8VRUjFoNsMjYI3ckqujiMeYjAkhIyEWoMvWk=,tag:jo0sglvMW1P3asBP3giIpA==,type:str]", + "lastmodified": "2026-06-15T04:46:38Z", + "mac": "ENC[AES256_GCM,data:NSNEwOc1vKpG2o0fH35758+2HWPxBcuetOYNWJ3wdXLLzhIYhHjuiDUiacIKX1GO8IHXottrY0wXnKbgaPvYZpFsmCPnweXOTLqbeLRfxymB8Pbt5Hfb3CnNefHdnSuje/b/lW5b2YXHPgAftzfky6IOlQVziDk/pSOAxKvFoY8=,iv:gP8n8M89hc3lP1a61SgoRyb/uMFdJSOMa8bEK0goPrk=,tag:7u3h15fDW+2VNFQ43KltPw==,type:str]", "unencrypted_suffix": "_unencrypted", "version": "3.13.1" }