VyOSLIII · Security HardeningSecurity
RPKI — Route Origin Authorisation, validation cache, and route validation
What you'll learn
- Explain the RPKI trust hierarchy and the ROA structure
- Configure an RPKI validator cache and connect the BGP speaker to it
- Apply RPKI invalid drop as part of the leak firewall
- Recognise the production failure modes where RPKI is unavailable or misconfigured
Prerequisites
Verified against VyOS 1.5.x LTS (circinus) · VyOS 1.4.x (sagitta) — legacy · FRRouting 10.x (VyOS 1.5) · Linux kernel 6.6 LTS (VyOS 1.5 base) · strongSwan 5.9.x (IPsec) · WireGuard 1.0.x (kernel module + userspace tooling)
RPKI (Resource Public Key Infrastructure) is the cryptographic complement to prefix-list filtering. Where the prefix-list says “this peer is allowed to advertise this cone”, RPKI says “this AS is authorised by the resource holder to originate this prefix”. A BGP route whose origin AS does not match an RPKI ROA is a hijack attempt or a misconfiguration that the prefix-list alone cannot detect.
On VyOS 1.5 LTS / FRR 10.x, RPKI is configured through a
validator cache (an external service that fetches ROAs
from the RPKI trust anchors) and the RTR protocol (the
cache-to-router protocol that delivers the ROAs to the BGP
speaker). The BGP speaker then validates every received
route against the ROA set and marks the route as valid,
invalid, or not-found.
This lesson covers the RPKI trust hierarchy, the ROA structure, the validator cache, the BGP route validation, and the production deployment pattern.
The RPKI trust hierarchy
RPKI is a hierarchy of cryptographic certificates that maps IP prefixes and AS numbers to the entities that hold them. The hierarchy mirrors the Internet’s allocation hierarchy — but it does not have a single root:
- Each RIR operates its own trust anchor. RIPE NCC, ARIN, APNIC, LACNIC and AFRINIC each publish a self-signed trust anchor certificate covering the resources they administer. There are five roots, not one; IANA does not sign them. A validator is bootstrapped with five Trust Anchor Locator files (TALs), one per RIR, and a validator missing a TAL is simply blind to everything under it.
- Local Internet Registries (ISPs, enterprises) hold the address space the RIR allocated to them. Each LIR’s resource certificate is signed by the parent RIR’s certificate.
The certificates are X.509 with the RFC 3779 resource extensions that carry the prefixes and ASNs. The tree is published in RFC 8182 (RRDP) and rsync repositories; validators fetch them periodically and rebuild the chain from each TAL downwards.
flowchart TB
TAL1[RIPE NCC\ntrust anchor] --> LIR1[Local ISP\nresource certificate]
TAL1 --> LIR2[Enterprise\nresource certificate]
TAL2[ARIN\ntrust anchor]
TAL3[APNIC\ntrust anchor]
LIR1 --> ROA1[ROA\n192.168.0.0/16\nmax-length /24\norigin AS 65001]
LIR1 --> ROA2[ROA\n192.169.0.0/16\nmax-length /16\norigin AS 65001]
LIR2 --> ROA3[ROA\n10.0.0.0/8\nmax-length /16\norigin AS 65002]
The diagram shows three of the five independent trees. A ROA is a signed statement by the resource holder that a specific AS is authorised to originate a specific prefix with a maximum prefix length. Nothing above the RIR signs anything, which is why “who do I trust?” in RPKI is answered by which TALs your validator loaded, not by a single root key.
The ROA structure
A Route Origin Authorisation (ROA) is the leaf object in the RPKI tree. It contains:
- AS number — the AS authorised to originate the prefix.
- Prefix — the IP prefix (e.g. 192.168.0.0/16).
- Maximum prefix length — the longest prefix the AS is authorised to advertise (e.g. /24 means the AS may advertise any sub-prefix up to /24 inclusive).
A ROA is signed by the LIR’s certificate. The signature proves that the LIR — the entity that holds the address space — authorises the AS to originate the prefix.
# Example ROA in JSON form (RPKI repository format)
{
"asn": "AS65001",
"prefix": "192.168.0.0/16",
"maxLength": 24,
"ta": "ripe"
}
The ROA says: AS 65001 is authorised to originate 192.168.0.0/16 with sub-prefixes up to /24. An advertisement of 192.168.1.0/25 (longer than /24) would be RPKI invalid; an advertisement of 192.168.1.0/24 is RPKI valid.
The validator cache and the RTR protocol
The BGP speaker does not fetch ROAs directly. The BGP speaker connects to a validator cache (a separate service) using the RPKI to Router Protocol (RTR, RFC 8210). The cache fetches the RPKI repositories, validates the certificate chain, and presents the ROAs to the BGP speaker.
flowchart LR
R1["Local router\nFRR / VyOS"] -->|RTR\nTCP/3323| CACHE["RPKI cache\nRoutinator, rpki-client\n+ StayRTR, FORT"]
CACHE -->|rsync\nHTTPS| REPO1["RIPE NCC\nrepository"]
CACHE -->|rsync\nHTTPS| REPO2["ARIN\nrepository"]
CACHE -->|rsync\nHTTPS| REPO3["APNIC\nrepository"]
RTR is a small binary protocol over plain TCP (RFC 8210
registers port 323; most implementations ship on 3323).
The router asks for the current set, the cache answers
with it and a serial number, and from then on the router
polls for the delta since that serial rather than
re-downloading everything. VyOS also supports running RTR
over SSH: each cache takes an ssh username and an
ssh key, with the key generated by
generate ssh client-key /config/auth/id_rsa_rpki. That
is the option to reach for when the cache is not on a
trusted management segment.
A production deployment typically runs one or more validator caches on dedicated hosts. The caches fetch the RPKI repositories from the RIRs; the BGP speakers fetch the ROAs from the caches.
# VyOS 1.5 — connect to two RPKI caches
set protocols rpki cache 10.0.0.1 port '3323'
set protocols rpki cache 10.0.0.1 preference '1'
set protocols rpki cache 10.0.0.2 port '3323'
set protocols rpki cache 10.0.0.2 preference '2'
# Timers (these are the FRR defaults, shown so they can be reasoned about)
set protocols rpki polling-period '300'
set protocols rpki retry-interval '600'
set protocols rpki expire-interval '7200'
The configuration specifies one or more caches with a preference; lower is preferred. The BGP speaker connects to the most preferred cache that is reachable and falls back to the next.
Two notes on those numbers. RFC 8210 registers TCP/323 for
RPKI-RTR, but Routinator, StayRTR and friends default to
3323 because that is above the privileged range and
they do not run as root — check what your cache actually
listens on rather than assuming the RFC port. And the
three timers are the ones the operator has to hold in
their head during an incident: polling-period is how
often the router asks for updates, retry-interval is how
long it waits before reconnecting after a failure, and
expire-interval is how long the already-downloaded ROA
set stays usable once the cache is gone. That last one is
why a cache outage does not blind the router immediately.
BGP route validation
The BGP speaker validates every received route against the ROA set. The validation result is one of three states:
- Valid — the route’s prefix and origin AS match a ROA, and the prefix length does not exceed the ROA’s max-length.
- Invalid — at least one ROA exists for the prefix, but the route’s origin AS does not match, or the prefix length exceeds the ROA’s max-length.
- Not-found — no ROA exists for the prefix.
flowchart LR
R[Received route\n192.168.1.0/24\norigin AS 65001] --> V{Validate\nagainst ROAs}
V --> ROA1[ROA:\n192.168.0.0/16\nmax /24\norigin AS 65001]
ROA1 -->|match| VALID[Valid]
R2[Received route\n192.168.1.0/24\norigin AS 65002] --> V
V --> ROA2[ROA:\n192.168.0.0/16\nmax /24\norigin AS 65001]
ROA2 -->|origin mismatch| INVALID[Invalid]
R3[Received route\n10.0.0.0/8\norigin AS 65003] --> V
V --> ROA3[No matching ROA]
ROA3 -->|no ROA| NOTFOUND[Not found]
The production BGP configuration uses the validation state to filter incoming routes:
# Route-map that denies RPKI invalid
set policy route-map DROP-RPKI-INVALID rule 10 action 'deny'
set policy route-map DROP-RPKI-INVALID rule 10 match rpki 'invalid'
set policy route-map DROP-RPKI-INVALID rule 20 action 'permit'
# Apply it inbound on the peer, under the address family
set protocols bgp system-as '64512'
set protocols bgp neighbor 10.0.0.1 remote-as '65002'
set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast route-map import 'DROP-RPKI-INVALID'
The route-map denies routes whose RPKI status is
invalid. Routes whose status is valid or not-found
pass through rule 20.
The last line is where a pre-1.4 configuration breaks. On
VyOS 1.4 and 1.5 the peer lives at
set protocols bgp neighbor <address> rather than under
the ASN, the local ASN is the system-as leaf, and the
inbound policy is attached under the address family as
route-map import <name>. There is no import-filter
node in the tree at all — a configuration carrying one does not
half-apply the policy, it fails to commit.
Production deployment
A production RPKI deployment has three components:
- Validator cache — a routinator or gortr installation that fetches the RPKI repositories and serves the ROA set over RTR. The cache is typically deployed on a dedicated host in the operator’s infrastructure.
- BGP speaker — every edge router that needs RPKI validation connects to the validator cache via RTR.
- ROA publication — the operator publishes ROAs for their own prefixes at the appropriate RIR’s RPKI portal (RIPE NCC’s myRIPE, ARIN’s online portal, etc.).
The defensive idiom: every edge router that connects to an upstream provider or to a customer cone runs RPKI validation. The operator who runs RPKI only on the aggregation router is one configuration error away from a hijacked prefix entering the local table.
Failure modes
RPKI cache unreachable
The BGP speaker cannot reach the validator cache. This is
the failure mode with a delay fuse: nothing changes for up
to expire-interval seconds, because the previously
downloaded table is still being used. When it expires,
every route becomes notfound, the deny invalid rule
stops matching anything, and the router accepts hijacks it
would have rejected an hour earlier. No BGP session goes
down. No route disappears. The only symptom is a filter
that no longer filters.
Diagnostic — three commands, in this order:
show rpki cache-connection
show rpki prefix-table
show bgp ipv4 unicast rpki invalid
The first says whether the RTR session to each configured
cache is up. The second says whether there is still a
payload table to validate against — an empty table with
caches configured is the expired state. The third is the
one to trend: a peer that was producing a steady trickle
of invalid routes and now produces none has either
cleaned up its act or lost validation, and it is almost
never the former.
Fix: restore connectivity to the validator cache. The
defensive idiom is two or more caches on different hosts
and different network paths, plus an alert on
show rpki cache-connection rather than on BGP state,
because BGP state will not tell you.
RPKI invalid drop too aggressive
The operator enables RPKI invalid drop before all
customer ROAs are published. A legitimate customer prefix
without a ROA is marked not-found (not invalid); the
import route-map passes it through. But the operator’s
configuration accidentally drops not-found too, and the
customer prefix is dropped.
Fix: read the route-map back and confirm that the only
deny rule matches invalid. The rendered FRR view is
the one that decides, so read it rather than the VyOS
tree:
show configuration commands | match DROP-RPKI-INVALID
vtysh -c 'show route-map DROP-RPKI-INVALID'
A rule matching rpki notfound with action deny is the
bug. So is a route-map with no final permit rule: FRR’s
implicit deny at the end of a route-map filters
everything that reached it, so a DROP-RPKI-INVALID that
lost its rule 20 does not drop invalids, it drops the
peer’s entire table.
ROA publication error
The operator publishes a ROA for 192.168.0.0/16 with
max-length /16. The operator’s BGP speaker advertises
192.168.1.0/24 — which is a more specific prefix. The
ROA’s max-length /16 does not authorise the /24; the
/24 is marked invalid. The operator’s own prefix is
dropped.
Diagnostic — ask the router what the payload table says about the prefix and origin pair, which is a direct answer rather than an inference:
show rpki prefix 192.168.1.0/24 65001
show bgp ipv4 unicast rpki invalid
show rpki prefix reports the covering ROA and the
max-length it carries. If the covering entry says
max-length 16 while the announcement is a /24, the
arithmetic is the whole diagnosis. The second command
lists what the local router is currently calling invalid;
on a peer that already drops invalids, your prefix will be
absent from show bgp ipv4 unicast entirely, which is the
symptom your customer reports as “unreachable”.
Fix: re-publish the ROA with max-length /24. The defensive idiom: max-length should be set to the longest sub-prefix the operator intends to advertise. A max-length of /16 for a customer cone with /24 sub-prefixes is too restrictive.
A repository is stale or unfetchable
There is no unknown state in RTR. The cache sends the
router validated ROA payloads — prefix, max-length,
origin AS — and an object that fails validation is simply
not in that set. Everything the router did not receive a
payload for is notfound. That is the whole state
machine, and it is worth holding on to, because it means
every repository problem presents identically at the
router: prefixes silently move from valid to notfound.
So when a CA’s repository goes unfetchable, or its
manifest expires, or a publication point is mid-update and
inconsistent, the router sees no error. It sees a smaller
prefix table. Your own prefix can move from valid to
notfound on every peer in the world without a single log
line on your router.
Diagnostic: this one is not answered on the router. Read
the validator’s own logs and metrics — Routinator,
rpki-client and FORT all report per-repository fetch
status and object counts — and compare the router’s
show rpki prefix-table count against the validator’s
VRP count. A router table that is much smaller than the
validator’s set means the RTR session, not RPKI, is the
problem.
Fix: usually to wait — publication points converge on their own — while monitoring the count rather than assuming. A validator whose fetch has been failing for hours is a page, not a graph to admire.
Rollback
An RPKI configuration change is reversible through the standard VyOS mechanisms:
# Remove one cache server
delete protocols rpki cache 10.0.0.1
# Remove the RPKI drop from one peer, leaving the session up
delete protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast route-map import
# Or revert the whole change set
rollback 1
Note the shape of the second command: on 1.4 and 1.5 the
peer is addressed as protocols bgp neighbor <ip> with no
ASN in the path, and the policy hangs off the address
family as route-map import. The pre-1.4 spelling with
the ASN in the path, or with an import-filter node, does
not delete anything on 1.5 — it fails.
Removing the import route-map is a policy change, not a session reset: FRR re-evaluates the peer’s stored routes and the previously dropped invalids reappear in the table within seconds. That makes it a genuinely fast escape hatch when RPKI is filtering something it should not, and also a fast way to accept a hijack, so it belongs in a change record either way.
The commit validator does not check that the cache is
reachable — a configuration referencing a cache that has
never answered commits perfectly happily. The only proof
is show rpki cache-connection after the commit.
Production discipline
Cross-course references
LIII-VyOS-Security(vyos-liii-03-prefix-filtering, the previous lesson) covers the prefix-list / AS-path filtering that complements RPKI.XXVIII-VyOS-BGPPrefixFilter(vyos-xxviii-04-filter-list) covers the BGP filter-list mechanism.- The RPKI trust hierarchy is documented in RFC 6480; the BGP origin validation is documented in RFC 6811; the RTR protocol is documented in RFC 8210.
Quiz
Knowledge check · 4 questions
Q1. What does an RPKI ROA authorise?
Q2. A BGP route whose prefix is covered by no ROA at all is marked RPKI `not-found`.
Q3. R1 is connected to a validator cache at 10.0.0.1. The cache is unreachable due to a network partition. R1's RPKI validation state for every received route is now `not-found`. The operator configured RPKI invalid drop on the BGP neighbour 10.0.0.2 (AS 65002). What is the impact?
R1's connection to the validator cache is down. R1's RPKI validation is not operational. The BGP neighbour has RPKI invalid drop configured.
Q4. R1 originates 192.168.1.0/24 in AS 65001. The operator publishes a ROA for 192.168.0.0/16 with origin AS 65001 and max-length /16. After deploying RPKI invalid drop, R1's own prefix 192.168.1.0/24 is being dropped by peers running RPKI. What went wrong?
R1 originates 192.168.1.0/24 in AS 65001. The operator published a ROA for 192.168.0.0/16 with origin AS 65001 and max-length /16. RPKI invalid drop is enabled. The /24 is being dropped by peers.
Passing score: 75%. Answers are checked in this browser.