RTFM · Publish
proxyto: a PROXY-protocol front-end
saphira-proxyto 0.1.0-r4 is a purpose-built single-file C service that sits in front of backends which do not understand PROXY protocol themselves. Its first client is the public Saphira Gopher service on TCP port 70, running at around 700 KiB RSS.
What proxyto is
Some services never learned to read the PROXY protocol header that a proxy uses to pass along the real client address. Rather than patching each such service, proxyto accepts the proxied connection, consumes the header itself, and relays plain bytes to the backend. The backend sees ordinary traffic; the client address is preserved at the edge instead of being replaced by the proxy address.
- Speaks PROXY v1 and v2, including auth TLVs.
- Validates the trusted-peer source before trusting a header.
- Relays bytes with zero leakage, verified by byte-identity testing.
- Fork-per-connection server with immediate in-handler child reaping.
Least privilege by construction
proxyto starts as root only so it can bind privileged ports such as TCP 70, then permanently drops to its dedicated package-owned identity, proxyto user and group 19. The drop is verified with strace during inspection:
getuid() = 0
setgid(19) = 0
setuid(19) = 0
getuid() = 19
getgid() = 19
setuid(0) = -1 EPERM
The final line is the point: after dropping, the process cannot regain root. The rules around that moment are fail-closed:
- Any privilege-drop failure is fatal; the daemon exits rather than continuing privileged.
- It never runs as root past startup, and never as a backend account.
- It refuses to start if its account is missing; the identity comes from the canonical APK-owned accounts fragment.
Run it on OpenRC and systemd
proxyto ships both init systems out of the box. OpenRC gets a supervise-daemon init script that is instance-parameterised through /etc/conf.d/proxyto, so one package can front several backends by naming instances. A systemd unit covers Saphira-d.
Instance configuration lives under /etc/saphira/proxyto, the package ships its man page as proxyto.1, and the Unix identity is the canonical proxyto user and group 19.
The systemd unit deliberately carries no User= directive. Privileged-port instances must start as root to bind, and the daemon's own irreversible self-drop guarantees the running process is proxyto:19 either way. The unit says so in a comment, so the missing directive reads as a decision rather than an omission.
An evidence-backed fix
Routine inspection of the live Gopher service found an exited proxyto child lingering as a zombie while the idle parent waited for its next connection. The cause was a SIGCHLD handler installed with SA_RESTART combined with deferred reaping: accept() resumed instead of returning to the main loop, so the reap waited for the next connection. The fix moves reaping into the handler itself with an async-signal-safe waitpid loop, and a regression test now asserts zero zombies while idle. The pre-fix binary fails that test; the fixed binary passes it.
Footprint and test suite
The live daemon serves Gopher normally at around 700 KiB RSS, which fits the Saphira minimal-host story: a small edge service should stay small. The recipe ships its full test suite rather than asking you to trust that:
- PROXY v1 and v2 protocol vectors.
- Byte-identity relay checks.
- Five-client concurrency.
- Configuration knobs.
- Privilege-drop refusal without the account.
- Post-drop UID and GID verification.
- Idle zombie reaping.