Skill v1.0.1
currentAutomated scan100/100+3 new
name: implementing-bgp-security-with-rpki description: Implement BGP route origin validation using RPKI with Route Origin Authorizations, RPKI-to-Router protocol, and ROV policies on Cisco and Juniper routers to prevent route hijacking. domain: cybersecurity subdomain: network-security tags:
- bgp
- rpki
- route-origin-validation
- rov
- roa
- route-hijacking
- internet-routing
- bgp-security
- prefix-hijack
version: '1.0' author: mahipal license: Apache-2.0 nist_csf:
- PR.IR-01
- DE.CM-01
- ID.AM-03
- PR.DS-02
mitre_attack:
- T1046
- T1040
- T1557
- T1071
Implementing BGP Security with RPKI
Overview
Resource Public Key Infrastructure (RPKI) provides cryptographic validation of BGP route origins to prevent route hijacking and accidental route leaks. RPKI enables network operators to create Route Origin Authorizations (ROAs) that declare which Autonomous Systems (ASes) are authorized to originate specific IP prefixes. BGP routers validate received route announcements against RPKI data through Route Origin Validation (ROV), rejecting routes with invalid origins. This skill covers creating ROAs through Regional Internet Registries (RIRs), deploying RPKI validator software, configuring ROV on Cisco IOS-XE and Juniper Junos routers, and implementing BGP filtering policies based on RPKI validation state.
When to Use
- When deploying or configuring implementing bgp security with rpki capabilities in your environment
- When establishing security controls aligned to compliance requirements
- When building or improving security architecture for this domain
- When conducting security assessments that require this implementation
Prerequisites
- IP address space allocated from an RIR (ARIN, RIPE, APNIC, AFRINIC, LACNIC)
- RIR member portal access for ROA creation
- BGP routers (Cisco IOS-XE 16.x+, Juniper Junos 12.2+, or similar)
- Linux server for RPKI validator/cache (Routinator, FORT, or OctoRPKI)
- Understanding of BGP routing and AS path concepts
Core Concepts
RPKI Architecture
┌──────────────────────────────────────────────┐│ Regional Internet Registries ││ (ARIN, RIPE, APNIC, AFRINIC, LACNIC) ││ ││ ┌─────────────────────────────────────────┐ ││ │ Trust Anchor (Root CA Certificate) │ ││ │ ├── CA Certificate (ISP/Organization) │ ││ │ │ ├── ROA: AS64512 → 198.51.100.0/24 │ ││ │ │ └── ROA: AS64512 → 2001:db8::/32 │ ││ │ └── CA Certificate (Another Org) │ ││ │ └── ROA: AS64513 → 203.0.113.0/24 │ ││ └─────────────────────────────────────────┘ │└──────────────────────────────────────────────┘│ rsync/RRDP▼┌──────────────────────┐│ RPKI Validator/Cache │ (Routinator, FORT, OctoRPKI)│ Validates ROAs ││ Serves VRPs to RTR │└──────────────────────┘│ RTR Protocol (TCP 8323)▼┌──────────────────────┐│ BGP Router ││ Performs ROV ││ Applies policy: ││ Valid → Accept ││ Invalid → Reject ││ NotFound → Accept │└──────────────────────┘
RPKI Validation States
| State | Meaning | Recommended Action | |
|---|---|---|---|
| Valid | ROA exists, origin AS and prefix match | Accept route (prefer) | |
| Invalid | ROA exists, but origin AS or prefix length mismatch | Reject route | |
| NotFound | No ROA covers this prefix | Accept (but lower preference) |
Route Origin Authorization (ROA)
A ROA is a signed object that states:
- Prefix: The IP address range (e.g., 198.51.100.0/24)
- Origin AS: The AS authorized to originate this prefix (e.g., AS64512)
- Max Length: Maximum prefix length that can be announced (e.g., /24)
Workflow
Step 1: Create ROAs at Your RIR
ARIN (North America):
- Log into ARIN Online portal
- Navigate to Routing Security > Route Origin Authorizations
- Create ROA:
- Prefix: 198.51.100.0/24
- Origin AS: AS64512
- Max Length: /24 (set equal to prefix length to prevent sub-prefix hijacking)
- Sign and submit
RIPE NCC (Europe):
- Log into RIPE NCC LIR Portal
- Navigate to Certification (RPKI) > ROAs
- Create ROA with prefix, origin AS, and max prefix length
Step 2: Deploy RPKI Validator (Routinator)
# Install Routinator on Ubuntusudo apt install -y routinator# Or install via Cargo (Rust)curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shcargo install routinator# Initialize Routinator (accept TALs)routinator init --accept-arin-rpa# Start Routinator in RTR server moderoutinator server \--rtr 0.0.0.0:8323 \--http 0.0.0.0:8080 \--refresh 600 \--retry 60 \--expire 7200# Run as systemd servicecat > /etc/systemd/system/routinator.service << 'SYSTEMD'[Unit]Description=Routinator RPKI ValidatorAfter=network.target[Service]Type=simpleUser=routinatorExecStart=/usr/bin/routinator server --rtr 0.0.0.0:8323 --http 0.0.0.0:8080Restart=alwaysRestartSec=10[Install]WantedBy=multi-user.targetSYSTEMDsudo systemctl enable routinatorsudo systemctl start routinator# Verify Routinator is serving datacurl http://localhost:8080/api/v1/statuscurl http://localhost:8080/api/v1/validity/AS64512/198.51.100.0/24# View Validated ROA Payloads (VRPs)routinator vrps --format json | head -50
Step 3: Configure ROV on Cisco IOS-XE
! Configure RPKI cache server connectionrouter bgp 64512bgp rpki server tcp 10.0.5.50 port 8323 refresh 600! Verify RPKI sessionshow bgp rpki servershow bgp rpki table! Create route-map for RPKI-based filteringroute-map RPKI-FILTER permit 10match rpki validset local-preference 200route-map RPKI-FILTER permit 20match rpki not-foundset local-preference 100route-map RPKI-FILTER deny 30match rpki invalid! Apply to BGP neighborsrouter bgp 64512address-family ipv4 unicastneighbor 198.51.100.1 route-map RPKI-FILTER inneighbor 203.0.113.1 route-map RPKI-FILTER inaddress-family ipv6 unicastneighbor 2001:db8::1 route-map RPKI-FILTER in! Verify ROV operationshow bgp ipv4 unicast rpki validationshow bgp ipv4 unicast 198.51.100.0/24show ip bgp rpki tableshow ip bgp neighbors 198.51.100.1 rpki state
Step 4: Configure ROV on Juniper Junos
# Configure RPKI cache connectionset routing-options validation group RPKI-VALIDATORS session 10.0.5.50 port 8323set routing-options validation group RPKI-VALIDATORS session 10.0.5.50 refresh-time 600set routing-options validation group RPKI-VALIDATORS session 10.0.5.50 hold-time 7200set routing-options validation group RPKI-VALIDATORS session 10.0.5.50 record-lifetime 7200# Create validation policyset policy-options policy-statement RPKI-POLICY term valid from validation-database validset policy-options policy-statement RPKI-POLICY term valid then validation-state validset policy-options policy-statement RPKI-POLICY term valid then local-preference 200set policy-options policy-statement RPKI-POLICY term valid then acceptset policy-options policy-statement RPKI-POLICY term invalid from validation-database invalidset policy-options policy-statement RPKI-POLICY term invalid then validation-state invalidset policy-options policy-statement RPKI-POLICY term invalid then rejectset policy-options policy-statement RPKI-POLICY term unknown from validation-database unknownset policy-options policy-statement RPKI-POLICY term unknown then validation-state unknownset policy-options policy-statement RPKI-POLICY term unknown then local-preference 100set policy-options policy-statement RPKI-POLICY term unknown then accept# Apply to BGP peersset protocols bgp group TRANSIT import RPKI-POLICYset protocols bgp group PEERS import RPKI-POLICY# Verifyshow validation sessionshow validation databaseshow validation statisticsshow route validation-state invalid
Step 5: Monitor RPKI Deployment
#!/usr/bin/env python3"""Monitor RPKI ROV deployment health and coverage statistics."""import jsonimport sysimport urllib.requestclass RPKIMonitor:def __init__(self, routinator_url: str = "http://localhost:8080"):self.routinator_url = routinator_urldef get_status(self) -> dict:"""Get Routinator server status."""url = f"{self.routinator_url}/api/v1/status"try:with urllib.request.urlopen(url) as resp:return json.loads(resp.read())except Exception as e:print(f"Error connecting to Routinator: {e}")return {}def check_validity(self, asn: int, prefix: str) -> dict:"""Check RPKI validity of a prefix/origin pair."""url = f"{self.routinator_url}/api/v1/validity/AS{asn}/{prefix}"try:with urllib.request.urlopen(url) as resp:return json.loads(resp.read())except Exception as e:return {"error": str(e)}def get_vrp_count(self) -> int:"""Get total number of Validated ROA Payloads."""status = self.get_status()return status.get("vrpsCount", 0)def report(self, prefixes_to_check: list):"""Generate RPKI monitoring report."""status = self.get_status()print(f"\n{'='*60}")print("RPKI MONITORING REPORT")print(f"{'='*60}")print(f"\nRoutinator Status:")print(f" Version: {status.get('version', 'Unknown')}")print(f" VRPs Total: {status.get('vrpsCount', 'N/A')}")print(f" Last Update: {status.get('lastUpdateDone', 'N/A')}")if prefixes_to_check:print(f"\nPrefix Validity Checks:")for asn, prefix in prefixes_to_check:result = self.check_validity(asn, prefix)validity = result.get("validated_route", {}).get("validity", {}).get("state", "error")print(f" AS{asn} -> {prefix}: {validity.upper()}")if __name__ == "__main__":monitor = RPKIMonitor()# Check own prefixesown_prefixes = [(64512, "198.51.100.0/24"),]monitor.report(own_prefixes)
Best Practices
- Create ROAs for All Prefixes - Sign ROAs for every prefix your organization announces
- Max Length = Prefix Length - Set max-length equal to announced prefix length to prevent sub-prefix hijacking
- Dual Validator - Run two independent RPKI validators for redundancy
- Soft Policy First - Start with logging RPKI-invalid routes before dropping them
- Monitor ROA Expiry - Set alerts for ROA certificates approaching expiration
- Coordinate with Upstreams - Notify transit providers about your RPKI deployment
- Test with Looking Glass - Verify your ROAs are visible using public RPKI validators