RPKI BGP Route Origin Validation: ROV Deployment Guide - 夜莺博客

RPKI BGP Route Origin Validation: ROV Deployment Guide

Route origin validation (ROV) with RPKI is the cheapest major improvement you can make to Internet routing security: it lets a router check that the AS number originating a prefix is actually authorized to do so, using signed Route Origin Authorizations. This guide explains how the RPKI-to-Router protocol feeds a validation database, shows a working cache-server session and import policy on both Junos and Cisco, and lists the operational habits - monitoring Invalid prefixes before dropping them - that keep ROV from becoming an outage generator.

RPKI route origin validation topology with cache server, iBGP and eBGP peers for BGP ROV

What a ROA proves, and what it does not

A ROA is a triple: address prefix, maximum length and origin AS. If your prefix is 198.51.100.0/24 with max length 26 and origin AS 64500, then any route whose prefix falls inside 198.51.100.0/24 with a length between 24 and 26 is Valid when originated by AS 64500, and Invalid when originated by anyone else. Prefixes with no covering ROA are Unknown. ROV validates the origin only - it says nothing about path manipulation, so leaks that keep the correct origin still pass. Use it together with prefix filters and, ideally, ASPA or Peerlock-style policies.

Inside the RPKI: from trust anchor to validated ROA

It helps to know what sits behind the ROA you publish, because every operational failure of ROV traces back to one of these components. RPKI is a hierarchy of trust. IANA holds the root, the regional internet registries operate trust anchors beneath it, and each registry delegates certificates down to its members - LIRs, ISPs and enterprises - who then sign ROAs for prefixes they hold. At every level the parent certificate signs the child, and each repository publishes a manifest and a certificate revocation list alongside the signed objects so that a validator can tell whether it has a complete and current view of that repository.

A validator such as NLnet Labs' Routinator, OpenBSD's rpki-client, OctoRPKI or FORT does the cryptography for you. It fetches every repository over rsync or RRDP (HTTPS), walks the certificate chain back to a trust anchor, discards anything that fails signature validation or is missing from a manifest, and produces a flat list of validated ROAs known as the VRP list - Validated ROA Payloads. That list is what your router actually uses. It is served over the RPKI-to-Router protocol (RTR, RFC 8210), normally TCP 323, and occasionally over TLS on 322 when the path between cache and router crosses a network you do not control.

The router never talks to a repository. It receives a stream of prefix, maxLength and origin-AS triples from the cache and does the matching locally against its BGP table, which is why the whole mechanism scales: the expensive part - validating tens of thousands of objects - happens once on a host, and the routers only do a longest-prefix match per route.

Two consequences follow from that design. First, correctness depends on the freshness of the cache, not on the quality of the route: a validator that has not completed a fetch cycle for a day is serving an old picture, and everything you add to it after that point is missing. Second, a router keeps the last database it received if the RTR session drops, so a cache outage does not instantly turn the table into a mass of Invalid routes - a detail worth remembering when you design your alerting, because the interesting failure is a router that reloads with no cache reachable at all, comes up with an empty database, and marks the entire internet Unknown.

Validation states and how they map to policy

State Meaning Typical action
Valid Covered by a ROA and origin AS matches Accept, raise local-pref slightly
Invalid Covered by a ROA but origin or length does not match Drop from eBGP (after monitoring)
Unknown / Not Found No covering ROA Accept - most of the table
Unverified No cache session up or database empty Accept; alert, this is a failure mode

The dangerous state is not Invalid, it is Unverified: if every cache session drops and your policy drops Invalid routes, all previously-Invalid routes become Unverified and are suddenly accepted. Track validation counts, not just session state.

Running a validator and the RTR session

You can consume somebody else's cache - several public RTR endpoints exist - but running your own validator on a small VM costs almost nothing and removes a dependency on a third party's availability. Routinator installs from a distribution package on Debian and Ubuntu and needs two configuration lines.

sudo apt-get install -y routinator
# /etc/routinator/routinator.conf
#   repository-dir = "/var/lib/routinator"
#   rtr-listen     = ["192.0.2.10:3323"]
#   http-listen    = ["127.0.0.1:8323"]
#   log-level      = "info"
sudo systemctl enable --now routinator
routinator vrps | head
routinator vrps --format csv --output /var/lib/routinator/vrps.csv

If you prefer the OpenBSD tool, rpki-client writes the same information as files that you can ship to routers with any RTR server, for example Cloudflare's stayrtr:

rpki-client -t /etc/rpki/trust-anchors -j -o /var/db/rpki-client
jq '.roas | length' /var/db/rpki-client/roas.json
stayrtr -cache /var/db/rpki-client -rtr.addr 192.0.2.10:3323

Whichever you choose, follow the same operational rules you would for a router loopback address: pin the cache to a stable IP (a loopback or a dedicated management VRF, never an interface that can go down independently of reachability), run two validators in different failure domains, and prefer a small number of large caches over many small ones. Alert on the health of the validator rather than only on the routers: the VRP count should grow slowly and monotonically, and the age of the last successful update should never exceed a few hours. A cache that has silently stopped fetching still answers RTR sessions perfectly, so without that metric you will not notice until a new prefix cannot be validated.

Junos: cache session and import policy

set routing-options validation group RPKI-CACHE session 192.0.2.10
set routing-options validation group RPKI-CACHE session 192.0.2.11
set routing-options validation group RPKI-CACHE session 192.0.2.10 refresh-time 300
set routing-options validation group RPKI-CACHE session 192.0.2.10 hold-time 600
set policy-options policy-statement ROV-IMPORT term VALID from validation-database valid
set policy-options policy-statement ROV-IMPORT term VALID then validation-state valid
set policy-options policy-statement ROV-IMPORT term VALID then local-preference 110
set policy-options policy-statement ROV-IMPORT term VALID then accept
set policy-options policy-statement ROV-IMPORT term INVALID from validation-database invalid
set policy-options policy-statement ROV-IMPORT term INVALID then validation-state invalid
set policy-options policy-statement ROV-IMPORT term INVALID then reject
set protocols bgp group EBGP import ROV-IMPORT

show validation database
show validation session
show validation statistics
show route validation-state invalid
show route 198.51.100.0/24 detail

Add set protocols bgp group IBGP family inet unicast validation or a community to carry the validation state across iBGP so you validate at the edge and act at the border. Keep the RPKI cache reachable over a stable path - a loopback, a dedicated VRF, or two caches in different failure domains.

Cisco: origin validation on IOS XE and IOS XR

! IOS XE
Router(config)# router bgp 64500
Router(config-router)# bgp rpki server tcp 192.0.2.10 port 323 refresh 600
Router(config-router)# address-family ipv4 unicast
Router(config-router-af)# bgp rpki route-filter 192.0.2.10
Router(config-router-af)# bgp bestpath origin-as validate

! IOS XR origin validation
Router(config)# rpki cache 192.0.2.10
Router(config-rpki-cache)# transport tcp port 323
Router(config-rpki-cache)# refresh-time 600
Router(config)# route-policy ROV
Router(config-rpl)# if validation-state is invalid then
Router(config-rpl)#   pass
Router(config-rpl)# else
Router(config-rpl)#   set local-preference 150
Router(config-rpl)# endif
Router(config-rpl)# end-policy
Router(config)# bgp 64500
Router(config-bgp)# rpki server 192.0.2.10
Router(config-bgp)# address-family ipv4 unicast
Router(config-bgp-af)# route-policy ROV in

Verification

Router# show bgp ipv4 unicast rpki servers
Router# show bgp ipv4 unicast rpki table
Router# show bgp ipv4 unicast 198.51.100.0/24
! validation state appears next to the path entry
Router# show bgp ipv4 unicast rpki table invalid
Router# clear bgp ipv4 unicast * soft in        ! re-run policy after DB change

Run the cache session for at least a week in monitor-only mode and count how many of your own or your customers' prefixes show up Invalid - that list is a to-do list of missing or wrong ROAs. Only then flip the drop rule, ideally with a staged policy that rejects only prefixes whose origin is invalid and whose path length is short, so you limit blast radius.

Staged rollout: monitor, then reject

The difference between a routine ROV deployment and a well-known outage is staging. Do not write the reject rule on day one; run the cache, let the validators fill the database, and watch what your own routes look like when they are checked against it.

# Junos - monitor only, no policy action
show validation session detail
show validation statistics
show validation database 198.51.100.0/24
show route validation-state invalid | count

# Cisco IOS XE - monitor only
show bgp ipv4 unicast rpki servers
show bgp ipv4 unicast rpki table
show bgp ipv4 unicast rpki table invalid
show bgp ipv4 unicast 198.51.100.0/24

Run in that mode for at least a week, ideally a month, and collect the list of prefixes that come back Invalid. That list is a work queue, not a routing problem: it is almost always missing ROAs, ROAs with a maxLength that is too short for a more specific you advertise, or a ROA signed for the wrong origin AS after a migration or a renumbering. Ask every downstream customer whose prefixes you carry to fix theirs, and fix your own first. Only when the invalid list is empty of anything you care about should the reject rule be enabled, and even then it is sensible to reject only where the origin is invalid and the path is short, so a mistake in a customer's ROA cannot black-hole a long multi-hop path that only passes through you.

Keep the reject policy in a single named policy statement so that the rollback is one command. On Junos, deactivating it and committing restores the previous behaviour in seconds, and on IOS XR removing the route-policy from the address family does the same. Practise that rollback once in the lab, so that the person who has to run it at 3 a.m. has seen it work before.

deactivate policy-options policy-statement ROV-IMPORT
commit
show policy-options policy-statement ROV-IMPORT

Failure modes, alerting and what to actually monitor

Once ROV is live, the states you need to be able to distinguish are Invalid, Not Found and Unverified, and the third one is the dangerous one. Not Found means the database is working and no ROA covers the prefix - normal for most of the table. Unverified means the router has no usable database at all, and if your policy rejects Invalid routes while accepting everything else, an Unverified table is not a safety failure, it is a silent rollback: every route that you were correctly rejecting reappears as acceptable. Monitor validation counts, not just session state, and alert on the count of Invalid routes dropping to zero at the same time as the cache session goes down.

Four metrics deserve a graph. The RTR session state and its uptime per router; the number of routes in each validation state; the number of VRPs the cache is serving and the timestamp of its last successful update; and the difference between your published ROAs and the prefixes you actually advertise, which is the measure that catches a ROA you forgot to update after an address change. Export them through the same collection system that holds your other routing telemetry so that a validation anomaly appears next to the route churn it probably caused.

Two smaller hardening points belong in the same checklist. Protect the RTR session: TCP 323 carries a database that decides what your network accepts, so it should be firewalled to the router addresses that need it, and authenticated or encrypted if it crosses any shared path. And keep the refresh interval comfortably shorter than the hold time, so a validator restart does not cause unnecessary session flaps, while still being short enough that a corrected ROA takes effect within a few minutes rather than the next day. When the database changes, remember that inbound policy is only re-evaluated if you ask for it - a soft reset of the peer is what turns a fresh VRP list into an actual routing decision.

Operational checklist

Publish ROAs for every prefix you advertise including its correct max length; monitor show route validation-state invalid as an alert, not a daily report; keep two cache servers; log changes of validation counts to your monitoring system; and document the rollback - a single policy statement change should restore pre-ROV behaviour in seconds. Related reading on this site: BGP best path selection, prefix-list and route-map filtering, BGP communities and BGP neighbor flapping.

原文链接:https://www.juniper.net/documentation/us/en/software/junos/bgp/topics/topic-map/bgp_origin_validation.html