Skip to content

Dev images: SSH over the USB cable only - #2

Merged
kwsantiago merged 5 commits into
mainfrom
dev-usb-ssh
Oct 19, 2026
Merged

kwsantiago merged 5 commits into
mainfrom
dev-usb-ssh

Conversation

@kwsantiago

@kwsantiago kwsantiago commented Oct 19, 2026 •

Copy link
Copy Markdown

A dev image (MOARCHY_SSH_KEY=<pubkey> ./scripts/build-image.sh) now gets SSH over the USB cable only. Before, the key enabled sshd on every interface, debug images turned mDNS back on to advertise it, and the Wi-Fi preseed enabled sshd too.

Change

  • image/configure.sh: with MOARCHY_SSH_KEY, installs authorized_keys and moarchy-usbnet, a configfs NCM gadget that puts the phone at 10.66.0.2/30 with fixed MAC addresses so the PC side can be a static 10.66.0.1 profile. The gadget script is safe to rerun after any partial failure, refuses an interface not named usb0, and turns IPv6 off on the link so the USB host cannot become the phone's router. sshd requires that unit, listens on 10.66.0.2 only, and admits moarchy@10.66.0.1 only. Because Linux accepts a packet for any local address on any interface, sshd's cgroup also passes only usb0 traffic to and from 10.66.0.1 (RestrictNetworkInterfaces=usb0, IPAddressDeny=any, IPAddressAllow=10.66.0.1). systemd starts a unit unfiltered when it cannot load those BPF programs, so an ExecStartPre check requires a loopback connect from sshd's cgroup to time out before sshd binds anything. NetworkManager leaves the link alone, matched by its fixed MAC. The Wi-Fi preseed no longer enables sshd, and the debug mDNS re-enable is gone.
  • image/verify.sh: release images must have no SSH key, sshd off and none of the five USB link files. DEV_IMAGE=1 instead requires a public-key-only authorized_keys, sshd enabled, all five files, and sshd's drop-in byte for byte as configure.sh writes it. It fails on anything else that could reset or widen the filter: another sshd.service drop-in, a replacement sshd.service, a system.control drop-in, type-wide service.d or slice drop-ins naming the filter settings, or an enabled ssh socket unit. It also requires IPv6 off and NetworkManager's unmanaged rule. It checks sshd's effective configuration with sshd -T in the image, as seen from 10.66.0.1 and from a LAN address, so keyword=value, quoting, Include and Match are all accounted for: ListenAddress 10.66.0.2:22 only, AllowUsers moarchy@10.66.0.1 only, no password auth. A dev run ends with a NOT PUBLISHABLE line. scripts/verify-image.sh passes DEV_IMAGE through.
  • image/negative-test.sh: a twelfth plant, the USB link's sshd drop-in in a release image. With DEV_IMAGE=1 it plants ten dev-image defects instead: a second ListenAddress=0.0.0.0, a Match block opening sshd to the LAN, the filter widened to wlan0, an sshd.service drop-in resetting it, a type-wide drop-in resetting it, a slice drop-in with IPAddressAllow=any, a replacement sshd.service, IPv6 left on, NetworkManager allowed to manage the link, and a socket-activated sshd.
  • .github/workflows/image.yml: builds a dev image with a throwaway key, runs DEV_IMAGE=1 verify and the dev negative test on it. It is never uploaded.
  • Plugin install-on-device.sh scripts default to moarchy@10.66.0.2. docs/structure.md I5 and I6 are updated, and moarchy goes to pkgrel 10.

Testing

Built in an isolated VM. The release image passed verify and the negative test caught all 12 plants. The dev image passed DEV_IMAGE=1 verify and the dev negative test caught all 13 plants. Both verifies pass apart from the online resize2fs check, which needs a native arm64 kernel and runs in CI.

Flashed to a Pixel 3a. SSH over the cable worked on first boot with no setup, and the host key fingerprint matched the one shown on the device. sshd started after its pre-start filter check timed out, as it should with the filters loaded, and systemd reports the three filter settings on the unit. ss shows sshd only on 10.66.0.2:22, nothing listens on 5353 or 5355, usb0 has IPv6 disabled and is unmanaged, and no units failed. On an earlier build with the same filter, Wi-Fi peers could not reach sshd with either filter alone. The pre-start check failing closed when the filters are absent was not exercised on the device.

@kwsantiago
kwsantiago merged commit 67cfdb2 into main Oct 19, 2026
1 check passed
@kwsantiago
kwsantiago deleted the dev-usb-ssh branch October 21, 2026 03:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant