ci: rootless fdroid publish via Lucy host forced-command (drop docker socket) [#444]
All checks were successful
build-apk / build-and-publish (push) Successful in 7m5s
gitleaks / scan (push) Successful in 50s

Replaces the 2 host-docker-socket publish steps with one SSH handoff to a
shell-denied forced-command on Lucy that re-verifies the APK signer, re-signs
the fdroid index (keystore stays on the host), and rsyncs to Rackham. Audited
4 script rounds + 1 whole-flow round.
This commit is contained in:
Cobb 2026-06-26 17:13:28 -07:00
parent 1e89c0739a
commit e11cc6a854

View file

@ -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