Skip to main content

Quickstart

Rivora is an eBPF/XDP load balancer: it needs a real Linux kernel, and the BPF programs only build and load on Linux. From a Mac or a thin laptop use the remote deploy path. On Kubernetes, skip to Kubernetes.

Requirements​

  • Linux with XDP and BPF, and root (or CAP_BPF, CAP_NET_ADMIN, CAP_NET_RAW, CAP_SYS_ADMIN). Full-NAT also needs TCX (Linux 6.6+) for its return path; DSR-only nodes do not. The forwarding program uses helpers added in 5.18, and only 6.8 has been tested.
  • bpffs mounted (/sys/fs/bpf): maps and links are pinned under /sys/fs/bpf/rivora-lb.
  • To build: Go, clang and llvm, libelf-dev; bpftool helps with troubleshooting.

rivora-doctor checks all of it:

sudo ./bin/rivora-doctor # --strict to fail on warnings, --interface eth0 for the driver

Run one VIP on one node​

make bpf build # bpf/*.o and bin/{rivorad,rivoractl,rivora-doctor,rivora,rivora-controller}
sudo ./bin/rivorad -config config/examples/single-vip.yaml -bpf-dir bpf
./bin/rivoractl status # in another shell
./bin/rivoractl vips

A minimal config, full-NAT (no backend changes needed):

interface: eth0
vips:
- address: 10.0.0.100
port: 80
protocol: tcp
mode: nat
backends:
- {address: 10.0.1.11, port: 8080}
- {address: 10.0.1.12, port: 8080}

Check a file without a running daemon: rivoractl validate config.yaml. Every setting is in the configuration reference.

What each mode needs from your backends​

ModeBackends need
natNothing, except that their route back to the client goes through the Rivora node (default gateway or a static route), so replies can be un-NATed.
dsrThe VIP bound locally (ip addr add <vip>/32 dev lo, no ARP for it), reachable at L2 from the node, and their MAC in the config.
dsr-ipip, dsr-greA tunnel endpoint, the VIP on lo and reverse-path filtering off; they may be any number of routed hops away.

Details: Forwarding modes.

Example configs​

All in config/examples/ and validated by CI.

FileShows
single-vip.yaml, single-vip-nat.yamlOne VIP, DSR and full-NAT
single-vip-ipv6-dsr.yaml, single-vip-ipv6-nat.yamlIPv6
multi-vip.yamlSeveral VIPs, mixing modes
weighted-backends.yamlUnequal traffic shares
session-affinity.yamlsessionAffinity: clientIP
http-healthcheck.yamlHTTP probes
rate-limited.yaml, vip-rate-limit.yamlNode-wide and per-VIP SYN limits
port-ranges.yamlPort ranges and multiple ports
l3-dsr.yamlIP-in-IP and GRE direct return
bgp-ha.yaml, bgp-options.yamlBGP + BFD, and its options
remote-api.yamlAn API bound to a non-loopback address

With more than one VIP, rivoractl status prints a line per VIP, and rivoractl vips / backends list them all.

Operate it​

rivoractl vips # every VIP: mode, backends, packets
rivoractl drain 12 # take backend 12 out of new-flow rotation (IDs from `rivoractl backends`)
rivoractl weight 12 5 # canary: give it more or less traffic
sudo systemctl reload rivorad # apply an edited config's VIP set (SIGHUP)
curl -s http://127.0.0.1:9871/metrics # Prometheus

Install as a service with scripts/install-systemd.sh (it binds the API to 0.0.0.0 and so refuses to run without RIVORA_API_KEY), or use deploy/systemd/rivorad.service. Add -persist-datapath to restart without a traffic gap.

Securing the API​

By default the API listens on 127.0.0.1:9870, unauthenticated and plain HTTP. Before binding it anywhere else set RIVORA_API_KEY (generate one with openssl rand -hex 24), and use TLS. Read-only keys, key rotation, named keys with an audit trail and client certificates are in API and console.

Kubernetes​

rivora install --set rivorad.interface=eth0 \
--set addressPools[0].name=default --set addressPools[0].addresses='{10.0.0.0/24}'
rivora status
kubectl expose deployment web --type=LoadBalancer --port=80 # gets an address from the pool

rivora drives the Helm chart with no helm binary; plain helm install deploy/helm/rivora ... takes the same values. See Kubernetes and the Helm chart.

Remote build and deploy​

make deploy-remote H=<host> U=<user> # rsync, install build deps, build, install
make deploy-remote-quick H=<host> U=<user> # skip the dependency install
make deploy-remote-verify H=<host> U=<user> # run the selftests there

Selftest failures are warnings during a deploy and hard failures in CI.

Next​