Add a documentation website, built with Zola
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>
This commit is contained in:
1 parent
9820a5d3bc
commit
dc275f99d7
61 files changed
+5632
-623
No files matched your search
@@ -0,0 +1,38 @@
|
||||
# Edge network with one transit provider and one session for each address
|
||||
# family. Route origin validation, ASPA verification, and kernel route
|
||||
# installation are enabled.
|
||||
router_id: "198.51.100.1"
|
||||
local_asn: 64500
|
||||
|
||||
peers {
|
||||
name: "transit-a-v4"
|
||||
endpoint { address: "203.0.113.0" }
|
||||
local_address: "203.0.113.1"
|
||||
remote_asn: 64496
|
||||
role: PROVIDER
|
||||
address_families: IPV4_UNICAST
|
||||
# Above the size of the IPv4 Internet routing table.
|
||||
import { max_prefixes: 1200000 }
|
||||
}
|
||||
|
||||
peers {
|
||||
name: "transit-a-v6"
|
||||
endpoint { address: "2001:db8:ffff:1::1" }
|
||||
local_address: "2001:db8:ffff:1::2"
|
||||
remote_asn: 64496
|
||||
role: PROVIDER
|
||||
address_families: IPV6_UNICAST
|
||||
import { max_prefixes: 300000 }
|
||||
}
|
||||
|
||||
originate { prefix: "198.51.100.0/24" }
|
||||
originate { prefix: "2001:db8:100::/40" }
|
||||
|
||||
# RPKI cache on the local host, for example Routinator.
|
||||
rpki { caches { address: "::1" port: 3323 } }
|
||||
|
||||
# Requires Linux and CAP_NET_ADMIN.
|
||||
kernel {}
|
||||
|
||||
# Not authenticated. Bind to a loopback address.
|
||||
http_endpoint { address: "127.0.0.1" port: 8080 }
|
||||
@@ -0,0 +1,73 @@
|
||||
# Network with one transit provider, a session with an Internet exchange
|
||||
# route server, and a bilateral session with one peer at the exchange.
|
||||
#
|
||||
# Routes learned at the exchange have local preference 150, and transit
|
||||
# routes have the default of 100. Accepted routes are marked with a large
|
||||
# community that identifies where they were learned (64500:2:x). Advertised
|
||||
# routes are marked with the advertising router (64500:3:r).
|
||||
router_id: "198.51.100.1"
|
||||
local_asn: 64500
|
||||
|
||||
peers {
|
||||
name: "transit-a"
|
||||
endpoint { address: "2001:db8:ffff:1::1" }
|
||||
remote_asn: 64496
|
||||
role: PROVIDER
|
||||
import {
|
||||
max_prefixes: 1500000
|
||||
remove_communities: "64500:2:*"
|
||||
remove_communities: "64500:3:*"
|
||||
add_large_communities: "64500:2:100" # Learned from transit-a.
|
||||
}
|
||||
}
|
||||
|
||||
# The route server does not prepend its AS number. Its routes have the
|
||||
# addresses of the originating members as next hops, which the role
|
||||
# ROUTE_SERVER permits.
|
||||
peers {
|
||||
name: "ixp-a-rs1"
|
||||
endpoint { address: "2001:db8:ffff:ff::fe" }
|
||||
remote_asn: 64511
|
||||
role: ROUTE_SERVER
|
||||
import {
|
||||
max_prefixes: 250000
|
||||
accept_any_first_as: true
|
||||
remove_communities: "64500:2:*"
|
||||
remove_communities: "64500:3:*"
|
||||
add_large_communities: "64500:2:1001" # Learned at exchange A.
|
||||
local_pref: 150
|
||||
}
|
||||
}
|
||||
|
||||
# Bilateral peer at the exchange. The prefix and origin AS lists are
|
||||
# generated from the IRR with bgpq4.
|
||||
peers {
|
||||
name: "ixp-a-as64499"
|
||||
endpoint { address: "2001:db8:ffff:ff::99" }
|
||||
remote_asn: 64499
|
||||
role: PEER
|
||||
require_role: true
|
||||
import {
|
||||
max_prefixes: 100
|
||||
prefixes { prefix: "2001:db8:9900::/40" max_length: 48 }
|
||||
origin_asns: 64499
|
||||
remove_communities: "64500:2:*"
|
||||
remove_communities: "64500:3:*"
|
||||
add_large_communities: "64500:2:1001"
|
||||
local_pref: 150
|
||||
}
|
||||
}
|
||||
|
||||
originate { prefix: "2001:db8:100::/40" }
|
||||
|
||||
# Applies to all neighbors: marks advertised routes with router 1.
|
||||
export { add_large_communities: "64500:3:1" }
|
||||
|
||||
rpki {
|
||||
caches { address: "::1" port: 3323 }
|
||||
caches { address: "2001:db8:100::53" port: 3323 }
|
||||
}
|
||||
|
||||
kernel {}
|
||||
|
||||
http_endpoint { address: "127.0.0.1" port: 8080 }
|
||||
@@ -0,0 +1,52 @@
|
||||
# /etc/systemd/system/ferrix.service
|
||||
#
|
||||
# Runs the Ferrix daemon as an unprivileged user, with only the
|
||||
# capabilities that it requires, and restricts its access to the system.
|
||||
# The log is written to the journal: journalctl -u ferrix.
|
||||
[Unit]
|
||||
Description=Ferrix BGP daemon
|
||||
Documentation=https://ferrix.rayhaan.net/docs/
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=ferrix
|
||||
Group=ferrix
|
||||
# Validate the configuration before starting. An invalid configuration
|
||||
# prevents the start, and the error is written to the journal.
|
||||
ExecStartPre=/usr/local/bin/bgp --check /etc/ferrix/server.textpb
|
||||
ExecStart=/usr/local/bin/bgp /etc/ferrix/server.textpb --level info
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
# Time to notify all neighbors and remove the kernel routes.
|
||||
TimeoutStopSec=30s
|
||||
|
||||
# TCP port 179, and the kernel routing table.
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
|
||||
NoNewPrivileges=yes
|
||||
|
||||
# BGP, RTR, HTTP, and netlink sockets only.
|
||||
RestrictAddressFamilies=AF_INET AF_INET6 AF_NETLINK AF_UNIX
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelLogs=yes
|
||||
ProtectControlGroups=yes
|
||||
ProtectClock=yes
|
||||
ProtectHostname=yes
|
||||
RestrictNamespaces=yes
|
||||
RestrictRealtime=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
MemoryDenyWriteExecute=yes
|
||||
SystemCallArchitectures=native
|
||||
SystemCallFilter=@system-service
|
||||
UMask=0077
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,30 @@
|
||||
# Quick start: Ferrix and a test peer on one host, over loopback.
|
||||
router_id: "192.0.2.1"
|
||||
local_asn: 64500
|
||||
|
||||
# A port above 1023, so that no privileges are required.
|
||||
listen_endpoints { address: "127.0.0.1" port: 1790 }
|
||||
|
||||
peers {
|
||||
name: "test-peer"
|
||||
endpoint { address: "127.0.0.1" port: 1791 }
|
||||
remote_asn: 65001
|
||||
import {
|
||||
max_prefixes: 1000
|
||||
# The test peer advertises documentation prefixes and uses a private
|
||||
# AS number. Both are rejected by default.
|
||||
accept_bogons: true
|
||||
# The test peer connects from 127.0.0.1 and advertises 10.0.0.2 as the
|
||||
# next hop.
|
||||
accept_third_party_next_hop: true
|
||||
}
|
||||
}
|
||||
|
||||
originate { prefix: "198.51.100.0/24" }
|
||||
originate {
|
||||
prefix: "2001:db8:100::/48"
|
||||
communities: "64500:100"
|
||||
}
|
||||
|
||||
# Status page, HTTP API, and metrics. Not authenticated.
|
||||
http_endpoint { address: "127.0.0.1" port: 8080 }
|
||||
@@ -0,0 +1,70 @@
|
||||
# Transit network with two providers and one customer. Customer routes are
|
||||
# advertised to all neighbors. Provider routes are advertised to the
|
||||
# customer only. The BGP roles of the neighbors enforce this.
|
||||
router_id: "198.51.100.1"
|
||||
local_asn: 64500
|
||||
|
||||
# Advertise learned routes to all neighbors, as BGP roles permit.
|
||||
export { learned_routes: true }
|
||||
|
||||
# Customers can restrict the advertisement of their routes with the large
|
||||
# communities 64500:0:P (not to AS P), 64500:1:P (only to AS P), and
|
||||
# 64500:0:0 (to no neighbor), or the standard communities 0:P, 64500:P, and
|
||||
# 0:64500.
|
||||
control_communities: true
|
||||
|
||||
peers {
|
||||
name: "transit-a"
|
||||
endpoint { address: "203.0.113.0" }
|
||||
remote_asn: 64496
|
||||
role: PROVIDER
|
||||
import {
|
||||
max_prefixes: 1500000
|
||||
remove_communities: "64500:2:*"
|
||||
add_large_communities: "64500:2:100"
|
||||
}
|
||||
}
|
||||
|
||||
peers {
|
||||
name: "transit-b"
|
||||
endpoint { address: "203.0.113.2" }
|
||||
remote_asn: 64497
|
||||
role: PROVIDER
|
||||
import {
|
||||
max_prefixes: 1500000
|
||||
remove_communities: "64500:2:*"
|
||||
add_large_communities: "64500:2:200"
|
||||
}
|
||||
# Prepend to make the path through transit-b less preferred. This
|
||||
# statement replaces the top-level export statement for this neighbor.
|
||||
export { learned_routes: true as_path_prepend: 2 }
|
||||
}
|
||||
|
||||
# Customer with a four-octet AS number. The prefix and origin AS lists
|
||||
# restrict its routes to its registered address space. Local preference
|
||||
# 200 makes its routes preferred over the same prefixes from providers.
|
||||
peers {
|
||||
name: "customer-65536"
|
||||
endpoint { address: "203.0.113.5" }
|
||||
remote_asn: 65536
|
||||
role: CUSTOMER
|
||||
require_role: true
|
||||
import {
|
||||
max_prefixes: 50
|
||||
prefixes { prefix: "192.0.2.0/24" }
|
||||
prefixes { prefix: "2001:db8:a00::/40" max_length: 48 }
|
||||
origin_asns: 65536
|
||||
remove_communities: "64500:2:*"
|
||||
add_large_communities: "64500:2:300"
|
||||
local_pref: 200
|
||||
}
|
||||
}
|
||||
|
||||
originate { prefix: "198.51.100.0/24" }
|
||||
originate { prefix: "2001:db8:100::/40" }
|
||||
|
||||
rpki { caches { address: "::1" port: 3323 } }
|
||||
|
||||
kernel {}
|
||||
|
||||
http_endpoint { address: "127.0.0.1" port: 8080 }
|
||||
Reference in new issue
Block a user