26a9228f52e4bb973674f2e69ca64899f0ab10fa
howto/OpenBGPD.md
| ... | ... | @@ -1,135 +1,220 @@ |
| 1 | 1 | This guide describes a simple configuration for [OpenBGPD](https://openbgpd.org) running on [OpenBSD](https://openbsd.org). |
| 2 | 2 | The [portable version](https://openbgpd.org/ftp.html) should run with little to no configuration changes on other operating systems as well. |
| 3 | 3 | |
| 4 | -# Setup |
|
| 5 | -Only IPv6 is used for the sake of simplicity. |
|
| 6 | -Neighbors use ULA addresses (/127 transfer net) assigned from one of the peer's allocation. |
|
| 4 | +Other than the |
|
| 5 | +[`bgpd.conf(5)`](https://man.openbsd.org/bgpd.conf.5) and |
|
| 6 | +[`bgpd(8)`](http://man.openbsd.org/bgpd.8) man pages and the |
|
| 7 | +OpenBSD |
|
| 8 | +[`/etc/examples/bgpd.conf`](http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/etc/examples/bgpd.conf?rev=HEAD&content-type=text/plain&only_with_tag=MAIN), |
|
| 9 | +you might also find useful reference or ideas in the |
|
| 10 | +[Bird2](/howto/Bird2) page (even if you don't use Bird), as |
|
| 11 | +it likely presents the most widespread dn42 router setup. |
|
| 12 | + |
|
| 13 | +# Example configuration |
|
| 14 | +When copying from the below configuration, be sure to at |
|
| 15 | +least replace the various `<PLACEHOLDER>`s with your own |
|
| 16 | +numbers. |
|
| 17 | + |
|
| 18 | +Concrete configuration examples can also be found elsewhere, |
|
| 19 | +e.g.: |
|
| 20 | + |
|
| 21 | +[https://smrk.net/text/openbsd-dn42-setup.txt](https://smrk.net/text/openbsd-dn42-setup.txt) |
|
| 22 | + |
|
| 23 | +[https://kaizo.org/2024/01/03/openbsd-bgpd/](https://kaizo.org/2024/01/03/openbsd-bgpd/) |
|
| 24 | + |
|
| 25 | +Given OpenBGPD's limited support for multiprotocol sessions |
|
| 26 | +(no extended next hop (RFC8950)) and |
|
| 27 | +[some](https://marc.info/?l=openbgpd-users&m=159983144408845&w=2) |
|
| 28 | +[issues](https://marc.info/?l=openbgpd-users&m=165605427017298&w=2) |
|
| 29 | +with IPv6 link-local nexthops, we configure separate IPv4 |
|
| 30 | +and IPv6 sessions for each peer, and for IPv6 we adjust |
|
| 31 | +nexthop to a "global" address (i.e., one from our dn42 IPv6 |
|
| 32 | +allocation) assigned to each peering (Wireguard) interface |
|
| 33 | +(each interface gets its own). |
|
| 34 | + |
|
| 35 | +To avoid burning a dn42 IPv4 address for each peering, we'll |
|
| 36 | +put the router's dn42 IPv4 address on the loopback interface |
|
| 37 | +and peer using an RFC1918 subnet (192.168.42/24) NATed to |
|
| 38 | +the loopback address (the NAT is only used in case of |
|
| 39 | +actively opening an IPv4 BGP session, it does not affect |
|
| 40 | +routing or incoming connections). |
|
| 41 | + |
|
| 42 | +## `/etc/hostname.lo0` |
|
| 7 | 43 | |
| 8 | -The goal is to have a small, yet complete setup for all peers with ROA validation and other safety measures in place. |
|
| 9 | - |
|
| 10 | -# Configuration |
|
| 11 | -[`/etc/bgpd.conf`](https://man.openbsd.org/bgpd.conf.5) contains all information and may include further (automatically generated) files, as is done in this guide. |
|
| 12 | - |
|
| 13 | -As per the manual, configuration is divided into logical sections; [`/etc/examples/bgpd.conf`](http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/etc/examples/bgpd.conf?rev=HEAD&content-type=text/plain&only_with_tag=MAIN) is a complete and commented example which this guide is roughly based on. |
|
| 14 | - |
|
| 15 | -By default, [bgpd(8)](http://man.openbsd.org/bgpd.8) listens on all local addresses (on the current default [`routing domain`](http://man.openbsd.org/rdomain.4)), but this guide explicitly listens on the configured transfer ULA only for each peer to better illustrate this setup. |
|
| 16 | - |
|
| 17 | -## local host |
|
| 18 | -Information such as ASN, router ID and allocated networks are required: |
|
| 19 | 44 | ```conf |
| 20 | -# macros |
|
| 21 | -ASN="4242421234" |
|
| 45 | +alias <YOUR-ROUTER-DN42-IPv4> |
|
| 46 | +``` |
|
| 22 | 47 | |
| 23 | -# global configuration |
|
| 24 | -AS $ASN |
|
| 25 | -router-id 1.2.3.4 |
|
| 48 | +## `/etc/hostname.wg1234` |
|
| 49 | +(one example; similar for each peer) |
|
| 26 | 50 | |
| 27 | -prefix-set mynetworks { |
|
| 28 | - fd00:12:34::/48 |
|
| 29 | -} |
|
| 51 | +```conf |
|
| 52 | +inet 192.168.42.1/32 |
|
| 53 | +inet6 fe80::1 64 # this is the address your peer will want to know |
|
| 54 | +# (and connect to); the following address is only really needed |
|
| 55 | +# to provide a non-link-local IPv6 address for the nexthop setting; |
|
| 56 | +# you can pick it "arbitrarily" from your dn42 IPv6 allocation |
|
| 57 | +inet6 <YOUR-DN42-IPv6-OF-PEER1-INTERFACE> 64 |
|
| 58 | +group my_dn |
|
| 59 | +wgport 21234 wgkey <PRIVKEY-BASE64> |
|
| 60 | +wgpeer <PEER1-PUBKEY-BASE64> \ |
|
| 61 | + wgdescr "dn42 peer1" \ |
|
| 62 | + wgaip fe80::/64 wgaip fd00::/8 wgaip 10.0.0.0/8 wgaip 172.20.0.0/14 \ |
|
| 63 | + wgendpoint <PEER1-HOSTNAME-OR-IP> 24321 |
|
| 64 | +up |
|
| 65 | +# add a static IPv4 route to the peer |
|
| 66 | +!route -nq add <PEER1-IPv4> 192.168.42.1 |
|
| 30 | 67 | ``` |
| 31 | 68 | |
| 32 | -These can be used in subsequent filter rules. |
|
| 33 | -The local peer's announcements is then defined as follows: |
|
| 69 | +## `/etc/pf.conf` |
|
| 70 | +(only the dn42-related snippet) |
|
| 71 | + |
|
| 34 | 72 | ```conf |
| 35 | -# Generate routes for the networks our ASN will originate. |
|
| 36 | -# The communities (read 'tags') are later used to match on what |
|
| 37 | -# is announced to EBGP neighbors |
|
| 38 | -network prefix-set mynetworks set large-community $ASN:1:1 |
|
| 73 | +pass in quick proto {icmp icmp6} max-pkt-rate 30/3 |
|
| 74 | +dn42_self = <YOUR-ROUTER-DN42-IPv4> |
|
| 75 | +table <dn42etc> const {172.20/14 172.31/16 10/8 fd00::/8 fe80::/64} |
|
| 76 | +table <dn42peers> const {<PEER1-IPv4> fe80::/64} |
|
| 77 | +pass in quick on egress proto udp to port 21234 |
|
| 78 | +pass in quick on my_dn proto tcp from <dn42peers> \ |
|
| 79 | + to {$dn42_self (my_dn)} port bgp |
|
| 80 | +# block everything (except for ICMP above) destined to the |
|
| 81 | +# router itself; only dn42 transit and BGP sessions are allowed |
|
| 82 | +block in log quick on my_dn to {$dn42_self (my_dn)} |
|
| 83 | +pass out on my_dn from 192.168.42/24 nat-to $dn42_self |
|
| 84 | +pass on my_dn from <dn42etc> to <dn42etc> |
|
| 39 | 85 | ``` |
| 40 | 86 | |
| 41 | -## neighbors |
|
| 42 | -For each neighbor its ASN and transfer ULA is required. |
|
| 43 | -An optional description is provided such that [bgpctl(8)](http://man.openbsd.org/bgpctl.8) for example can be used with mnemonic names instead of AS numbers: |
|
| 87 | +## `/etc/bgpd.conf` |
|
| 44 | 88 | ```conf |
| 45 | -# peer A, transport over IPSec/GRE |
|
| 46 | -$A_local="fd00:12:34:A::1" |
|
| 47 | -$A_remote="fd00:12:34:A::2" |
|
| 48 | -$A_ASN="4242425678" |
|
| 89 | +ASN = "<YOUR-AS-NUMBER>" |
|
| 49 | 90 | |
| 50 | -listen on $A_local |
|
| 91 | +AS $ASN |
|
| 92 | +router-id <YOUR-ROUTER-DN42-IPv4> |
|
| 51 | 93 | |
| 52 | -neighbor $A_remote { |
|
| 53 | - remote-as $A_ASN |
|
| 54 | - descr "A" |
|
| 94 | +# list of networks that may be originated by our ASN |
|
| 95 | +prefix-set mydn42 { |
|
| 96 | + <YOUR-DN42-IPv4-PREFIX> |
|
| 97 | + <YOUR-DN42-IPv6-PREFIX> |
|
| 55 | 98 | } |
| 56 | -``` |
|
| 57 | - |
|
| 58 | -## filter rules |
|
| 59 | -**bgpd** blocks all BGP __UPDATE__ messages by default. |
|
| 60 | -The filter rules are evaluated in sequential order, from first to last. |
|
| 61 | -The last matching allow or deny rule decides what action is taken. |
|
| 62 | - |
|
| 63 | -Start off with basic protection and sanity rules: |
|
| 64 | -```conf |
|
| 65 | -# deny more-specifics of our own originated prefixes |
|
| 66 | -deny quick from ebgp prefix-set mynetworks or-longer |
|
| 67 | 99 | |
| 68 | -# filter out too long paths, establish more peerings instead |
|
| 69 | -deny quick from any max-as-len 8 |
|
| 70 | -``` |
|
| 100 | +# https://dn42.eu/howto/Bird2#example-configuration |
|
| 101 | +prefix-set dn42etc { |
|
| 102 | + 172.20.0.0/14 prefixlen 21 - 29 # dn42 |
|
| 103 | + 172.20.0.0/24 prefixlen 28 - 32 # dn42 Anycast |
|
| 104 | + 172.21.0.0/24 prefixlen 28 - 32 # dn42 Anycast |
|
| 105 | + 172.22.0.0/24 prefixlen 28 - 32 # dn42 Anycast |
|
| 106 | + 172.23.0.0/24 prefixlen 28 - 32 # dn42 Anycast |
|
| 107 | + 172.31.0.0/16 or-longer # ChaosVPN |
|
| 108 | + 10.100.0.0/14 or-longer # ChaosVPN |
|
| 109 | + 10.127.0.0/16 prefixlen 16 - 32 # neonetwork |
|
| 110 | + 10.0.0.0/8 prefixlen 15 - 24 # Freifunk.net |
|
| 111 | + fd00::/8 prefixlen 44 - 64 # dn42 |
|
| 112 | +} |
|
| 71 | 113 | |
| 72 | -`quick` rules are considered the last matching rule, and evaluation of subsequent rules is skipped. |
|
| 114 | +# https://dn42.burble.com/services/public/#roa-data |
|
| 115 | +# https://dn42.burble.com/roa/dn42_roa_obgpd_46.conf |
|
| 116 | +# see the crontab snippet and an update script further below |
|
| 117 | +include "/var/db/openbgpd/dn42_roa_obgpd_46.conf" |
|
| 118 | + |
|
| 119 | +network prefix-set mydn42 set { |
|
| 120 | + # https://dn42.dev/howto/BGP-communities |
|
| 121 | + # e.g., for Germany this could read |
|
| 122 | + # community 64511:41 |
|
| 123 | + # community 64511:1276 |
|
| 124 | + community 64511:<READ-THE-LINK-ABOVE> |
|
| 125 | + community 64511:<READ-THE-LINK-ABOVE> |
|
| 126 | + large-community $ASN:1:1 |
|
| 127 | +} |
|
| 73 | 128 | |
| 74 | -Allow own announcements: |
|
| 75 | -```conf |
|
| 76 | -# Outbound EBGP: only allow self originated networks to ebgp peers |
|
| 77 | -# Don't leak any routes from upstream or peering sessions. This is done |
|
| 78 | -# by checking for routes that are tagged with the large-community $ASN:1:1 |
|
| 79 | -allow to ebgp prefix-set mynetworks large-community $ASN:1:1 |
|
| 80 | -``` |
|
| 129 | +listen on <YOUR-ROUTER-DN42-IPv4> |
|
| 130 | +listen on <PEER1-IPv6-LOCAL> # e.g. fe80::1%wg1234 |
|
| 131 | + |
|
| 132 | +group dn42peers { |
|
| 133 | + # RFC7454 sec. 8 |
|
| 134 | + # (currently no peer sends more than 800 prefixes for |
|
| 135 | + # a single address family; increase this if using |
|
| 136 | + # multi-protocol BGP (or when the network grows)!) |
|
| 137 | + max-prefix 1000 restart 60 |
|
| 138 | + neighbor <PEER1-IPv4> { |
|
| 139 | + descr peer1_4 |
|
| 140 | + remote-as <PEER1-ASN> |
|
| 141 | + } |
|
| 142 | + neighbor <PEER1-IPv6-REMOTE> { # e.g. fe80::2%wg1234 |
|
| 143 | + descr peer1_6 |
|
| 144 | + remote-as <PEER1-ASN> |
|
| 145 | + set nexthop <YOUR-DN42-IPv6-OF-PEER1-INTERFACE> |
|
| 146 | + } |
|
| 147 | +} |
|
| 81 | 148 | |
| 82 | -Allow all remaining UPDATES based on **O**rigin **V**alidation **S**tates: |
|
| 83 | -```conf |
|
| 84 | -# enforce ROA |
|
| 85 | -allow from ebgp ovs valid |
|
| 86 | -``` |
|
| 149 | +# deny EBGP UPDATEs to our own originated prefixes |
|
| 150 | +deny quick from ebgp prefix-set mydn42 or-longer |
|
| 151 | +# filter out overlong paths |
|
| 152 | +deny quick from any max-as-len 10 |
|
| 87 | 153 | |
| 88 | -Note how the `ovs` filter requires the `roa-set {...}` to be defined; see the `ROA` section below. |
|
| 154 | +allow from group dn42peers prefix-set dn42etc ovs valid |
|
| 155 | +allow to group dn42peers prefix-set dn42etc |
|
| 89 | 156 | |
| 90 | -### path attributes |
|
| 91 | -Besides `allow` and `deny` statements, filter rules can modify UPDATE messages, e.g. |
|
| 92 | -```conf |
|
| 93 | -# Scrub normal and large communities relevant to our ASN from EBGP neighbors |
|
| 94 | -# https://tools.ietf.org/html/rfc7454#section-11 |
|
| 157 | +# scrub communities relevant to our ASN from EBGP neighbors |
|
| 158 | +# (RFC7454 sec. 11) |
|
| 159 | +# match from ebgp set { community delete $ASN:* } |
|
| 95 | 160 | match from ebgp set { large-community delete $ASN:*:* } |
| 96 | 161 | |
| 97 | -# Honor requests to gracefully shutdown BGP sessions |
|
| 98 | -# https://tools.ietf.org/html/rfc8326 |
|
| 162 | +# honor requests to gracefully shutdown BGP sessions (RFC8326) |
|
| 99 | 163 | match from any community GRACEFUL_SHUTDOWN set { localpref 0 } |
| 100 | 164 | ``` |
| 101 | 165 | |
| 102 | -# ROA |
|
| 166 | +## ROA |
|
| 103 | 167 | |
| 104 | -An roa-set can be generated from the registry directly or you can use the following pre-built tables. |
|
| 168 | +The `roa-set` for route origin validation (`ovs valid` in |
|
| 169 | +the config above) can be generated from the dn42 registry; |
|
| 170 | +here we use |
|
| 171 | +[data](https://dn42.burble.com/services/public/#roa-data) |
|
| 172 | +conveniently provided by BURBLE-MNT. |
|
| 105 | 173 | |
| 106 | -One single `roa-set` may be defined, against which **bgpd** will validate the origin of each prefix; this allows filter rules to use the `ovs` keyword as demonstrated above. |
|
| 174 | +If using the update script below, don't forget to create the |
|
| 175 | +`/var/db/openbgpd/` directory first. |
|
| 107 | 176 | |
| 108 | -ROA files generated by [dn42regsrv](https://git.dn42.dev/burble/dn42regsrv) are available from burble.dn42: |
|
| 177 | +### `/root/openbgpd-roa-update.sh` |
|
| 178 | +```sh |
|
| 179 | +#!/bin/sh |
|
| 109 | 180 | |
| 110 | -|URL| IPv4/IPv6 | |
|
| 111 | -|---|---| |
|
| 112 | -| <https://dn42.burble.com/roa/dn42_roa_obgpd_46.conf> | Both | |
|
| 113 | -| <https://dn42.burble.com/roa/dn42_roa_obgpd_4.conf> | IPv4 Only | |
|
| 114 | -| <https://dn42.burble.com/roa/dn42_roa_obgpd_6.conf> | IPv6 Only | |
|
| 115 | - |
|
| 116 | -`/etc/dn42.roa-set` is the generated set: |
|
| 117 | -```conf |
|
| 118 | -roa-set { |
|
| 119 | - fd00:12:34::/48 source-as 4242421234 |
|
| 120 | - fd00:ab:cd::/44 maxlen 64 source-as 4242427890 |
|
| 121 | - ... |
|
| 181 | +die() { |
|
| 182 | + >&2 printf '%s: %s\n' "${0##*/}" "$*" |
|
| 183 | + exit 1 |
|
| 122 | 184 | } |
| 185 | + |
|
| 186 | +# Unfortunately, burble regenerates the ROA files (hourly?) |
|
| 187 | +# even when nothing changed, so If-Modified-Since doesn't |
|
| 188 | +# help (similar story for .meta). |
|
| 189 | + |
|
| 190 | +metafile=/var/db/openbgpd/registry.meta |
|
| 191 | +err=$(ftp -o "$metafile" \ |
|
| 192 | + https://explorer.burble.com/api/registry/.meta 2>&1 >/dev/null) || |
|
| 193 | + die "/api/registry/.meta download failed: $err" |
|
| 194 | + |
|
| 195 | +if ! cmp -s "$metafile" "$metafile".old >/dev/null 2>&1; then |
|
| 196 | + mv "$metafile" "$metafile".old |
|
| 197 | + roafile=/var/db/openbgpd/dn42_roa_obgpd_46.conf |
|
| 198 | + if err=$(ftp -To "$roafile".new \ |
|
| 199 | + https://dn42.burble.com/roa/dn42_roa_obgpd_46.conf \ |
|
| 200 | + 2>&1 >/dev/null); then |
|
| 201 | + mv "$roafile".new "$roafile" |
|
| 202 | + bgpctl reload |
|
| 203 | + else |
|
| 204 | + die "ROA download failed: $err" |
|
| 205 | + fi |
|
| 206 | +else |
|
| 207 | + logger -cisp user.info "${0##*/}: registry unchanged, not reloading" |
|
| 208 | +fi |
|
| 123 | 209 | ``` |
| 124 | 210 | |
| 125 | -Include it in `/etc/bgpd.conf`: |
|
| 211 | +### `/var/cron/tabs/root` |
|
| 126 | 212 | ```conf |
| 127 | -# defines roat-set, see _rpki-client crontab |
|
| 128 | -include "/etc/dn42.roa-set" |
|
| 213 | +~ * * * * -ns /root/openbgpd-roa-update.sh |
|
| 129 | 214 | ``` |
| 130 | 215 | |
| 131 | 216 | # Looking glass |
| 132 | 217 | This is mostly OpenBSD specific since [bgplg(8)](http://man.openbsd.org/bgplg.8) and [httpd(8)](http://man.openbsd.org/httpd.8) ship as part of the operating system. |
| 133 | -The **bgplg** manual contains the few steps and example [httpd.conf(5)](http://man.openbsd.org/httpd.conf.5) required to enable the looking glass. |
|
| 218 | +The **bgplg** manual contains the steps and example [httpd.conf(5)](http://man.openbsd.org/httpd.conf.5) required to enable the looking glass. |
|
| 134 | 219 | |
| 135 | 220 | See <https://t4-2.high5.nl/bgplg> for a running instance operating within DN42. |