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