Audit of the public repo against the actual server and konduit-ctl source. The happy path was documented; the failure modes were not. - server.toml had no reference at all. Add docs/server-config.md covering every key, [tls], the control API, and key handling. - The mantra could not recover the identity key on its own: derive_from_passphrase needs the [kdf] salt, which is 32 random bytes generated once at bootstrap. bootstrap says so in the file it generates (core/src/config.rs:403); the quickstart claimed the opposite. Fixed, with a backup step added before it is needed rather than after. - konduit-server never appeared in a download step, yet konduit-server.service expects /opt/konduit/konduit-server - a path no instruction created. Added the install section and moved setcap after the copy, since capabilities do not survive every cross-filesystem copy and a binary that silently lost cap_net_bind_service fails at bind time. - Hot config reload was a headline feature with no documented mechanism. It is a control API on 127.0.0.1:3001, and add-client only edits the file on disk, so a new peer is not live until something reloads it. Documented, including that /reload does not disconnect revoked peers but /sync does. - [tls] native termination was entirely undocumented; the key must be PKCS#8. - Dropped a README claim about a konduit-admin-ui unit that does not exist, and fixed a systemd copy path that only resolved if this repo had been cloned to /opt/konduit. #k-ops/#1/#211
7.3 KiB
Konduit Server Quickstart
Running your own Konduit server takes three binaries: konduit-server (the VPN
server), konduit-ctl (provisioning), and konduit (a client, for testing).
1. Install
Download konduit-server-<version>-linux-x86_64 and konduit-ctl from the
Releases page, plus konduit-<version>-linux-x86_64 if you
want to test the tunnel from the same host.
sudo mkdir -p /opt/konduit
sudo cp konduit-server konduit-ctl konduit /opt/konduit/
cd /opt/konduit
sudo chmod +x konduit-server konduit-ctl konduit
Run everything from /opt/konduit — konduit-ctl writes server.toml into the
current directory, and the systemd unit expects the config at
/opt/konduit/server.toml.
Capabilities
Grant the binary permission to create TUN devices and bind port 443 without running as root:
sudo setcap cap_net_admin,cap_net_bind_service=+ep /opt/konduit/konduit-server
Do this after copying the binary. Capabilities are stored as an extended
attribute on the file, and a copy made across filesystems can drop them — a
konduit-server that quietly lost cap_net_bind_service fails with a permission
error the moment it tries to bind 443. The unit in step 6 runs as User=root,
where capabilities are irrelevant; this matters for running it by hand.
To confirm what the binary actually ended up with:
getcap /opt/konduit/konduit-server
2. Prerequisites
IP forwarding
sudo sysctl -w net.ipv4.ip_forward=1
# Persist: add 'net.ipv4.ip_forward=1' to /etc/sysctl.conf
TUN device
ls /dev/net/tun || sudo modprobe tun
Without it the server cannot create its interface and exits at startup.
3. Bootstrap
Konduit derives its identity key from a mantra (secret phrase) using Argon2id.
cd /opt/konduit
echo "your secret mantra phrase here" | ./konduit-ctl bootstrap -l vpn.example.com:443 -p -
-l sets both the port the server binds and the hostname written into every
client config, so use the name you will actually serve. Bootstrap refuses to
overwrite an existing server.toml unless you pass --force.
It prints the server's public identity key — clients need this, and it is
already inside the generated .client.toml files.
Behind a reverse proxy (HAProxy on 443, konduit on 8443):
echo "your secret mantra phrase here" | \
./konduit-ctl bootstrap -l vpn.example.com:8443 --public-port 443 -p -
--public-port sets the port written into client configs, so they connect to
443 even though konduit listens on 8443 — useful when running behind any
TCP-passthrough reverse proxy (e.g. HAProxy, nginx stream module). Konduit
can also terminate TLS itself; see [tls] in the
server configuration reference.
Back up
server.tomlnow, before you need it. It holds the server's private identity key in plaintext, and its[kdf] salt_hexis 32 random bytes generated once at bootstrap. The mantra is only half the key material — if you lose the file you lose the salt, and the mantra alone cannot regenerate the key. See Key handling.
sudo install -m 600 server.toml /root/konduit-server.toml.bak
4. Add a Client
./konduit-ctl add-client my-laptop
Creates my-laptop.client.toml and appends the peer to server.toml. Transfer
the .client.toml to the client device, or scan the QR payload with the mobile
app.
The running server does not see this yet. add-client only edits the file on
disk; the live peer list updates on the next reload. Either add --reload to the
command, or reload separately:
./konduit-ctl add-client my-laptop --reload
# or, on an already-running server:
./konduit-ctl reload
--reload talks to the control API on 127.0.0.1:3001. Running konduit-ctl
on a different machine than the server needs --control-url, and the API is
bound to loopback — see Reloading before changing
that.
5. TUN Interface Name
Set a fixed TUN interface name so firewall rules are stable:
# server.toml
[network]
tun_name = "konduit0"
The default is tun0, but konduit falls back to the first free tunN if that
name is taken. On a host that also runs WireGuard or OpenVPN the name then
shifts underneath you and any firewall rule keyed to it stops matching. The
masquerade rule in step 7 uses this name.
6. Run
./konduit-server --config server.toml
Check the file before starting for real:
./konduit-server --config server.toml --validate
For persistent operation, use the provided systemd units in docs/systemd/. Copy konduit-server.service to /etc/systemd/system/, then:
sudo systemctl daemon-reload
sudo systemctl enable --now konduit-server
sudo journalctl -u konduit-server -f
7. NAT / Masquerade
VPN clients send packets with source IP 10.10.0.x — the /24 implied by the
default server_ip = "10.10.0.1". Masquerade rewrites this to the server's
public IP so internet hosts can reply. If you changed server_ip, match the rule
to the new subnet.
firewalld (RHEL, Fedora, recent Ubuntu)
# Trust the VPN interface
sudo firewall-cmd --zone=trusted --add-interface=konduit0 --permanent
# Enable masquerade on the public zone
sudo firewall-cmd --zone=public --add-masquerade --permanent
sudo firewall-cmd --reload
Important:
iptablescommands are silently ignored on firewalld systems. Always usefirewall-cmd.
iptables (Debian/Ubuntu without firewalld)
IFACE=$(ip route get 1.1.1.1 | awk '{print $5; exit}')
sudo iptables -t nat -I POSTROUTING 1 -s 10.10.0.0/24 -o $IFACE -j MASQUERADE
sudo iptables -I FORWARD 1 -i konduit0 -o $IFACE -j ACCEPT
sudo iptables -I FORWARD 2 -i $IFACE -o konduit0 -m state --state RELATED,ESTABLISHED -j ACCEPT
# Persist
sudo apt install iptables-persistent && sudo netfilter-persistent save
8. Verify
# Connect a client and confirm the tunnel comes up
./konduit --config my-laptop.client.toml
# Is the peer actually connected?
./konduit-ctl peers
# Check konduit logs
journalctl -u konduit-server -f
./konduit-ctl peers returning your peer's address is the check worth having —
it distinguishes "the client authenticated" from "the client is routing traffic",
and only the second one proves NAT is set up.
Troubleshooting
| Symptom | Cause |
|---|---|
Permission denied binding 443 |
cap_net_bind_service was lost in a copy. Re-run setcap against the final path and confirm with getcap. |
No such file or directory on startup |
/dev/net/tun missing — sudo modprobe tun. |
| Tunnel connects, but no internet | Masquerade missing or scoped to the wrong subnet. Check step 7 matches your server_ip. |
| iptables rules look right but do nothing | firewalld is running and ignoring them. Use firewall-cmd. |
| New client cannot connect | Peer was added to server.toml but never reloaded. Run konduit-ctl reload. |
| Revoked client still has access | /reload does not disconnect live sessions. Restart the service. |
IP pool exhausted (10.10.0.10-254) |
The /24 is full. Widen netmask or move server_ip to another subnet. |
For more detail than the table, set RUST_LOG=debug:
sudo systemctl edit konduit-server
# [Service]
# Environment=RUST_LOG=debug