Deployment guide

BIRD Internet routing daemon

Overview

This guide covers launching and verifying BIRD Internet Routing Daemon on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Hardened FRRouting, OpenBGPD & BGP Routing and Peering AMIs listing on AWS Marketplace. You select it as the software version when you subscribe and launch. The other routing and peering products in the listing are described on the listing's guide hub.

BIRD is an open source dynamic IP routing daemon developed by CZ.NIC. One daemon, bird, speaks BGP, OSPF, RIP, Babel, BFD and RPKI-to-router for IPv4 and IPv6, keeps its own routing tables, filters routes with a small programming language, and exports the routes it selects into the Linux kernel. Internet exchanges run it as a route server, and networks use it as an edge router, a route reflector and an anycast announcer. You control it at runtime with the birdc client.

The image ships BIRD 2.20.0 as the bird2 package from CZ.NIC Labs' signed Debian repository (pkg.labs.nic.cz/bird2), which publishes BIRD releases for Ubuntu 24.04 sooner than Ubuntu does. Apt trusts that repository with one signing key, and apt preferences keep you on the 2.x line. apt upgrade takes new 2.x releases from CZ.NIC, and it never pulls in Ubuntu's older bird2, a CZ.NIC development snapshot, BIRD 1 or BIRD 3.

A routing daemon needs peers before it has anything to show, so the image ships a working configuration that touches nothing outside the instance. You can see BIRD running end to end, from its own table to the kernel, without a single peer. No BGP, OSPF or other network protocol is configured, so BIRD opens no network port until you add one.

What you get:

  • device and kernel protocols for IPv4 and IPv6, so that BIRD tracks the interfaces and exports its routes into the Linux kernel
  • A static protocol, demo_static, that holds the three RFC 5737 documentation prefixes as blackhole routes. These ranges are never routed on the internet, and blackhole routes send no traffic anywhere
  • BIRD 2.20.0: the bird daemon and the birdc and birdcl clients in /usr/sbin, from the unmodified CZ.NIC bird2 package
  • bird.service, which runs BIRD as the unprivileged bird user, and bird-firstboot.service, which sets the router ID on first boot
  • The configuration in /etc/bird/bird.conf (owned by root:bird, mode 0640) and an instance record in /root/bird-instance.txt (root only)
  • The CZ.NIC repository source, its signing key and the apt preferences in /etc/apt/, and the list of packages that the build added, with their sources, in /usr/share/doc/sac-bird/package-provenance.txt
  • IP forwarding switched on in /etc/sysctl.d/99-sac-bird.conf, because a router forwards packets

Default user: ubuntu. Architecture: x86_64. Root volume: 8 GiB gp3.

Security posture

  • No routing port on your network until you add one. BIRD opens no TCP or UDP socket. Its only listener is the local control socket, so the instance's only network service is SSH (22). After you add a BGP peer, BIRD listens on TCP 179, and you open that port only to your peers' addresses.
  • No password and no network management interface. BIRD has no login of its own and no web or API port. You manage it with sudo birdc over a Unix socket, /run/bird/bird.ctl, owned by root:bird with mode 0660. Root and members of the bird group can open it, and the kernel refuses every other local user (Permission denied). This is a file permission, not a firewall rule, so changing the firewall does not open it.
  • No shared secret in the image. There is nothing to rotate, because the image holds no BIRD password, key or token. For TCP-MD5 on a BGP session, you choose the password and agree it with that peer.
  • Per-instance router ID. On first boot, BIRD's router ID is set to the instance's own private IPv4 address, read from IMDSv2. No two instances share an ID, and none carries the build machine's.
  • Unprivileged, sandboxed daemon. BIRD starts as root only to create its control socket. It then switches to the bird user and keeps four capabilities: CAP_NET_ADMIN (to program the kernel routing table), CAP_NET_BIND_SERVICE (to bind port 179), CAP_NET_RAW (for OSPF and RIP) and CAP_NET_BROADCAST. It runs under a systemd sandbox with a read-only /usr and /etc, no access to home directories, a private /tmp and no privilege gain.
  • No metadata access for the daemon. BIRD makes outbound connections to whatever neighbors you configure. bird.service is therefore blocked from reaching the instance metadata service at 169.254.169.254 and fd00:ec2::254. Your peers, your VPC and Amazon Time Sync (169.254.169.123) are unaffected.
  • IMDSv2 required. The AMI is registered with ImdsSupport v2.0, so instances launched from it refuse token-less (IMDSv1) metadata requests.
  • Crash recovery and fail-closed configuration. If BIRD crashes, systemd restarts it. If you restart with a configuration that bird -p rejects, the unit stays stopped and failed instead of restarting in a loop, and no control socket appears.
  • Per-instance identity. Every instance generates its own SSH host keys and machine ID.

Prerequisites

  • An AWS account and permission to launch EC2 instances
  • An EC2 key pair in the Region you launch in
  • A VPC subnet for the router, and the addresses and AS numbers of the peers it will talk to

Recommended instance type: m5.large. BIRD is very light, and the daemon in this configuration uses about 2 MB of memory. A small route server or edge router runs comfortably on t3.small or larger. Full internet tables from several peers need a few GB of memory per copy of the table.

Connecting to your instance

Version: BIRD Internet Routing Daemon on Ubuntu 24.04.

Operating system: Ubuntu 24.04 LTS.

SSH user: ubuntu.

1

Launch the instance

Option A: AWS Marketplace console

  • Open the SecureAnyCloud Hardened FRRouting, OpenBGPD & BGP Routing and Peering AMIs listing on AWS Marketplace, choose View purchase options, then Subscribe. One subscription covers every version in the listing.
  • After the subscription becomes active, choose Launch your software.
  • Choose One-click launch from AWS Marketplace (or Launch from EC2 Console for the full launch wizard). In Version, choose BIRD Internet Routing Daemon on Ubuntu 24.04, then choose your Region.
  • Pick an instance type (m5.large recommended; the software charge is $0.04 per vCPU per hour) and your key pair.
  • Review the security group. The listing's default group allows SSH (22) only. Restrict it to your own IP address. Step 9 covers allowing BGP from your peers.
  • Choose your VPC and subnet, leave the metadata defaults (the image already requires IMDSv2), then choose Launch.

Option B: AWS CLI

Run this on your own workstation. It creates a security group that allows SSH from your management address only, then launches the instance. Replace the AMI ID with the one shown for your Region on the Marketplace launch page. Replace the VPC, subnet, key pair and CIDR with your own:

SG=$(aws ec2 create-security-group --group-name bird-router \
  --description "BIRD router" --vpc-id vpc-0123456789abcdef0 \
  --query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 run-instances \
  --image-id ami-0123456789abcdef0 \
  --instance-type m5.large \
  --key-name my-key-pair \
  --security-group-ids "$SG" \
  --subnet-id subnet-0123456789abcdef0 \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=bird-router}]'

You do not need --metadata-options: the AMI already makes IMDSv2 required.

2

Connect and read the instance details

Connect as ubuntu with the key pair you chose:

ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>

First boot finishes within a minute of launch. It reads the instance's private IPv4 address from IMDSv2 and writes it into /etc/bird/bird.conf as the router ID. It then checks the configuration with bird -p and records the details in /root/bird-instance.txt. It starts nothing itself, because systemd starts BIRD after first boot finishes. The record holds no password or key. It is root-only because it describes the router:

sudo stat -c '%a %U:%G %n' /root/bird-instance.txt /etc/bird /etc/bird/bird.conf
sudo cat /root/bird-instance.txt
600 root:root /root/bird-instance.txt 750 root:bird /etc/bird 640 root:bird /etc/bird/bird.conf
# BIRD Internet Routing Daemon 2.20.0 on Ubuntu 24.04 - packaged by sac
# This instance holds NO BIRD password or key: BIRD is managed only with 'sudo birdc' over the local control socket. BIRD_ROUTER_ID=172.31.83.57 BIRD_VERSION=2.20.0 BIRD_CONFIG=/etc/bird/bird.conf BIRD_CONTROL_SOCKET=/run/bird/bird.ctl BIRD_GUIDE_URL=https://secureanycloud.com/stacks/bird-internet-routing-daemon/
#
# Try it:
sudo birdc show status
sudo birdc show protocols
sudo birdc show route
# ip route show proto bird

On later boots the first-boot unit sees that the instance is already prepared and changes nothing. A router ID you set yourself is kept across reboots.

3

Confirm the services and the package source

Both units are active, and BIRD reports version 2.20.0. The provenance file lists the packages that the build added on top of Ubuntu and where each came from: bird2 from CZ.NIC, and its one extra library from Ubuntu's main archive. apt-cache policy shows the installed version and the preference that holds it on CZ.NIC's 2.x line:

systemctl is-active bird-firstboot bird bird --version 2>&1 cat /usr/share/doc/sac-bird/package-provenance.txt apt-cache policy bird2 | sed -n '1,5p'
active active BIRD version 2.20.0
# Packages the sac build added on top of the sac Ubuntu 24.04 golden image, with the apt source of
# the installed version (d-506: Ubuntu main only, plus bird2 from CZ.NIC Labs' signed repository). bird2 2.20.0-cznic.1~noble https://pkg.labs.nic.cz/bird2 noble/main libssh-gcrypt-4 0.10.6-2ubuntu0.5 http://us-east-1.ec2.archive.ubuntu.com/ubuntu noble-updates/main bird2: Installed: 2.20.0-cznic.1~noble Candidate: 2.20.0-cznic.1~noble Version table: *** 2.20.0-cznic.1~noble 990
4

Verify that BIRD is running and programming the kernel

This is the positive control: the daemon is up, and the four protocols are up. The three documentation prefixes are in BIRD's table and in the Linux kernel routing table, where proto bird marks the routes BIRD installed:

sudo birdc show status
sudo birdc show protocols
sudo birdc show route ip route show proto bird
BIRD 2.20.0 ready. BIRD 2.20.0 Router ID is 172.31.83.57 Hostname is ip-172-31-83-57 Current server time is 2026-11-11 10:05:04.635 Last reboot on 2026-11-11 10:04:09.528 Last reconfiguration on 2026-11-11 10:04:09.528 Daemon is up and running BIRD 2.20.0 ready. Name Proto Table State Since Info dev1 Device --- up 10:04:09.528 kernel4 Kernel master4 up 10:04:09.528 kernel6 Kernel master6 up 10:04:09.528 demo_static Static master4 up 10:04:09.528 BIRD 2.20.0 ready. Table master4: 198.51.100.0/24 blackhole [demo_static 10:04:09.528] * (200) 192.0.2.0/24 blackhole [demo_static 10:04:09.528] * (200) 203.0.113.0/24 blackhole [demo_static 10:04:09.528] * (200) blackhole 192.0.2.0/24 metric 32 blackhole 198.51.100.0/24 metric 32 blackhole 203.0.113.0/24 metric 32

The router ID is this instance's private address. The kernel4 protocol is what copies BIRD's best routes into the kernel. Every route you later learn from a peer reaches the kernel the same way, subject to your filters.

5

Review the secure defaults

The following commands list every TCP and UDP listener on the instance, then every Unix socket that BIRD listens on. SSH listens on all interfaces (the security group restricts it). The rest are local system services: systemd-resolved's DNS stub, chrony on the loopback and the DHCP client. BIRD itself has no TCP or UDP socket at all:

ss -Hltun | awk '{print $1, $5}' | sort
sudo ss -Hlxp | grep '"bird"' | awk '{print $5}'
tcp 0.0.0.0:22 tcp 127.0.0.53%lo:53 tcp 127.0.0.54:53 tcp [::]:22 udp 127.0.0.1:323 udp 127.0.0.53%lo:53 udp 127.0.0.54:53 udp 172.31.83.57%ens5:68 udp [::1]:323 /run/bird/bird.ctl

The control socket is the only way in, and it has no password. Anyone who can open it can reconfigure or shut down the router. It is owned by root:bird with mode 0660, in a directory that only the bird group can enter. The ubuntu user is not in that group, so the kernel refuses birdc run as ubuntu without sudo. With sudo, it works:

ls -ld /run/bird /run/bird/bird.ctl id -nG ubuntu
sudo -u ubuntu birdc show status 2>&1 | grep -o 'Permission denied' || true
sudo birdc show status | grep 'Daemon is up'
drwxr-x--- 2 bird bird 60 Nov 11 10:04 /run/bird srw-rw---- 1 root bird 0 Nov 11 10:04 /run/bird/bird.ctl ubuntu adm cdrom
sudo dip lxd Permission denied Daemon is up and running

Let an operator use birdc without sudo

Add the operator's account to the bird group. The change applies at their next login:

sudo usermod -aG bird alice

Add only people whom you would trust with root on the router, because a member of the bird group can change every protocol, route and filter. Never expose the control socket on the network. If a remote system must drive BIRD, reach the socket over SSH instead.

6

Verify that metadata access is blocked for the daemon

bird.service carries a systemd IPAddressDeny rule for the instance metadata addresses, so nothing running inside BIRD can reach them. Each entry is a single address, so your peers, your VPC and the Amazon Time Sync Service at 169.254.169.123 keep working, as chrony shows. BIRD runs as the unprivileged bird user:

systemctl show -p IPAddressDeny bird.service chronyc -n sources | grep 169.254 ps -o user,args -C bird
IPAddressDeny=::ffff:169.254.169.254/128 fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32 ::ffff:168.63.129.16/128 ^* 169.254.169.123 3 4 177 1 -3792ns[ +102us] +/- 499us USER COMMAND bird /usr/sbin/bird -f -c /etc/bird/bird.conf -u bird -g bird

You can observe the block from inside the service. The first command runs curl in bird.service's own control group and asks the metadata service for an IMDSv2 token. The request never gets an answer (000). The second command runs the same request as root outside the service and gets 200:

CG=$(systemctl show -p ControlGroup --value bird.service)
sudo sh -c 'echo $$ > "/sys/fs/cgroup$1/cgroup.procs" && exec curl -s -m 4 -o /dev/null -w "%{http_code}\n" -X PUT -H "X-aws-ec2-metadata-token-ttl-seconds: 60" http://169.254.169.254/latest/api/token' _ "$CG" || true curl -s -m 4 -o /dev/null -w "%{http_code}\n" -X PUT -H "X-aws-ec2-metadata-token-ttl-seconds: 60" http://169.254.169.254/latest/api/token
000 200

Your VPC is deliberately not blocked. BIRD's peers are normally other hosts in your VPC or across a VPN, so bird.service may reach any address except the metadata service. Limit who BIRD talks to through its configuration (only the neighbors you define get a session) and through the security group.

7

Forwarding and the EC2 source/destination check

The image is a router, so IP forwarding is on for IPv4 and IPv6:

cat /etc/sysctl.d/99-sac-bird.conf sysctl net.ipv4.ip_forward net.ipv6.conf.all.forwarding
# sac BIRD appliance: this instance is an IP router. On EC2 also turn OFF the instance's source/destination # check, or the VPC drops the traffic it forwards (see the guide). net.ipv4.ip_forward = 1 net.ipv6.conf.all.forwarding = 1 net.ipv4.ip_forward = 1 net.ipv6.conf.all.forwarding = 1

If this instance will forward traffic for the routes it learns, rather than only exchange routes, two EC2 settings matter:

  • Turn off the source/destination check. By default, EC2 drops any packet whose source or destination is not the instance's own address, and that includes every packet a router forwards. In the EC2 console, choose the instance, then Actions, Networking, Change source/destination check, and select Stop. From the AWS CLI, run:

bash aws ec2 modify-instance-attribute --instance-id i-0123456789abcdef0 --no-source-dest-check

  • Point your VPC route tables at the instance (its network interface) for the prefixes that it should carry.

The image ships no firewall rule of its own. Use the security group to decide what may reach the router.

If the instance only exchanges routes, for example as a route server or route reflector, leave the source/destination check on. To stop forwarding altogether, set both values to 0 in /etc/sysctl.d/99-sac-bird.conf and run sudo sysctl --system.

8

Peer with your own routers

First, find this instance's private IP. BGP peers on your VPC connect to it, never to the public address. The metadata service needs an IMDSv2 token:

TOKEN=$(curl -s -X PUT http://169.254.169.254/latest/api/token -H 'X-aws-ec2-metadata-token-ttl-seconds: 60')
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/local-ipv4; echo
172.31.83.57

The configuration is /etc/bird/bird.conf. It ends with a commented-out BGP example. Edit the file with sudo and your editor (for example sudo nano /etc/bird/bird.conf), and add a protocol per peer.

The following example is a minimal eBGP session with one router. It authenticates with TCP-MD5, accepts only the prefixes you expect from that peer and announces only your own:

# Prefixes this router accepts from the core, and the ones it announces. define CORE_PREFIXES = [ 10.20.0.0/16{16,24} ]; define MY_PREFIXES = [ 10.30.0.0/16 ]; protocol bgp core1 { description "core-router-1"; local 172.31.83.57 as 64512; neighbor 172.31.80.10 as 64513; password "replace-with-the-key-you-agree-with-this-peer"; ttl security on;
# directly connected peer only (GTSM, RFC 5082) ipv4 { import where net ~ CORE_PREFIXES; export where net ~ MY_PREFIXES; }; }

Replace the AS numbers, addresses, prefixes and password with your own:

  • password turns on TCP-MD5 for the session. The peer must use the same password, and a session with a wrong or missing password never comes up. Generate a long random password yourself (for example head -c 18 /dev/urandom | base64) and agree it with that peer only.
  • ttl security on makes BIRD send with TTL 255 and drop BGP packets that arrive with a lower TTL. A spoofer more than one hop away therefore cannot reach the session. Enable it on both ends, and only for directly connected peers. For a peer several hops away, leave it out and use multihop instead.
  • The import and export filters decide what crosses the session. An eBGP session should never be import all or export all. The export filter above announces only your own prefixes, and only those that BIRD actually has in its table.

The demo_static blackholes stay in place unless you remove that protocol. Remove it when you no longer need the demo. Check the file, then apply it to the running daemon without a restart:

sudo birdc configure check
sudo birdc configure
sudo birdc show protocols all core1

birdc configure check parses the file without touching the running daemon, and prints Configuration OK or the line and reason it rejects. birdc configure applies the file. If the new file does not parse, BIRD keeps running with the old configuration and reports the error. show protocols all core1 shows the session state (Established once the peer is up) and how many routes were imported, filtered and exported.

9

Open BGP to your peers in the security group

Allow TCP 179 from your peers only in the instance's security group. In the EC2 console choose the instance, open Security, choose the security group, then Edit inbound rules, and add this rule:

  • Protocol: TCP
  • Port: 179
  • Source: each peer's address (/32)
  • Purpose: BGP sessions

Add one rule per peer rather than a subnet-wide range. BIRD accepts a session only from neighbors it has a protocol for, and the security group stops everyone else from reaching port 179 at all. OSPF (IP protocol 89), RIP (UDP 520), Babel (UDP 6696) and BFD (UDP 3784 and 3785) need their own rules if you use them. OSPF also needs multicast, which a VPC does not carry, so on AWS it is normally run over a GRE or VPN tunnel.

10

Manage the service

BIRD logs through syslog, so its messages are in the journal under bird.service:

systemctl status bird --no-pager -n 0
sudo journalctl -u bird -b --no-pager -o cat | tail -n 4
● bird.service - BIRD Internet Routing Daemon (sac) Loaded: loaded (/etc/systemd/system/bird.service; enabled; preset: enabled) Drop-In: /etc/systemd/system/bird.service.d └─50-sac-imds-deny.conf Active: active (running) since Wed 2026-11-11 10:04:10 UTC; 54s ago Docs: https://secureanycloud.com/stacks/bird-internet-routing-daemon/ https://bird.nic.cz/ man:bird(8) Process: 834 ExecStartPre=/usr/lib/bird/prepare-environment (code=exited, status=0/SUCCESS) Process: 876 ExecStartPre=/usr/sbin/bird -p -c ${BIRD_CONF} (code=exited, status=0/SUCCESS) Process: 901 ExecStartPost=/usr/local/sbin/bird-ctl-socket (code=exited, status=0/SUCCESS) Main PID: 899 (bird) Tasks: 1 (limit: 3748) Memory: 1.7M (peak: 2.8M) CPU: 164ms CGroup: /system.slice/bird.service └─899 /usr/sbin/bird -f -c /etc/bird/bird.conf -u bird -g bird Starting bird.service - BIRD Internet Routing Daemon (sac)... Started bird-ctl-socket: /run/bird/bird.ctl root:bird 0660 in /run/bird bird:bird 0750; BIRD is up Started bird.service - BIRD Internet Routing Daemon (sac).

The drop-in line confirms the metadata block is applied. Prefer sudo birdc configure to a restart: it applies a new configuration without dropping sessions whose settings did not change. Common commands:

sudo birdc show protocols
# every protocol and its state
sudo birdc show protocols all <name>
# one protocol in detail, with route counts
sudo birdc show route protocol <name>
# the routes one peer sent you, after import filters
sudo birdc show route export <name>
# what you announce to one peer
sudo birdc show route for 10.20.1.1
# which route matches an address
sudo birdc reload in <name>
# re-run import filters without dropping the session
sudo birdc restart <name>
# reset one session
11

Update BIRD

unattended-upgrades installs Ubuntu's security updates automatically. BIRD itself comes from CZ.NIC's repository, so it updates when you choose. These commands show whether a newer 2.x release is available:

sudo apt-get update apt-cache policy bird2 | sed -n '1,3p'

Then install it:

sudo apt-get install --only-upgrade bird2

If apt asks about /etc/bird/bird.conf ("A new version of configuration file ... is available, but the version installed currently has been locally modified"), choose keep the local version currently installed. That file is your configuration, and the first-boot router ID is in it. The upgrade restarts BIRD, so sessions re-establish. The file's root:bird 0640 ownership is registered with dpkg-statoverride, so upgrades keep it.

The apt preferences in /etc/apt/preferences.d/sac-bird2 keep this instance on BIRD 2. BIRD 3 is a new major release, with multithreading and some configuration changes, and CZ.NIC publishes it as a separate bird3 package in its own repository. The preferences block it, so an upgrade never switches you over by accident. To evaluate BIRD 3, test a copy of your configuration on a separate instance first.

12

Security recommendations

  • Keep the security group tight. Allow TCP 179 only from each peer's own address, and SSH only from your management address.
  • Authenticate every session. Give each neighbor its own TCP-MD5 password, agreed with that peer. Enable TTL security for directly connected peers.
  • Filter explicitly. Accept only the prefixes you expect from each peer and announce only your own. Never use import all or export all on an eBGP session.
  • Guard the control socket. It has no password, so whoever can open it controls the router. Keep the bird group to trusted operators and keep the socket permissions as shipped.
  • Keep the OS and BIRD patched. Unattended security upgrades are on for the Ubuntu packages. Check for new BIRD 2.x releases with apt-cache policy bird2 after sudo apt-get update.

Support and licensing

BIRD is developed by CZ.NIC and distributed under the GNU General Public License, version 2 or later. The license and copyright notice are in /usr/share/doc/bird2/copyright, and CZ.NIC publishes the source at bird.nic.cz. SecureAnyCloud provides the packaging, the pinned signed repository, the first-boot router ID, the secure defaults and 24/7 support.

SecureAnyCloud is not affiliated with or endorsed by CZ.NIC. BIRD is a mark of its owner and is used here only to identify the software.

For help with deployment or configuration, email sales@secureanycloud.com or visit SecureAnyCloud support.

Deploy on AWS

Launch BIRD Internet Routing Daemon on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS