Relay: its own image, binaries and signatures at release (RL-31) #271

Merged
jcoffey-dev merged 2 commits from relay/packaging into main 2026-10-31 00:43:34 +00:00
Owner

Step 6, first part: inbuxa Relay ships as its own image and binaries, and every release's relay binaries are signed (RL-31, RL-29).

Image (Dockerfile.relay)

  • inbuxa-relay alone, built on Alpine as a static musl binary, natively for each architecture.
  • FROM scratch: the binary, the CA roots and /var/lib/inbuxa-relay. 12.4 MB on amd64.
  • Runs as uid 2001 with cap_net_bind_service only, and sets INBUXA_RELAY_CONTAINER, so it reports selfUpgrade: false.
  • Checked locally: the container joined from INBUXA_RELAY_JOIN_TOKEN and listened on 443 as 2001 with CapEff 0x400. It holds no server binary.

Release workflows

  • GitHub (ci.yml, the builder today):
    • relay-publish builds per architecture on native runners.
    • relay-index checks that the amd64 image prints inbuxa-relay <Cargo version> and holds no inbuxa file (RL-T24), then moves the server's tags.
    • binaries also takes out inbuxa-relay-x86_64-linux-musl and inbuxa-relay-aarch64-linux-musl, adds them to SHA256SUMS, and attaches them.
    • report waits for the relay jobs.
  • Gitea (publish.yml, the fallback): the same jobs. publish-relay builds both platforms after the server's arm64 build; arm64 compiles under QEMU there.
  • relay-sign, on Gitea whichever forge builds:
    • It downloads the two binaries from the release and signs each with the RELAY_RELEASE_KEY secret (sign-relay.sh, Cargo version, the target from the file name). It attaches .sig files, replacing any old ones.
    • The key exists as a file only while signing, then it's removed.
    • Without the secret it warns and signs nothing, so a release never fails over it.
  • Both release notes mention the relay image, the binaries and the .sig files.

Keys (tools/release/new-relay-keys.sh DIR)

  • It makes ci.pem, which goes into the secret, and offline.pem, a backup to keep off any server, and prints only their public halves.
  • Made on 2026-10-30. Their public halves are in RELEASE_KEYS (second commit), with a test that pins the bytes to the printed strings. From this build on, relays take signed upgrades.
  • Rerun after adding them: relay crate (28 tests), relay-proto release tests, and relay_tests, whose "signed by no release key" case now hits a bad signature instead of a missing key.

Host install (resources/systemd/inbuxa-relay.service)

  • Runs /opt/inbuxa-relay/bin/inbuxa-relay run. That directory is owned by the relay's user so an upgrade can replace the binary.
  • Restart=always, so it restarts after the upgrade exit (75).
  • Only CAP_NET_BIND_SERVICE, with ProtectSystem=strict and only that directory and the state directory writable, plus the usual hardening. Passes systemd-analyze verify; its only warning is that the binary isn't installed on this machine.

Trust note: with BUILD_ON=github, GitHub's run uploads the binaries and Gitea signs what it finds. A signature means "this release's build", which includes trusting the GitHub build.

Not tested end to end: the workflow jobs only run on a tag. The YAML parses, and every command in them was run by hand against a local image. The first release candidate after this merges is the real test; I'd watch it.

Still to come in step 6: the installer path, and the docs page with the WireGuard setup (RL-32).

Step 6, first part: inbuxa Relay ships as its own image and binaries, and every release's relay binaries are signed (RL-31, RL-29). **Image** (`Dockerfile.relay`) - `inbuxa-relay` alone, built on Alpine as a static musl binary, natively for each architecture. - `FROM scratch`: the binary, the CA roots and `/var/lib/inbuxa-relay`. 12.4 MB on amd64. - Runs as uid 2001 with `cap_net_bind_service` only, and sets `INBUXA_RELAY_CONTAINER`, so it reports `selfUpgrade: false`. - Checked locally: the container joined from `INBUXA_RELAY_JOIN_TOKEN` and listened on 443 as 2001 with CapEff `0x400`. It holds no server binary. **Release workflows** - **GitHub (`ci.yml`, the builder today):** - `relay-publish` builds per architecture on native runners. - `relay-index` checks that the amd64 image prints `inbuxa-relay <Cargo version>` and holds no `inbuxa` file (RL-T24), then moves the server's tags. - `binaries` also takes out `inbuxa-relay-x86_64-linux-musl` and `inbuxa-relay-aarch64-linux-musl`, adds them to `SHA256SUMS`, and attaches them. - `report` waits for the relay jobs. - **Gitea (`publish.yml`, the fallback):** the same jobs. `publish-relay` builds both platforms after the server's arm64 build; arm64 compiles under QEMU there. - **`relay-sign`, on Gitea whichever forge builds:** - It downloads the two binaries from the release and signs each with the `RELAY_RELEASE_KEY` secret (`sign-relay.sh`, Cargo version, the target from the file name). It attaches `.sig` files, replacing any old ones. - The key exists as a file only while signing, then it's removed. - Without the secret it warns and signs nothing, so a release never fails over it. - Both release notes mention the relay image, the binaries and the `.sig` files. **Keys** (`tools/release/new-relay-keys.sh DIR`) - It makes `ci.pem`, which goes into the secret, and `offline.pem`, a backup to keep off any server, and prints only their public halves. - Made on 2026-10-30. Their public halves are in `RELEASE_KEYS` (second commit), with a test that pins the bytes to the printed strings. From this build on, relays take signed upgrades. - Rerun after adding them: relay crate (28 tests), relay-proto release tests, and `relay_tests`, whose "signed by no release key" case now hits a bad signature instead of a missing key. **Host install** (`resources/systemd/inbuxa-relay.service`) - Runs `/opt/inbuxa-relay/bin/inbuxa-relay run`. That directory is owned by the relay's user so an upgrade can replace the binary. - `Restart=always`, so it restarts after the upgrade exit (75). - Only `CAP_NET_BIND_SERVICE`, with `ProtectSystem=strict` and only that directory and the state directory writable, plus the usual hardening. Passes `systemd-analyze verify`; its only warning is that the binary isn't installed on this machine. **Trust note:** with `BUILD_ON=github`, GitHub's run uploads the binaries and Gitea signs what it finds. A signature means "this release's build", which includes trusting the GitHub build. **Not tested end to end:** the workflow jobs only run on a tag. The YAML parses, and every command in them was run by hand against a local image. The first release candidate after this merges is the real test; I'd watch it. **Still to come in step 6:** the installer path, and the docs page with the WireGuard setup (RL-32).
jcoffey-dev added 1 commit 2026-10-31 00:10:48 +00:00
Relay: its own image, binaries and signatures at release (RL-31)
ci / fork-checks (pull_request) Skipped
ci / build (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 10m6s
e11fdae372
Dockerfile.relay builds inbuxa-relay alone, as a static musl binary, into
a FROM-scratch image of about 12 MB: the binary, the CA roots and a state
directory, run as uid 2001 with cap_net_bind_service only, and
INBUXA_RELAY_CONTAINER set so the relay doesn't upgrade itself.

A release tag builds it on whichever forge builds, under the server's
tags, after checking that the image prints this version and holds no
server binary (RL-T24). The release gets the two binaries, taken out of
the image, in SHA256SUMS with the server's. Gitea's relay-sign job then
signs them with the RELAY_RELEASE_KEY secret and attaches the .sig files;
without the secret it warns and signs nothing.

tools/release/new-relay-keys.sh makes the CI key and an offline backup
key and prints only their public halves. resources/systemd has the unit
for a host install: binary in /opt/inbuxa-relay/bin, owned by the relay's
user so upgrades can replace it, Restart=always, sandboxed.
jcoffey-dev added 1 commit 2026-10-31 00:32:02 +00:00
Relay: the inbuxa release keys
ci / fork-checks (pull_request) Skipped
ci / build (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 10m25s
cbf6bd668a
The CI key, which the RELAY_RELEASE_KEY secret holds, and the offline
key, as tools/release/new-relay-keys.sh printed their public halves. A
test checks the bytes against those strings.
jcoffey-dev merged commit 392f17abe9 into main 2026-10-31 00:43:34 +00:00
jcoffey-dev deleted branch relay/packaging 2026-10-31 00:43:34 +00:00
Sign in to join this conversation.