diff --git a/projects/alamesh/setting_up_bird.md b/projects/alamesh/setting_up_bird.md new file mode 100644 index 0000000..c5c8c89 --- /dev/null +++ b/projects/alamesh/setting_up_bird.md @@ -0,0 +1,267 @@ +# Setting up BIRD + +BIRD, or the "BIRD Internet Routing Daemon", is a daemon that turns a dump linux box into a fully fledged router that +can support many different routing protocols. It speaks those protocols with other routers and builds a dynamic routing +table that is then sent to the kernel (which does the actual packet forwarding). + +This doc tracks my learnings from install and using bird on a cheap, $4/month DO droplet. + +# My Network + +I'm not trying to to any sort of dynamic routing yet, but here's what I have working right now. My current network is +the entire `0200::/7` address space (all addresses prefixed with `0x2` and `0x00`). + + +``` +┌──────────┐ wg tunnel +│ hyperion ◄───────────────┐ +│ 200::3 │ │ +└──────────┘ │ + ┌────────▼────────────┐ + │ router running bird │ + │ 200::1 │ +┌─────────┐ └────────▲────────────┘ +│ lykos │ │ +│ 200::55 ◄────────────────┘ +└─────────┘ wg tunnel +``` + +# Installing `bird` + +Ubuntu's apt repo only has version `1.6.8` and I think we want the newer `2.0.10`. This means compiling it from source +which wasn't too bad. + +First we need some dependencies: + +``` +sudo apt install gcc flex bison make m4 libncurses5-dev libncursesw5-dev libreadline6-dev +``` + +Actually compiling it: +``` +curl -LO https://bird.network.cz/download/bird-2.0.10.tar.gz +tar -xvf bird-2.0.10.tar.gz +cd bird +./configure +make +make install +bird --version +``` + +I moved my bin to `/usr/local/bin/bird` after having some weird env issues. + +~~Now we need to create a user with `CAP_NET_*` privileges that bird can run as~~. We actually don't need to do that +immediately. We can use `-u` to drop to a user like `nobody` and bird will only give itself the privileges that it +needs. + +# Configuration + +Bird is configured with a pretty simple file (or list of files). You can use the global `/etc/bird/bird.conf` but I've +chosen to use a local config file under `~/.bird/bird.conf`. You can give bird the path to the config file with `-c`. + +dn42 has a [great guide](https://dn42.eu/howto/Bird) on using bird (with peers!). I'm going to copy the configuration +structure from that page here and attempt to add more commentary. + +The guide requires a few pieces of information (what we'll need to decide on): + +* ``: your [Autonomous System Number](https://en.wikipedia.org/wiki/Autonomous_system_(Internet)) +* ``: your gateway ip, the internal `0200::/7` address that the router will run on. +* ``: whatever subnet you control. Such as `0200:1234::/16`. +* ``: the ip of a peer connected to you. +* ``: the AS number of the peer. +* ``: what you want to call that peer. + +> Most of this information should be located in (and authenticated through) a git repo. If we do it with git, then +> updating bird should simply be a `git pull` and then running a cli tool to generate the config and reload bird. We need +> to find a balance between magic and understanding of what's happening. Maybe a tool that can describe what's going on +> at any level of detail would be enough? + + +Now for the actual configuration. We're going to be using IPv6 only. If we did v4 we would need a separate set of rules +files, subnets, ips, etc. + +The first file is the root `bird.conf`. This is what we point bird to when starting the daemon. I will try to comment +every line and block with my understanding of what it means. + +## bird.conf + +``` +# "everything" is a protocol in bird. The `device` protocol is not really +# a protocol, it just instructs bird to ask the kernel for information +# on devices (interfaces). +protocol device { + # scan devices every 10 seconds. + scan time 10; +} + +# Include our local configuration. +# +# We define our own AS and subnet in here along with some +# helper functions for determining if a route is in our +# own network. We'll cover this short file later. +include "/etc/bird/local6.conf + +# Tell bird how to interact with the kernel (modify routes): +# +# Again, not really a protocol but ¯\_(ツ)_/¯ + +# Comment from dn42. +/* + krt_prefsrc defines the source address for outgoing connections. + On Linux, this causes the "src" attribute of a route to be set. + + Without this option outgoing connections would use the peering IP which + would cause packet loss if some peering disconnects but the interface + is still available. (The route would still exist and thus route through + the TUN/TAP interface but the VPN daemon would simply drop the packet.) +*/ +protocol kernel { + # scan the routing table every 20 seconds (update bird state). + scan time 20; + + # Don't import any routes from the kernel, let bird figure out + # what routes to add or change. `none` is a "filter" that discards + # routes that are given to it. The kernel gives bird routes and we + # want bird to ignore them. kernel -> bird. + import none; + + # We write an inline filter that describes what routes to give the + # kernel from bird. bird -> kernel. + export filter { + # if the source of this route is static configuration, then we + # don't want to give it to the kernel. (only want dynamic routes). + # I don't know why this is important? Is that right? + if source = RTS_STATIC then reject; + # set the `krt_prefsrc` attribute for this route. + # see comment above above the kernel block. + krt_prefsrc = OWNIP; + # accept this route (don't filter it out). + accept; + }; +} + +# Static protocol lets you define static routes that do not change. +protocol static { + # reject any routes in our subnet (I think?) + route reject; + # tell bird about everything. static -> bird. + import all; + # bird can't configure static routes (default)?. + # bird -> static. + export none; +} + +# Create a template for the `bgp` protocol. This is where the real +# magic happens. We use a template since every peer will essentially +# have the same BGP configuration. The template name is `alapeers`. +# +# Each peer gets its own template nad file under `peers6/*``. +# +# We're using BGP in "exterior" or `eBGP` mode. This means we're using +# the protocol to define routes between Autonomous Systems, not really +# between individual IP addrs. +# +# Each instance of the `bgp` protocol corresponds with one neighboring +# router, or in our case, a peer. +template bgp alapeers { + # our `as` number is the OWNAS constant. This is defined in the local + # defs file that comes later. + local as OWNAS; + # compare path lengths to determine which route is the best one. + path metric 1; + + # remember rejected routes for later debugging. + import keep filtered; + + # only accept routes from peers that follow this inline filter. + # bgp -> bird. + import filter { + # Accept the route if its a valid network and is not our + # own network. (fns defined in local defs file). + if is_valid_network() && !is_self_net() then { + accept; + } + reject; + }; + # only send routes that follow this inline filter. + # bird -> bgp (other peers). + export filter { + # Only send if it's a valid netowrk and the route comes from + # static configuration or from BGP. + if is_valid_network() && source ~ [RTS_STATIC, RTS_BGP] then { + accept; + } + reject; + }; + + # Only allow for 1000 total routes to be imported. Once we hit + # 1000, block further routes. + import limit 1000 action block; +} + +# Include all of our peers! +include "/etc/bird/peers6/*"; +``` + +Whew, that was a rather larger file but that's pretty much it. Now for our local configuration and what a peer looks +like. + +## local6.conf + +``` +# Our globally unique identifier. Most people in dn42 us their internal router's ip. +# I wonder if a UUID or something would work here? +router id ; + +# Define our own Autonomous System Number here: +define OWNAS = ; + +# Define the router's ip: +define OWNIP = ; + +# Now for some helper functions (filters) that we use in the config above: + +# Returns if the `net` special variable (what the route is actually talking about, could be prefix), +# is in our own subnet. The `+` after the subnet is shorthand for "every possible address in this subnet". +# +# `a ~ b` means "a is in b". +function is_self_net() { + return net ~ [+]; +} + +# We use this function to limit our routes to those that are in the alanet. +function is_valid_network() { + return net ~ [ + 0200::/7+ # All address that start with 0200 are in our network. + ] +} +``` + +That's it for the local configuration! Define some constants and two simple helper functions. + +## peers/*.conf + +Peer configuration is super simple with the template we defined above: + +``` +protocol bgp from alapeers { + neighbor as ; # as == autonomous system +} +``` + +Done! Adding a new peer just means adding a new file in the peers directory. + +# ROA Tables + +Route origin authorization. This basically is a central list of who owns what routes and is allowed to send changes. +There's some crypto stuff you can do too, but that's really only important if we start letting people join arbitrarily. + +There is a concern that one of us messes up the config and starts sending out bad routes, but we'll cross that bridge +when we get there (large outage lol). + +# What's Next + +Now I need to actually test this with another router. I'll most likely setup another VPS or ask someone else to spin +something up. + +I need to determine a fake AS number, subnets, etc. \ No newline at end of file