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
This commit is contained in:
168
docs/server-quickstart.md
Normal file
168
docs/server-quickstart.md
Normal file
@@ -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.
|
||||
Reference in New Issue
Block a user