The documentation moves from READMEs into a site in docs/: getting started, a user guide, operations, a reference, and development pages, written in the style of network vendors' documentation (docs/STYLE.md). bin/bgp/README.md and docs/RFCs.md are folded into it. - A theme of its own in the International Typographic Style, in blues, with self-hosted Inter and JetBrains Mono, search over Zola's index, dark mode, and no resources loaded from other hosts. - Example configurations in docs/examples/, shown on the pages and checked with `bgp --check` in CI. - A test that fails when a field of config.proto is missing from the configuration reference. - A CI job that builds the site; Zola fails on broken links and anchors. - A hardened systemd unit, tried under systemd with kernel routes on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6.0 KiB
+++ title = "Deploying Ferrix" description = "Describes how to run Ferrix as a systemd service, secure the hosts and interfaces around it, change its configuration, and restart it." weight = 1
[extra] nav = "Deployment" +++
This topic describes how to deploy Ferrix on a Linux router as a systemd service, and the security measures that a production deployment requires.
Running Ferrix as a systemd Service
The provided systemd unit runs the daemon as an unprivileged user, with only the capabilities that it requires, and restricts its access to the rest of the system.
To install the service:
-
Create the system user and the configuration directory.
sudo useradd --system --no-create-home --shell /usr/sbin/nologin ferrix sudo install -d -m 0750 -o root -g ferrix /etc/ferrix sudo install -m 0640 -o root -g ferrix server.textpb /etc/ferrix/server.textpb -
Install the unit file as
/etc/systemd/system/ferrix.service.{{ file(name="ferrix.service", lang="ini") }}
-
Enable and start the service.
sudo systemctl daemon-reload sudo systemctl enable --now ferrix -
Verify that the service is running.
systemctl status ferrix journalctl -u ferrix -f
The ExecStartPre command validates the configuration before the daemon
starts. If the configuration is invalid, the service does not start, and
the journal contains the error.
The sandboxing options in the unit have been tested with kernel route
installation enabled. The command systemd-analyze security ferrix rates
the unit's exposure level as 2.1 (OK).
Securing the Deployment
HTTP Interface
Caution
The status page and HTTP API do not require authentication. They expose the configuration, all received routes, and the state of every neighbor. Bind
http_endpointto a loopback address, or place it behind a reverse proxy that requires authentication.
BGP Port Filtering
Ferrix closes connections from addresses that are not configured neighbors before it reads from them. Ferrix does not support TCP-AO or TCP MD5 authentication. Configure the host firewall to accept connections to TCP port 179 only from neighbor addresses.
The following nftables rules, in the input chain of an inet filter
table, drop BGP connections from any other address:
nft add rule inet filter input tcp dport 179 ip saddr != { 203.0.113.0, 203.0.113.2 } drop
nft add rule inet filter input tcp dport 179 ip6 saddr != { 2001:db8:ffff:1::1 } drop
RPKI Cache Connection
The RTR connection is not authenticated. Run the RPKI validator on the same host or on a trusted network. See RPKI.
Metrics Collection
Collect metrics from /metricz over the loopback interface, or through
the authenticating reverse proxy.
Neighbor Policy
Configure max_prefixes and role for every EBGP neighbor, and prefix and
origin AS lists for every neighbor whose routes are known. See
Import Policy.
Logging
The daemon writes its log to standard output. Under systemd, the log is stored in the journal. When standard output is not a terminal, the log contains no color codes.
The --level option sets the minimum level for the daemon's own messages.
The RUST_LOG environment variable sets the level for other components.
Table 1 lists the messages at each level.
| Level | Messages |
|---|---|
info (default) |
Session state changes, the first rejected route from each neighbor and at most one per minute thereafter, and kernel route installation state |
debug |
Additionally, one message for each UPDATE received and each route advertised |
Note
At the
debuglevel, a full routing table produces millions of log messages.
Ferrix rate-limits log messages that a neighbor can trigger, such as messages about malformed attributes or rejected connections. Each occurrence is counted in a metric. The log contains at most one message for each interval, with the number of suppressed occurrences.
Changing the Configuration
Ferrix reads its configuration at startup only. To change the configuration:
-
Validate the new configuration.
sudo bgp --check /etc/ferrix/server.textpb.new -
Replace the active configuration.
sudo mv /etc/ferrix/server.textpb.new /etc/ferrix/server.textpb -
Restart the service.
sudo systemctl restart ferrix
Restart Behavior
When the daemon stops in response to SIGTERM, it sends a Cease NOTIFICATION message with the subcode Administrative Shutdown to every neighbor, and removes its routes from the kernel. The neighbors withdraw the routes that they received from Ferrix. Traffic uses alternative paths until the sessions are reestablished and the routing tables are exchanged again. For a full routing table, this takes several seconds.
If the daemon terminates unexpectedly, its routes remain in the kernel, and
the router continues to forward traffic. At the next start, Ferrix retains
these routes for stale_seconds (default: 120 seconds) while the neighbors
readvertise their routes, and then removes the routes that were not
readvertised.
Warning
A restart interrupts forwarding through the router for the time that the sessions take to converge. Restart Ferrix in a maintenance window, or after traffic has been drained to another router.
Running Ferrix in a Container
The container image built from the repository's Dockerfile runs
bgp /etc/ferrix/server.textpb --level info. The container requires the
host network namespace and the configuration directory. If kernel route
installation is enabled, it also requires the NET_ADMIN capability.
docker run -d --name ferrix --restart unless-stopped \
--network host --cap-add NET_ADMIN \
-v /etc/ferrix:/etc/ferrix:ro \
ferrix
Note
The container image runs the daemon as root. Because the container uses the host network namespace, the systemd service is the recommended deployment method on a router.