diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index 457987c5b..6eacbba73 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -6,10 +6,12 @@ # the STRAW_SIGNING_KEYSTORE_B64 secret (the same androiddebugkey the whole # series is signed with — vaulted as "Sulkta — Straw fdroid signing keystore"). # -# Publish path mirrors scripts/publish-fdroid.sh: drop the APK into the Lucy -# fdroid repo + re-sign the index (both via the host docker socket, keystore -# never leaves Lucy), then stream the signed repo to Rackham web168 over a -# scoped, forced-command deploy key (STRAW_FDROID_RACKHAM_KEY). +# Publish is ROOTLESS (#444): the signed APK is handed to a forced-command +# script on the Lucy host (key STRAW_FDROID_LUCY_KEY, pinned to that one script +# via restrict,command=). The host does the fdroid index re-sign + the Rackham +# rsync — the signing keystore never enters this CI container, and this job no +# longer needs the host docker socket. The host script re-verifies the APK +# signer before publishing. name: build-apk on: @@ -102,31 +104,32 @@ jobs: echo "STRAW_VC=$VC" >> "$GITHUB_ENV" echo "STRAW_APK=$NAME" >> "$GITHUB_ENV" - # ---- Publish: only on a real push to main, AFTER the build above passed ---- - - name: Publish to fdroid (Lucy repo + index re-sign, via host docker socket) - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - run: | - set -euo pipefail - FDROID=git.sulkta.com/sulkta-infra/fdroid-sulkta:latest - # Stream the APK into the host-mounted fdroid repo (helper runs as the - # fdroid uid so ownership matches). - docker run --rm -i -u 1000:1000 -v /mnt/cache/appdata/fdroid-sulkta/repo:/repo \ - --entrypoint sh "$FDROID" -c "cat > /repo/${STRAW_APK}" < "$GITHUB_WORKSPACE/${STRAW_APK}" - # Re-sign the index (the index keystore lives only on Lucy). - docker run --rm -u 1000:1000 -v /mnt/cache/appdata/fdroid-sulkta:/repo "$FDROID" update - - - name: Publish to Rackham (rsync signed repo to web168) + # ---- Publish (rootless, no docker socket): hand the signed APK to the + # Lucy host forced-command. The host re-verifies the signer, re-signs + # the fdroid index (keystore stays on Lucy), and rsyncs to Rackham. ---- + - name: Publish to fdroid via Lucy host forced-command if: github.event_name == 'push' && github.ref == 'refs/heads/main' env: - RACKHAM_KEY: ${{ secrets.STRAW_FDROID_RACKHAM_KEY }} + LUCY_KEY: ${{ secrets.STRAW_FDROID_LUCY_KEY }} run: | set -euo pipefail - mkdir -p ~/.ssh && echo "$RACKHAM_KEY" > ~/.ssh/id_deploy && chmod 600 ~/.ssh/id_deploy - ssh-keyscan -H 142.44.213.229 >> ~/.ssh/known_hosts 2>/dev/null - FDROID=git.sulkta.com/sulkta-infra/fdroid-sulkta:latest - # tar the signed repo out of the host-mounted volume and stream it to - # the forced-command deploy key on Rackham (which untars + sudo-rsyncs). - docker run --rm -u 1000:1000 -v /mnt/cache/appdata/fdroid-sulkta:/src \ - --entrypoint sh "$FDROID" -c 'tar c -C /src repo' \ - | ssh -i ~/.ssh/id_deploy kayos@142.44.213.229 - echo "Published vc=${STRAW_VC} to fdroid.sulkta.com" + install -d -m700 "$HOME/.ssh" + printf '%s\n' "$LUCY_KEY" > "$HOME/.ssh/id_lucy" + chmod 600 "$HOME/.ssh/id_lucy" + # Pin the Lucy host key (no TOFU). + printf '%s\n' '192.168.0.5 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIk1uDXaIz6MC3f5IymRHQ9B/azLx4XRrkTNb2F/xRP4' \ + > "$HOME/.ssh/known_hosts_lucy" + # Hand off the signed APK on stdin. Host exit codes: 0 = published, + # 3 = already published (benign no-op on a same-commit re-run), + # anything else = real failure. + set +e + ssh -i "$HOME/.ssh/id_lucy" -o IdentitiesOnly=yes -o BatchMode=yes \ + -o StrictHostKeyChecking=yes -o UserKnownHostsFile="$HOME/.ssh/known_hosts_lucy" \ + root@192.168.0.5 "publish ${STRAW_APK}" < "$GITHUB_WORKSPACE/${STRAW_APK}" + rc=$? + set -e + case "$rc" in + 0) echo "Published vc=${STRAW_VC} to fdroid.sulkta.com (rootless — no docker socket)";; + 3) echo "vc=${STRAW_VC} already published — nothing to do";; + *) echo "::error::fdroid publish failed (rc=$rc)"; exit "$rc";; + esac