From 278cbf4fb4fb0bd6d2d933dc24d53527c8f0d62b Mon Sep 17 00:00:00 2001 From: "E. Kaparulin" Date: Sat, 10 Oct 2026 22:58:19 +0300 Subject: [PATCH] docs: add a server quickstart and disclose the UDP relay provisioning Same audit as konduit-public (c61664c), applied to koridor. The conclusion is the opposite: there is no server binary and no config format, so nothing can be documented wrongly. The source backs the README - ffi/src/ssh.rs:389 uses per-flow channel_open_direct_tcpip, with no streamlocal, remote TUN or forwarded-tcpip anywhere in the crate. No contradictions found; the gap was thinness. Added docs/server-quickstart.md. The whole server story was previously one clause in the README: - The real requirement (AllowTcpForwarding yes), and that it is already the OpenSSH default, so most servers need no configuration at all. - What you do NOT need: ip_forward, masquerade rules, /dev/net/tun, root. Anyone who has set up a VPN server before assumes all four are required. - Keypair generation, since the user supplies the key; koridor does not generate one for the tunnel. - Host key trust on first connect. The connect is refused until the fingerprint is accepted, and a later change is fatal rather than retried. The finding that justified the work: UDP forwarding provisions itself on the user's server on first enable - it writes ~/.koridor/udp-relay.sh, appends a forced-command line to ~/.ssh/authorized_keys marked # koridor-udp-relay, and requires socat (hardcoded in ffi/src/udp_relay/script.rs:12). So "No special server-side software required" is true for TCP and false once that toggle is on. Nothing public said so. It is off by default and idempotent, and remove_marked_line strips only koridor's own line, leaving every other key untouched - but a user's authorized_keys and home directory are modified without them being told. The README now carries that caveat inline. #k-ops/#6/#212 --- README.md | 7 +- docs/server-quickstart.md | 168 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 docs/server-quickstart.md diff --git a/README.md b/README.md index 2172d4b..e0afc45 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Most VPN apps ask you to trust the app vendor's servers. koridor doesn't have an ## Features - **Full-device tunnel** — a TUN-based VPN, not just an app-aware SOCKS proxy. Every app on the device is covered, with no per-app setup. -- **Bring your own server** — connects to any SSH server with `AllowTcpForwarding yes`. No special server-side software required. +- **Bring your own server** — connects to any SSH server with `AllowTcpForwarding yes`. No special server-side software required. (One opt-in exception: UDP port forwarding provisions a relay script and an `authorized_keys` line on your server — see the [Server Quickstart](docs/server-quickstart.md#6-optional-udp-port-forwarding).) - **Simple onboarding** — set up a connection profile, toggle on, done. - **Fail-closed reconnects** — transient drops are retried with exponential backoff; if the tunnel can't recover, the route is torn down rather than silently leaking traffic in the open. - **Tunnel-only DNS** — lookups are resolved through the server's own resolver over the tunnel, never leaked to a third-party DNS provider. @@ -47,6 +47,11 @@ Releases are published on the [Releases](../../releases) page. Coming soon. +## Setup + +- [Server Quickstart](docs/server-quickstart.md) — what your SSH server needs, the + keypair, host-key trust, and what UDP forwarding provisions on your box + ## Architecture ``` diff --git a/docs/server-quickstart.md b/docs/server-quickstart.md new file mode 100644 index 0000000..3e06eb3 --- /dev/null +++ b/docs/server-quickstart.md @@ -0,0 +1,168 @@ +# Running Your Own koridor Server + +koridor has no server component. It is a client that speaks standard SSH to a +server you already operate — a VPS, a NAS, a home box, anything running `sshd`. +There is nothing to install on the far end and no koridor-operated backend. + +This is the whole server story for normal TCP traffic. One optional feature — +UDP port forwarding — does provision something on your server, and it is +covered separately in step 6 because it changes your `authorized_keys`. + +## 1. What you need + +- An SSH server you control, reachable from wherever the device will be. +- An SSH keypair. **You supply the key** — koridor does not generate one for the + tunnel, so create it first. + +```bash +ssh-keygen -t ed25519 -f ~/.ssh/koridor_key +``` + +The private key is what you paste into the app. It can have a passphrase; koridor +prompts for it and keeps it in the OS credential store. + +## 2. Server requirements (TCP) + +`AllowTcpForwarding yes` — and that is the entire requirement. + +It is **on by default** in OpenSSH, so most servers need no configuration at +all. Check what yours is actually set to: + +```bash +sudo sshd -T | grep -i allowtcpforwarding +``` + +If it says `no`, enable it in `/etc/ssh/sshd_config`: + +``` +AllowTcpForwarding yes +``` + +```bash +sudo sshd -t && sudo systemctl reload sshd +``` + +`sshd -t` validates the config before the reload — run it, or a typo takes SSH +out remotely. + +### What you do *not* need + +This trips up people who have set up a VPN server before. None of the following +applies to koridor: + +- **No `net.ipv4.ip_forward`.** koridor does not forward packets through the + server; `sshd` opens each outgoing connection itself. +- **No iptables or nftables masquerade rules.** Each TCP flow is an ordinary + SSH `direct-tcpip` channel, so the server makes its own outbound connections + and the kernel never routes between interfaces. +- **No `/dev/net/tun` and no TUN interface on the server.** The TUN device lives + on the client only. +- **No root.** A normal unprivileged SSH user is enough. +- **No konduit-style provisioning, bootstrap, or peer list.** There is no + control API and nothing to reload. + +The cost on the server is one SSH session per active TCP flow. A busy browser +tab set will show more `sshd` sessions than you might expect — that is expected, +not a leak. + +## 3. Add the profile + +In the app, create a connection profile: + +| Field | Value | +|---|---| +| Host | Your server's hostname or IP | +| Port | SSH port, `22` unless you changed it | +| Username | The SSH user whose key you installed | +| Private key | Contents of the `.pub`-less private key file (PEM) | +| Passphrase | Only if the key has one | + +The host may be a `~/.ssh/config` alias — koridor resolves it and pins the host +key against whatever the alias resolves to, not the alias name. + +## 4. Trust the host key + +The first connection to a new server does **not** connect. koridor shows you the +host key fingerprint and waits for you to accept it. This is deliberate: the key +is established once, on your say-so, and never silently assumed. + +Compare the fingerprint against the one SSH already knows: + +```bash +ssh-keygen -lf /etc/ssh/ssh_ed25519_key.pub +``` + +If the server is remote, compare against the fingerprint `ssh` shows you on +first connect from a machine you trust. + +Once pinned, koridor checks the key on every connection. If it later changes, +koridor stops and tells you — it does not retry or fall back, because a changed +host key means you are no longer talking to the same machine. + +## 5. Connect + +Toggle the profile on. Traffic routes through the tunnel, and DNS lookups go +through the server's own resolver over the tunnel rather than to a third-party +resolver. + +If the tunnel cannot recover after a drop, koridor tears the route down instead +of leaving traffic to leak in the open. That is the intended behaviour, not a +bug. + +Transient failures retry with exponential backoff up to roughly two minutes. +Failures that retrying cannot fix — a private key that will not decode, a wrong +passphrase, a rejected public key — fail immediately instead of sitting out that +schedule. + +## 6. Optional: UDP port forwarding + +**Off by default.** SSH has no UDP channel type, so koridor relays specific +allow-listed UDP ports through a second, restricted SSH connection. + +Enabling it **provisions itself on your server**, automatically, the first time +you turn it on: + +- Installs a managed script at `~/.koridor/udp-relay.sh` for the SSH user. +- Adds one line to that user's `~/.ssh/authorized_keys`, marked + `# koridor-udp-relay`, restricted with a forced command so the key can only + run that relay. +- Requires **`socat`** on the server. + +```bash +sudo apt install socat # or: dnf install socat +``` + +Both changes are idempotent and reversible — clearing the allow-list removes the +`authorized_keys` line, leaving every other key in the file untouched. Every +other line in `authorized_keys` is preserved across re-syncs. + +Ports are gated by destination port only, not destination IP: any host on an +allow-listed port can be relayed to. Datagrams are piped through `socat` rather +than framed, so two datagrams written back to back are not guaranteed to arrive +as two separate reads — fine for WireGuard and similar retrying protocols, not +fine as a general UDP transport. + +The main use case is running another UDP-based tool, most often a second VPN +like WireGuard, through koridor. + +## 7. Optional: TCP local forwards + +For per-app routing without the TUN, a profile can carry classic SSH local +forward rules in `LOCAL_PORT:IP:PORT` form. These are ordinary `-L` forwards and +need nothing extra on the server. + +## Troubleshooting + +| Symptom | Cause | +|---|---| +| Refuses to connect, no fingerprint shown | A profile with no pinned host key. Accept the fingerprint on first connect. | +| "host key changed" error | The server's key really changed — a rebuild, or you are not talking to the same host. Investigate before accepting the new one; koridor will not retry. | +| Connects, but no traffic | Check `AllowTcpForwarding` with `sshd -T`. | +| Works with `ssh`, fails in the app | Usually the key. koridor wants the private key contents, not a path, and the key must be PEM. | +| Sits retrying for two minutes then fails | A fatal error being treated as transient: bad key, wrong passphrase, or rejected public key. These normally fail at once — if yours does not, the error text is the useful part. | +| Second VPN (WireGuard) comes up but passes no traffic | WireGuard is UDP and UDP forwarding is off. See step 6. | +| UDP forwarding fails at setup | `socat` missing on the server, or the SSH user cannot write to its own `~/.ssh/`. | + +Logs follow the usual pattern: connection failures retry on a backoff schedule, +and anything that retrying cannot fix is reported immediately rather than +silently repeated.