Networking Azure

ExaBGP on Ubuntu 24.04 on Azure User Guide

| Product: ExaBGP on Ubuntu 24.04 LTS on Azure

Overview

ExaBGP is the Python "BGP swiss army knife" from Exa Networks. Rather than trying to be a traditional routing stack, it is built to let software talk BGP: it runs as a daemon driven by a plain text configuration, announces and withdraws routes on command through a named pipe CLI (exabgp cli), and streams every BGP event it receives as JSON to helper processes you write in any language. That makes it the tool of choice for injecting routes from scripts and controllers, steering or blackholing traffic during DDoS mitigation, announcing anycast service addresses with health checks, and prototyping BGP behaviour including FlowSpec.

A bare BGP daemon has nothing to show on its own, so the cloudimg image ships a complete, self-contained, provably-working BGP reference deployment on the one VM:

  • A primary ExaBGP (AS 65001) — the daemon you drive with sudo exabgp cli — which dials out to the demo peer and listens nowhere.
  • A demo peer ExaBGP (AS 65002) listening on an internal loopback range address only (10.255.255.2:179), authenticated with a per-VM TCP-MD5 key.
  • A real eBGP session between them that reaches established, with static routes propagating in both directions and every BGP event the peer receives logged as JSON to /var/log/exabgp/peer-events.jsonl.

The moment the VM boots, sudo exabgp cli show neighbor summary shows an established session, and one announce route command later you can watch your route arrive at the peer in real time — so you can see BGP working before you point the daemon at your own routers.

What is included:

  • ExaBGP 5.0.9 (exabgp and exabgp-cli) in a dedicated virtual environment at /opt/exabgp/venv, pinned to the official upstream release and verified by SHA-256
  • A primary ExaBGP (AS 65001) controlled over the named pipe CLI: sudo exabgp cli <command>
  • A demo peer ExaBGP (AS 65002) listening on the internal demo address only, authenticated with a per-VM TCP-MD5 key
  • JSON event logs of everything the daemons receive, at /var/log/exabgp/peer-events.jsonl and /var/log/exabgp/primary-events.jsonl
  • Per-VM TCP-MD5 key generated at first boot, at /root/exabgp-credentials.txt (root only)
  • Secure by default: no port open on the public interface at all — BGP listens only on the internal demo link
  • Runs as the unprivileged exabgp system account with only the two capabilities it needs
  • 24/7 cloudimg support

Deploying on Azure

Launch the image from the Azure Marketplace on a Standard_B2s (or larger) VM. ExaBGP is a lightweight pure Python daemon; Standard_B2s (2 vCPU / 4 GB) is ample for the reference deployment and for a modest production route injector. SSH in as azureuser with the key you selected at launch:

ssh azureuser@<vm-ip>

On first boot the appliance generates this VM's unique TCP-MD5 session key, renders both ExaBGP configurations, validates them with exabgp validate, brings the reference deployment up, and proves the session establishes, routes propagate both ways and a live CLI injection lands — before it writes its first boot sentinel. No two VMs ever share a key, and the captured image contains no key at all.

Step 1: The Authenticated eBGP Session is Established

show neighbor summary lists the primary daemon's peers and their session state. The demo peer (10.255.255.2, AS 65002) is established — and it only gets there because both ends share this VM's TCP-MD5 key:

sudo exabgp cli show neighbor summary

Expected output:

Peer            AS        up/down state       |     #sent     #recvd
10.255.255.2    65002     0:00:51 established          26         22

exabgp cli show neighbor summary with the demo peer established over the authenticated session

Step 2: Routes Propagate in Both Directions

The Adjacency RIB In shows what the primary has received from the demo peer (its static route 192.0.2.0/24), and the Adjacency RIB Out shows what the primary is advertising to it (its own static route 203.0.113.0/24):

sudo exabgp cli show adj-rib in
sudo exabgp cli show adj-rib out

Expected output:

10.255.255.2 ipv4 unicast 192.0.2.0/24 next-hop 10.255.255.2
10.255.255.2 ipv4 unicast 203.0.113.0/24 next-hop 10.255.255.1

The reverse direction is provable from a file. The demo peer runs an ExaBGP API process that appends every BGP event it receives to a JSON log — the arrival of the primary's route is recorded there:

sudo jq -c 'select(.type=="update") | .neighbor.message.update.announce // empty' /var/log/exabgp/peer-events.jsonl | head -3

Expected output:

{"ipv4 unicast":{"10.255.255.1":[{"nlri":"203.0.113.0/24"}]}}

The Adj-RIB-In and Adj-RIB-Out, and the demo peer's JSON event log recording the received route

Step 3: Inject a Route Live From the CLI

Route injection is what ExaBGP is for. Announce a prefix on the primary and watch it arrive at the demo peer in real time:

sudo exabgp cli announce route 198.51.100.0/24 next-hop 10.255.255.1
sleep 3
sudo jq -c 'select(.type=="update") | .neighbor.message.update.announce // empty' /var/log/exabgp/peer-events.jsonl | tail -1

Expected output:

{"ipv4 unicast":{"10.255.255.1":[{"nlri":"198.51.100.0/24"}]}}

Withdraw it just as easily — the peer logs the withdrawal too:

sudo exabgp cli withdraw route 198.51.100.0/24 next-hop 10.255.255.1
sleep 3
sudo jq -c 'select(.type=="update") | .neighbor.message.update.withdraw // empty' /var/log/exabgp/peer-events.jsonl | tail -1

Expected output:

{"ipv4 unicast":[{"nlri":"198.51.100.0/24"}]}

A live announce landing in the demo peer's JSON event log, then the withdraw

Step 4: Secure by Default

Nothing is exposed on the VM's routable interface. The primary daemon listens nowhere at all (it dials out), and the demo peer's BGP listener is bound to the internal demo address 10.255.255.2 only. This VM's TCP-MD5 key lives only in /root/exabgp-credentials.txt, readable by root alone:

sudo ss -lntH | awk '{print $1, $4}' | grep -E '10.255.255|:179'
sudo grep -v '^#' /root/exabgp-credentials.txt | head -2

Expected output (BGP on the internal demo link only — nothing on eth0):

LISTEN 10.255.255.2:179

ss showing BGP bound to the internal demo link only, and the per-VM TCP-MD5 key record

Peering With Your Own Routers

The primary daemon's configuration is at /etc/exabgp/exabgp.conf. To peer with your own routers, add a neighbor block for each one — a minimal example:

neighbor <remote-peer-ip> {
    router-id <this-vm-private-ip>;
    local-address <this-vm-private-ip>;
    local-as 65001;
    peer-as <remote-as>;
    md5-password "<a-shared-md5-key>";

    static {
        route <prefix-to-announce> next-hop self;
    }
}

Open TCP 179 to those peers in your Azure Network Security Group, then apply the change with sudo systemctl restart exabgp.

You can drive everything at runtime without editing the config: sudo exabgp cli announce route <prefix> next-hop <ip>, sudo exabgp cli withdraw route <prefix> next-hop <ip>, and sudo exabgp cli help for the full command set. To feed received routes into your own automation, add an ExaBGP process block — the shipped /usr/local/sbin/exabgp-route-log.sh is a working example that streams every event as JSON to a file.

Managing the Service

sudo systemctl status exabgp --no-pager
sudo journalctl -u exabgp -n 50 --no-pager

The demo peer (exabgp-peer.service) exists purely to make the shipped image a working reference deployment. Once you have configured your own peers you can disable it if you prefer a clean single daemon setup with sudo systemctl disable --now exabgp-peer.service.

Support

Every cloudimg deployment ships fully patched with unattended security updates enabled, and includes 24/7 cloudimg support. For help with this image, contact support through the Azure Marketplace listing or at cloudimg.co.uk.