diff --git a/.github/workflows/dev-beta-release.yaml b/.github/workflows/dev-beta-release.yaml index 6866df1b..b72e7d17 100644 --- a/.github/workflows/dev-beta-release.yaml +++ b/.github/workflows/dev-beta-release.yaml @@ -362,12 +362,12 @@ jobs: - name: Sync zuey ops scripts to VPS env: - ZUEY_SSH_PRIVATE_KEY: ${{ secrets.ZUEY_SSH_PRIVATE_KEY }} + ZUEY_SSH_PRIVATE_KEY_B64: ${{ secrets.ZUEY_SSH_PRIVATE_KEY_B64 }} ZUEY_SUDO_PASS: ${{ secrets.ZUEY_SUDO_PASS }} run: | set -euo pipefail - if [[ -z "${ZUEY_SSH_PRIVATE_KEY:-}" || -z "${ZUEY_SUDO_PASS:-}" ]]; then - echo "::warning::ZUEY_SSH_PRIVATE_KEY or ZUEY_SUDO_PASS not configured; skipping VPS script sync. Configure both repository secrets to keep /usr/local/bin/goclaw-deploy and /usr/local/bin/goclaw-upgrade-release in sync with the repo on every beta deploy." + if [[ -z "${ZUEY_SSH_PRIVATE_KEY_B64:-}" || -z "${ZUEY_SUDO_PASS:-}" ]]; then + echo "::warning::ZUEY_SSH_PRIVATE_KEY_B64 or ZUEY_SUDO_PASS not configured; skipping VPS script sync. Store the private key as base64 (\`base64 -w0 < /path/to/key\`) in the ZUEY_SSH_PRIVATE_KEY_B64 secret to keep /usr/local/bin/goclaw-deploy and /usr/local/bin/goclaw-upgrade-release in sync with the repo on every beta deploy." exit 0 fi @@ -377,10 +377,22 @@ jobs: bash -n "$f" || { echo "::error::$f has syntax error"; exit 1; } done - # Stage SSH key (BatchMode + StrictHostKeyChecking against known_hosts) + # Stage SSH key: base64-decode, write with 0600, validate format. + # Why base64: GitHub Secrets storage round-trips can normalize/strip + # newlines in PEM blocks, producing `error in libcrypto` on load. + # Base64 is a single line, immune to whitespace mangling. install -m 700 -d ~/.ssh - printf '%s\n' "$ZUEY_SSH_PRIVATE_KEY" > ~/.ssh/id_zuey - chmod 0600 ~/.ssh/id_zuey + # Create the key file with a tight umask so it is born at 0600 — + # avoids the brief 0644 window between `>` redirect and `chmod`. + if ! ( umask 077 && printf %s "$ZUEY_SSH_PRIVATE_KEY_B64" | base64 -d > ~/.ssh/id_zuey ) 2>/dev/null; then + echo "::error::ZUEY_SSH_PRIVATE_KEY_B64 is not valid base64. Regenerate with: base64 -w0 < /path/to/private_key" + exit 1 + fi + if ! ssh-keygen -y -f ~/.ssh/id_zuey >/dev/null 2>&1; then + echo "::error::Decoded SSH key is not a valid OpenSSH/PEM private key. Verify the source file is a real private key and re-encode." + rm -f ~/.ssh/id_zuey + exit 1 + fi ssh-keyscan -p "$ZUEY_SSH_PORT" -H "$ZUEY_SSH_HOST" >> ~/.ssh/known_hosts 2>/dev/null SSH_BASE=(-i ~/.ssh/id_zuey -o BatchMode=yes -o StrictHostKeyChecking=yes -o ConnectTimeout=15) diff --git a/docs/deployment-guide.md b/docs/deployment-guide.md index 6ed63241..ecfacde3 100644 --- a/docs/deployment-guide.md +++ b/docs/deployment-guide.md @@ -290,7 +290,7 @@ The `Sync zuey ops scripts to VPS` step in `dev-beta-release.yaml` runs `scp + s | Secret | Purpose | |---|---| -| `ZUEY_SSH_PRIVATE_KEY` | CI-only ed25519/rsa key; its public key must be appended to `zuey@82.197.71.246:~/.ssh/authorized_keys`. **Do not reuse the operator's personal key.** | +| `ZUEY_SSH_PRIVATE_KEY_B64` | CI-only ed25519/rsa key, **base64-encoded as a single line** (`base64 -w0 < /path/to/key`). Its public key must be appended to `zuey@82.197.71.246:~/.ssh/authorized_keys`. **Do not reuse the operator's personal key.** Base64 avoids the `error in libcrypto` failure caused by GitHub Secrets normalizing newlines inside multi-line PEM blocks. | | `ZUEY_SUDO_PASS` | Same value as `ZUEY_GOCLAW_SUDO_PASS` in the operator's local `.env`; used by `sudo -S` over the SSH session to install scripts. | Optional repository **variables** (override defaults if the VPS endpoint changes): @@ -306,7 +306,10 @@ To rotate the CI SSH key: ```bash ssh-keygen -t ed25519 -f /tmp/ci-zuey-key -N '' -C 'gh-actions-deploy-zuey-beta' # add /tmp/ci-zuey-key.pub to zuey:~/.ssh/authorized_keys (consider restricting to scp+install via `command="..."` forced-command) -# paste contents of /tmp/ci-zuey-key into the ZUEY_SSH_PRIVATE_KEY secret +# base64-encode the private key on a single line, then paste into the +# ZUEY_SSH_PRIVATE_KEY_B64 secret: +base64 -w0 < /tmp/ci-zuey-key # macOS: `base64 -i /tmp/ci-zuey-key | tr -d '\n'` +# (copy the single-line output and paste it into the secret value) # then `shred -u /tmp/ci-zuey-key*` locally ``` diff --git a/docs/project-changelog.md b/docs/project-changelog.md index 9cc2b8b1..549c34e4 100644 --- a/docs/project-changelog.md +++ b/docs/project-changelog.md @@ -16,9 +16,13 @@ Significant changes, features, and fixes in reverse chronological order. - Moved `scripts/goclaw-upgrade-release.sh` → `scripts/zuey/goclaw-upgrade-release.sh`. - Added `scripts/zuey/goclaw-deploy.sh` (canonical source for the on-host `/usr/local/bin/goclaw-deploy`). -- Wired `Sync zuey ops scripts to VPS` step in `.github/workflows/dev-beta-release.yaml` to `scp + sudo install` both scripts before triggering the gateway upgrade endpoint on every beta release. Requires new repository secrets `ZUEY_SSH_PRIVATE_KEY` and `ZUEY_SUDO_PASS`; step skips with a warning if either is unset. +- Wired `Sync zuey ops scripts to VPS` step in `.github/workflows/dev-beta-release.yaml` to `scp + sudo install` both scripts before triggering the gateway upgrade endpoint on every beta release. Requires new repository secrets `ZUEY_SSH_PRIVATE_KEY_B64` (base64-encoded private key, single line) and `ZUEY_SUDO_PASS`; step skips with a warning if either is unset. - Updated `docs/deployment-guide.md` with the self-loop guard rationale, manual sync recipe, and required-secrets table. +**Followup fix (run 26499549166)** + +- First real `deploy_zuey_beta` run on `dev` failed with `Load key: error in libcrypto` because GitHub Secrets storage normalized newlines inside the multi-line PEM block. Switched the secret to base64 (`ZUEY_SSH_PRIVATE_KEY_B64`) and added pre-flight validation (`ssh-keygen -y -f`) that fails fast with a remediation hint if the decoded key is malformed. + --- ## 2026-05-24