-
Notifications
You must be signed in to change notification settings - Fork 459
Security Best Practices
Securing your ExaBGP deployment is critical, as BGP is a foundational protocol for network routing. This guide covers security hardening for ExaBGP in production environments.
- Overview
- Authentication
- BGP Security Features
- System Security
- Network Security
- API Security
- Monitoring and Logging
- Defense in Depth
- Security Checklist
- See Also
Important: ExaBGP does NOT manipulate the routing table (RIB/FIB) directly, but it does inject routes into BGP. Unauthorized access to ExaBGP can allow attackers to manipulate routing, cause traffic black-holing, or perform man-in-the-middle attacks.
- Authentication - Verify BGP peer identity
- Authorization - Limit what can be announced
- Integrity - Detect message tampering
- Confidentiality - Protect sensitive data (optional)
- Availability - Prevent DoS attacks
- Least Privilege - Run with minimal permissions
- Defense in Depth - Multiple security layers
Threats to consider:
- Unauthorized BGP peering
- Route hijacking via compromised ExaBGP
- Denial of service attacks
- Compromised API processes
- Information disclosure
- Man-in-the-middle attacks
MD5 authentication (RFC 2385) protects BGP sessions against spoofed packets and unauthorized peers.
# /etc/exabgp/exabgp.conf
neighbor 192.0.2.1 {
router-id 192.0.2.10;
local-address 192.0.2.10;
local-as 65001;
peer-as 65001;
# MD5 authentication
md5-password "your-strong-password-here";
family {
ipv4 unicast;
}
}# BAD: Weak password
md5-password "password123";
# BAD: Short password
md5-password "abc";
# GOOD: Strong random password
md5-password "K8$mP9#nQ2@vR5!wX7^yZ4&bC1";
# BEST: Use password from secure file
md5-password include "/etc/exabgp/secrets/neighbor-192.0.2.1.pwd";# Create secrets directory
mkdir -p /etc/exabgp/secrets
chmod 700 /etc/exabgp/secrets
# Generate strong password
openssl rand -base64 32 > /etc/exabgp/secrets/neighbor-192.0.2.1.pwd
chmod 600 /etc/exabgp/secrets/neighbor-192.0.2.1.pwd
chown exabgp:exabgp /etc/exabgp/secrets/neighbor-192.0.2.1.pwd
# Password content (example)
cat /etc/exabgp/secrets/neighbor-192.0.2.1.pwd
# K8$mP9#nQ2@vR5!wX7^yZ4&bC1neighbor 192.0.2.1 {
router-id 192.0.2.10;
local-address 192.0.2.10;
local-as 65001;
peer-as 65001;
# Load password from secure file
md5-password include "/etc/exabgp/secrets/neighbor-192.0.2.1.pwd";
family {
ipv4 unicast;
}
}Always verify peer AS numbers and addresses:
neighbor 192.0.2.1 {
# Verify peer identity
peer-as 65001; # Expected AS number
# Only accept connections from this address
local-address 192.0.2.10;
# Prevent address spoofing
md5-password "strong-password";
}GTSM (RFC 5082) prevents attacks from non-adjacent routers by requiring TTL=255.
neighbor 192.0.2.1 {
router-id 192.0.2.10;
local-address 192.0.2.10;
local-as 65001;
peer-as 65001;
# Enable TTL security
ttl-security 255;
family {
ipv4 unicast;
}
}How it works:
- ExaBGP sends packets with TTL=255
- Expects to receive packets with TTL=255
- Rejects packets with TTL < 255
- Effective against remote attackers (packets traversing routers will have TTL < 255)
Protect against route table overflow attacks:
neighbor 192.0.2.1 {
router-id 192.0.2.10;
local-address 192.0.2.10;
local-as 65001;
peer-as 65001;
# Limit routes received from peer
maximum-prefix {
ipv4 unicast 10000; # Max 10,000 IPv4 routes
ipv6 unicast 5000; # Max 5,000 IPv6 routes
}
family {
ipv4 unicast;
ipv6 unicast;
}
}Note: ExaBGP is primarily used for route announcement, not reception. This feature is most relevant when receive-routes is enabled.
Filter what routes can be announced:
#!/usr/bin/env python3
# /etc/exabgp/announce-with-validation.py
import sys
import ipaddress
# Whitelist of allowed prefixes
ALLOWED_PREFIXES = [
ipaddress.ip_network("198.51.100.0/24"),
ipaddress.ip_network("203.0.113.0/24"),
]
def is_allowed_prefix(prefix_str):
"""Check if prefix is in whitelist"""
try:
prefix = ipaddress.ip_network(prefix_str)
return any(prefix.subnet_of(allowed) for allowed in ALLOWED_PREFIXES)
except:
return False
def announce_route(prefix, next_hop):
"""Announce route only if allowed"""
if is_allowed_prefix(prefix):
print(f"announce route {prefix} next-hop {next_hop}", flush=True)
sys.stderr.write(f"Announced allowed prefix: {prefix}\n")
else:
sys.stderr.write(f"SECURITY: Blocked unauthorized prefix: {prefix}\n")
# Example usage
announce_route("198.51.100.0/24", "192.0.2.10") # Allowed
announce_route("10.0.0.0/8", "192.0.2.10") # BlockedNever run ExaBGP as root. Create a dedicated user with minimal privileges.
# Create exabgp user and group
sudo groupadd -r exabgp
sudo useradd -r -g exabgp -s /bin/false -c "ExaBGP daemon" exabgp
# Create necessary directories
sudo mkdir -p /etc/exabgp
sudo mkdir -p /var/run/exabgp
sudo mkdir -p /var/log/exabgp
# Set ownership
sudo chown -R exabgp:exabgp /etc/exabgp
sudo chown -R exabgp:exabgp /var/run/exabgp
sudo chown -R exabgp:exabgp /var/log/exabgp
# Set permissions
sudo chmod 750 /etc/exabgp
sudo chmod 750 /var/run/exabgp
sudo chmod 750 /var/log/exabgp# /etc/systemd/system/exabgp.service
[Unit]
Description=ExaBGP
Documentation=https://github.com/Exa-Networks/exabgp/wiki
After=network.target
[Service]
Type=simple
# Security: Run as non-root user
User=exabgp
Group=exabgp
# Security: Restrict capabilities
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
# Security: Filesystem protections
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/run/exabgp /var/log/exabgp
PrivateTmp=true
# Security: Namespace isolation
PrivateDevices=true
ProtectKernelTunables=true
ProtectControlGroups=true
RestrictRealtime=true
# Security: System call filtering
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources
# ExaBGP command
ExecStart=/usr/local/bin/exabgp /etc/exabgp/exabgp.conf
# Restart policy
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target# Start service
sudo systemctl start exabgp
# Check process user
ps aux | grep exabgp
# Should show 'exabgp' user, NOT 'root'
# Verify with systemd
sudo systemctl status exabgpRestrict access to configuration files:
# Configuration files: readable only by exabgp user
chmod 640 /etc/exabgp/exabgp.conf
chown exabgp:exabgp /etc/exabgp/exabgp.conf
# Secrets: readable only by exabgp user
chmod 600 /etc/exabgp/secrets/*
chown exabgp:exabgp /etc/exabgp/secrets/*
# Scripts: executable by exabgp user
chmod 750 /etc/exabgp/*.py
chown exabgp:exabgp /etc/exabgp/*.py
# Logs: writable by exabgp user
chmod 750 /var/log/exabgp
chown exabgp:exabgp /var/log/exabgpOnly enable what you need:
neighbor 192.0.2.1 {
router-id 192.0.2.10;
local-address 192.0.2.10;
local-as 65001;
peer-as 65001;
# Only enable required address families
family {
ipv4 unicast; # Enable only what's needed
# ipv6 unicast; # Disabled if not needed
# ipv4 flow; # Disabled if not needed
}
# Disable unnecessary capabilities
capability {
add-path disable; # Disable if not needed
graceful-restart disable; # Disable if not needed
}
}Restrict BGP access to authorized peers only.
#!/bin/bash
# /etc/exabgp/firewall-rules.sh
# BGP peers
PEER1="192.0.2.1"
PEER2="192.0.2.2"
LOCAL_IP="192.0.2.10"
# Default: DROP all BGP traffic
iptables -A INPUT -p tcp --dport 179 -j DROP
iptables -A OUTPUT -p tcp --sport 179 -j DROP
# Allow BGP from authorized peers only
iptables -I INPUT -s $PEER1 -d $LOCAL_IP -p tcp --dport 179 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -I INPUT -s $PEER2 -d $LOCAL_IP -p tcp --dport 179 -m state --state NEW,ESTABLISHED -j ACCEPT
# Allow BGP to authorized peers only
iptables -I OUTPUT -s $LOCAL_IP -d $PEER1 -p tcp --sport 179 -m state --state ESTABLISHED -j ACCEPT
iptables -I OUTPUT -s $LOCAL_IP -d $PEER2 -p tcp --sport 179 -m state --state ESTABLISHED -j ACCEPT
# Log dropped BGP attempts
iptables -A INPUT -p tcp --dport 179 -j LOG --log-prefix "DROPPED BGP: "#!/usr/sbin/nft -f
# /etc/exabgp/nftables.conf
table inet filter {
set bgp_peers {
type ipv4_addr
elements = { 192.0.2.1, 192.0.2.2 }
}
chain input {
type filter hook input priority 0; policy drop;
# Allow established connections
ct state established,related accept
# Allow BGP from authorized peers only
tcp dport 179 ip saddr @bgp_peers accept
# Log and drop unauthorized BGP
tcp dport 179 log prefix "DROPPED BGP: " drop
}
chain output {
type filter hook output priority 0; policy accept;
}
}Bind to specific local addresses:
neighbor 192.0.2.1 {
# Bind to specific local IP
local-address 192.0.2.10;
# This prevents accepting connections on other interfaces
router-id 192.0.2.10;
local-as 65001;
peer-as 65001;
}Use separate network for BGP management:
ββββββββββββββββββββββββ
β Production Network β
β (Data traffic) β
ββββββββββββββββββββββββ
ββββββββββββββββββββββββ
β Management Network β <- ExaBGP listens here
β (BGP peering) β
β 192.0.2.0/24 β
ββββββββββββββββββββββββ
Configuration:
# Listen only on management interface
neighbor 192.0.2.1 {
local-address 192.0.2.10; # Management network
# ...
}Only run trusted API processes:
process healthcheck {
# Use absolute paths
run /usr/local/bin/exabgp-healthcheck;
# NOT: run /tmp/untrusted-script.py
}Sanitize input in API processes:
#!/usr/bin/env python3
# /etc/exabgp/secure-announce.py
import sys
import re
import ipaddress
def validate_route(prefix, next_hop):
"""Validate route before announcing"""
# Validate prefix format
try:
network = ipaddress.ip_network(prefix)
except ValueError:
sys.stderr.write(f"SECURITY: Invalid prefix format: {prefix}\n")
return False
# Validate next-hop format
try:
next_hop_ip = ipaddress.ip_address(next_hop)
except ValueError:
sys.stderr.write(f"SECURITY: Invalid next-hop format: {next_hop}\n")
return False
# Check prefix is not too specific
if network.prefixlen > 24:
sys.stderr.write(f"SECURITY: Prefix too specific: {prefix}\n")
return False
# Check prefix is not default route
if network.prefixlen == 0:
sys.stderr.write(f"SECURITY: Cannot announce default route\n")
return False
return True
def announce_route(prefix, next_hop):
if validate_route(prefix, next_hop):
print(f"announce route {prefix} next-hop {next_hop}", flush=True)
else:
sys.stderr.write(f"SECURITY: Blocked invalid route: {prefix} -> {next_hop}\n")
# Example
announce_route("198.51.100.0/24", "192.0.2.10") # Valid
announce_route("0.0.0.0/0", "192.0.2.10") # Blocked (default route)
announce_route("198.51.100.0/32", "192.0.2.10") # Blocked (too specific)Never use shell commands with unsanitized input:
# BAD: Command injection vulnerability
route = user_input # e.g., "198.51.100.0/24; rm -rf /"
os.system(f"echo 'announce route {route} next-hop self'")
# GOOD: Use safe API
print(f"announce route {validated_route} next-hop self", flush=True)Run API processes with minimal privileges:
# Systemd service for API process
[Service]
User=exabgp-api
Group=exabgp-api
CapabilityBoundingSet=
PrivateDevices=true
ProtectKernelTunables=true
NoNewPrivileges=true# /etc/exabgp/exabgp.conf
# Logging configuration
log {
all = true;
destination = /var/log/exabgp/exabgp.log;
level = INFO;
}
neighbor 192.0.2.1 {
# ... neighbor config ...
}Monitor logs for security events:
# Monitor for authentication failures
tail -f /var/log/exabgp/exabgp.log | grep -i "authentication\|failed\|denied"
# Monitor for unexpected disconnections
tail -f /var/log/exabgp/exabgp.log | grep -i "down\|closed\|reset"
# Monitor for route changes
tail -f /var/log/exabgp/exabgp.log | grep -i "announce\|withdraw"Send logs to SIEM or centralized logging:
# rsyslog configuration
# /etc/rsyslog.d/exabgp.conf
$ModLoad imfile
$InputFileName /var/log/exabgp/exabgp.log
$InputFileTag exabgp:
$InputFileStateFile exabgp-state
$InputFileSeverity info
$InputFileFacility local0
$InputRunFileMonitor
local0.* @@siem.example.com:514Set up alerts for security events:
#!/usr/bin/env python3
# /etc/exabgp/security-monitor.py
import sys
import re
import smtplib
def send_alert(message):
"""Send security alert email"""
# Configure email settings
smtp_server = "smtp.example.com"
from_addr = "[email protected]"
to_addr = "[email protected]"
msg = f"Subject: ExaBGP Security Alert\n\n{message}"
server = smtplib.SMTP(smtp_server)
server.sendmail(from_addr, to_addr, msg)
server.quit()
# Monitor ExaBGP logs
log_file = "/var/log/exabgp/exabgp.log"
with open(log_file, 'r') as f:
for line in f:
# Alert on authentication failures
if re.search(r'authentication.*fail', line, re.IGNORECASE):
send_alert(f"Authentication failure: {line}")
# Alert on unexpected peer disconnections
if re.search(r'peer.*down', line, re.IGNORECASE):
send_alert(f"Peer disconnection: {line}")Implement multiple layers of security:
- Firewall rules restricting BGP access
- Network segmentation (management network)
- DDoS protection
- MD5 authentication
- TTL security
- Prefix limits
- Non-root execution
- File permissions
- Systemd hardening
- Input validation
- Route filtering
- API security
- Comprehensive logging
- Security alerting
- Incident response procedures
# /etc/exabgp/exabgp.conf - Hardened Configuration
# Logging
log {
all = true;
destination = /var/log/exabgp/exabgp.log;
level = INFO;
}
# Secure API process
process healthcheck {
run /usr/local/bin/exabgp-healthcheck;
encoder text;
}
# Secure neighbor configuration
neighbor 192.0.2.1 {
router-id 192.0.2.10;
# Bind to specific local address
local-address 192.0.2.10;
# AS verification
local-as 65001;
peer-as 65001;
# MD5 authentication
md5-password include "/etc/exabgp/secrets/neighbor-192.0.2.1.pwd";
# TTL security
ttl-security 255;
# Only enable required address families
family {
ipv4 unicast;
}
# Disable unnecessary capabilities
capability {
add-path disable;
}
# API process
api {
processes [ healthcheck ];
}
}Use this checklist for production deployments:
- MD5 authentication enabled on all peers
- Strong passwords (32+ characters, random)
- Passwords stored in separate files with 600 permissions
- Peer AS numbers verified
- Local/peer addresses explicitly configured
- TTL security enabled (where supported)
- Maximum prefix limits configured
- Only required address families enabled
- Unnecessary capabilities disabled
- Route filtering implemented in API processes
- ExaBGP runs as non-root user (exabgp)
- Configuration files have 640 permissions
- Secret files have 600 permissions
- Scripts have 750 permissions
- Systemd hardening enabled (ProtectSystem, PrivateTmp, etc.)
- Firewall rules restrict BGP to authorized peers
- Local address binding configured
- Management network separated from production
- DDoS protection in place
- Only trusted API processes configured
- Absolute paths used for process execution
- Input validation in API processes
- No shell command injection vulnerabilities
- API processes run with minimal privileges
- Comprehensive logging enabled
- Logs sent to centralized logging system
- Security alerts configured
- Log rotation configured
- Regular log reviews scheduled
- Configuration changes version controlled
- Access to ExaBGP host restricted
- Incident response procedures documented
- Regular security audits scheduled
- Security patches applied promptly
- Configuration Syntax - ExaBGP configuration reference
- Debugging - Troubleshooting and diagnostics
- Monitoring - Production monitoring
- Security Hardening - System hardening guide
- BGP Security - RFC 7454: BGP Operations and Security
π» Ghost written by Claude (Anthropic AI)
π Home
π Getting Started
π§ API
π‘οΈ Use Cases
π Address Families
βοΈ Configuration
π Operations
π Reference
- Architecture
- BGP State Machine
- Communities (RFC)
- Extended Communities
- BGP Ecosystem
- Capabilities (AFI/SAFI)
- RFC Support
π Migration
π Community
π External
- GitHub Repo β
- Slack β
- Issues β
π» Ghost written by Claude (Anthropic AI)