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
6.7 KiB
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.
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:
sudo sshd -T | grep -i allowtcpforwarding
If it says no, enable it in /etc/ssh/sshd_config:
AllowTcpForwarding yes
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;sshdopens each outgoing connection itself. - No iptables or nftables masquerade rules. Each TCP flow is an ordinary
SSH
direct-tcpipchannel, so the server makes its own outbound connections and the kernel never routes between interfaces. - No
/dev/net/tunand 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:
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.shfor 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
socaton the server.
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.