RPKI ROA para BGP: crear ROAs, validar rutas en BIRD2 y FRR
Crea ROAs IPv4 e IPv6 en el portal de RIPE NCC, instala Routinator como caché RTR, configura la validación RPKI en BIRD2 y FRRouting, y verifica el estado de tus prefijos con bgp.tools y RIPE Stat.
Sin RPKI, cualquier ASN puede originar cualquier prefijo. Un Route Origin Authorization (ROA) vincula tu prefijo a tu ASN de forma criptográfica, y la validación de origen de rutas (ROV) permite a tu router rechazar anuncios inválidos. Este tutorial cubre la cadena completa: crear ROAs en el portal de RIPE NCC, ejecutar Routinator como caché RPKI local, configurar la validación en BIRD2 y FRRouting, y verificar que todo funciona.
Requisitos previos: un ASN registrado, prefijos IP asignados (PA o PI), una sesión BGP funcional en un VPS Linux (BGP con tu propia IP en un VPS), y BIRD2 (Configuración BGP con BIRD2) o FRR (Configuración BGP con FRR) ya en funcionamiento.
¿Qué es RPKI y por qué tu prefijo BGP necesita un ROA?
Resource Public Key Infrastructure (RPKI) es un marco criptográfico definido en la RFC 6480 que vincula recursos numéricos de Internet (prefijos IP, ASNs) a sus titulares legítimos mediante certificados X.509 emitidos por los Registros Regionales de Internet. Un Route Origin Authorization (ROA) es un objeto firmado que declara: "el ASN X está autorizado a originar el prefijo Y con una longitud máxima de Z." Los validadores obtienen estos ROAs y transmiten el resultado a los routers mediante el protocolo RPKI-to-Router (RTR) (RFC 8210).
Cuando un router recibe una actualización BGP, comprueba el ASN de origen y el prefijo contra su tabla ROA local. Cada prefijo obtiene uno de tres estados de validación:
| Estado | Significado | Acción recomendada |
|---|---|---|
| Valid | Existe un ROA que coincide con el ASN de origen y la longitud del prefijo | Aceptar |
| Invalid | Existe un ROA pero el ASN de origen o la longitud del prefijo no coinciden | Rechazar |
| Not Found | Ningún ROA cubre este prefijo | Aceptar (opcionalmente con local-pref más bajo) |
Sin un ROA, tus prefijos aparecen como "Not Found." Eso es mejor que "Invalid", pero las redes que realizan ROV preferirán las rutas "Valid" de tus peers sobre tus anuncios "Not Found" cuando ambas estén disponibles. Crear ROAs es el primer paso. Validar las rutas entrantes protege tu red de aceptar prefijos secuestrados.
¿Cómo crear ROAs IPv4 e IPv6 en el portal de RIPE NCC?
Inicia sesión en el portal de RIPE NCC, navega a Resources, luego RPKI Dashboard. Si aún no has inicializado RPKI, selecciona "Hosted" certificate authority. RIPE NCC gestiona la CA y la firma por ti. Una vez que la Hosted CA esté activa, ve a la pestaña BGP Announcements. RIPE precarga sugerencias de ROA basadas en tus anuncios BGP actuales.
- Haz clic en Create ROA (o + New ROA en la pestaña ROA).
- Establece Origin ASN con tu número AS (por ejemplo,
AS213279). - Establece Prefix con tu asignación IPv4 (por ejemplo,
192.0.2.0/24). - Establece Maximum Length igual a la longitud del prefijo (
/24). No lo aumentes. Consulta la sección sobre maxLength más abajo. - Haz clic en Publish.
- Repite para tu prefijo IPv6 (por ejemplo,
2001:db8::/48con longitud máxima/48).
Verifica en el RPKI Dashboard que el estado del ROA muestre "Published." La propagación a los validadores tarda normalmente de 10 a 20 minutos, según su intervalo de actualización.
Recordatorio dual-stack: crea un ROA por prefijo. Si anuncias 192.0.2.0/24 y 2001:db8::/48, necesitas dos ROAs. Si anuncias more-specifics adicionales (un /25 tallado del /24), cada uno necesita su propio ROA con su propia vinculación ASN.
¿Cómo instalar Routinator como caché RPKI-RTR en Linux?
Routinator es un Relying Party RPKI (validador) desarrollado por NLnet Labs. Obtiene y valida ROAs de los cinco trust anchors de los RIR, y luego sirve Validated ROA Payloads (VRPs) a tu router mediante el protocolo RTR. Versión estable actual: 0.15.1.
Instalación desde el repositorio de NLnet Labs
En Debian 12 o Ubuntu 24.04:
sudo apt update
sudo apt install -y ca-certificates curl gnupg lsb-release
Añade la clave de firma y el repositorio de NLnet Labs:
curl -fsSL https://packages.nlnetlabs.nl/aptkey.asc | sudo gpg --dearmor -o /usr/share/keyrings/nlnetlabs-archive-keyring.gpg
Para Debian:
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/nlnetlabs-archive-keyring.gpg] https://packages.nlnetlabs.nl/linux/debian $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/nlnetlabs.list > /dev/null
Para Ubuntu:
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/nlnetlabs-archive-keyring.gpg] https://packages.nlnetlabs.nl/linux/ubuntu $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/nlnetlabs.list > /dev/null
Instala Routinator:
sudo apt update
sudo apt install -y routinator
El paquete instala un servicio systemd que arranca automáticamente. Routinator se ejecuta como el usuario routinator.
Verificar el servicio
sudo systemctl status routinator
Deberías ver active (running). La primera sincronización tarda de 2 a 5 minutos mientras Routinator obtiene todos los certificados de trust anchor y valida el conjunto global de ROAs.
Comprueba que la sincronización inicial se completó consultando la API HTTP (la versión empaquetada envía logs a syslog con poco detalle; la API HTTP es la fuente fiable):
curl -s http://127.0.0.1:8323/api/v1/status | python3 -c "import sys,json; d=json.load(sys.stdin); print(f'IPv4 VRPs: {d[\"payload\"][\"routeOriginsIPv4\"][\"final\"]}, IPv6 VRPs: {d[\"payload\"][\"routeOriginsIPv6\"][\"final\"]}')"
Si la sincronización aún está en curso, los contadores serán cero. Espera de 2 a 5 minutos e inténtalo de nuevo. Cuando veas contadores distintos de cero (cientos de miles para IPv4, decenas de miles para IPv6), la sincronización inicial ha terminado.
Configuración
La configuración del paquete está en /etc/routinator/routinator.conf. Los valores por defecto son seguros: RTR escucha en 127.0.0.1:3323 y HTTP en 127.0.0.1:8323. Ambos están vinculados solo a localhost.
Parámetros principales:
| Opción | Por defecto | Propósito |
|---|---|---|
rtr-listen |
["127.0.0.1:3323"] |
Servidor RTR para routers |
http-listen |
["127.0.0.1:8323"] |
Interfaz HTTP y API |
refresh |
600 | Segundos entre sincronizaciones RPKI |
retry |
600 | Segundos antes de reintentar tras una sincronización fallida |
expire |
7200 | Segundos antes de que los VRPs en caché se consideren obsoletos |
Si BIRD2 o FRR se ejecuta en la misma máquina (configuración típica para un VPS BGP), mantén el binding por defecto en 127.0.0.1. No se necesitan cambios de firewall.
Si ejecutas Routinator en un servidor separado, vincúlalo a una IP privada y restringe el acceso:
sudo ufw allow from 10.0.0.0/24 to any port 3323 proto tcp comment "RTR from routers"
Comprueba la interfaz HTTP:
curl -s http://127.0.0.1:8323/api/v1/status | head -20
Devuelve JSON con el recuento actual de VRPs, la hora de la última sincronización y el estado del validador.
¿Cómo configurar la validación RPKI en BIRD2?
BIRD2 tiene soporte nativo de RPKI mediante el protocolo rpki (disponible desde BIRD 2.0; Ubuntu 24.04 incluye BIRD 2.14). Se conecta a Routinator por RTR, rellena las tablas ROA y proporciona la función roa_check() para filtros de importación. No se necesitan bibliotecas externas.
Añade lo siguiente a tu configuración de BIRD2 (normalmente /etc/bird/bird.conf):
Definir tablas ROA
roa4 table roa_v4;
roa6 table roa_v6;
Configurar el protocolo RPKI
protocol rpki rpki_routinator {
roa4 { table roa_v4; };
roa6 { table roa_v6; };
remote 127.0.0.1 port 3323;
retry keep 90;
refresh keep 600;
expire keep 7200;
}
La palabra clave keep indica a BIRD que prefiera los valores de temporizador proporcionados por el servidor, con los valores especificados como respaldo. retry 90 significa que BIRD se reconecta 90 segundos después de perder la sesión RTR.
Añadir validación ROA al filtro de importación
filter bgp_import_v4 {
if (roa_check(roa_v4, net, bgp_path.last_nonaggregated) = ROA_INVALID) then {
print "RPKI INVALID: ", net, " from AS", bgp_path.last;
reject;
}
if (roa_check(roa_v4, net, bgp_path.last_nonaggregated) = ROA_VALID) then {
bgp_local_pref = 200;
}
accept;
}
filter bgp_import_v6 {
if (roa_check(roa_v6, net, bgp_path.last_nonaggregated) = ROA_INVALID) then {
print "RPKI INVALID: ", net, " from AS", bgp_path.last;
reject;
}
if (roa_check(roa_v6, net, bgp_path.last_nonaggregated) = ROA_VALID) then {
bgp_local_pref = 200;
}
accept;
}
bgp_path.last_nonaggregated es más seguro que bgp_path.last porque omite las entradas AS_SET de la agregación. Las rutas inválidas se rechazan. Las rutas válidas obtienen un local-pref más alto. Las rutas Not Found pasan con el local-pref por defecto.
Aplicar el filtro a tu peer BGP
protocol bgp upstream_v4 {
local as 213279;
neighbor 198.51.100.1 as 64500;
ipv4 {
import filter bgp_import_v4;
import table;
export where source ~ [RTS_STATIC, RTS_BGP];
};
}
La directiva import table es importante. Permite a BIRD reevaluar las rutas filtradas cuando la tabla ROA cambia, sin necesidad de reiniciar la sesión completa.
Recargar y verificar
sudo birdc configure
Comprueba la sesión RPKI:
sudo birdc show protocols all rpki_routinator
Busca Established en la salida. Luego comprueba el contenido de la tabla ROA:
sudo birdc show route table roa_v4 count
Deberías ver cientos de miles de entradas (la tabla ROA global tiene más de 800.000 VRPs a principios de 2026).
Comprueba la validación de un prefijo específico:
sudo birdc show route 192.0.2.0/24 all
La salida incluye un campo ROA que muestra valid, invalid o unknown (not found).
¿Cómo configurar la validación RPKI en FRRouting?
FRRouting soporta RPKI mediante el módulo rpki, que usa librtr internamente (Ubuntu 24.04 incluye FRR 8.4.4; FRR 9.x+ y 10.x también están soportados). El módulo se conecta al servidor RTR de Routinator y se integra con las route-maps de BGP.
Instalar el módulo RPKI
En Debian/Ubuntu con FRR ya instalado:
sudo apt install -y frr-rpki-rtrlib
Activar el módulo
Edita /etc/frr/daemons y añade -M rpki a las opciones de bgpd:
bgpd_options=" -A 127.0.0.1 -M rpki"
Reinicia FRR:
sudo systemctl restart frr
Verifica que bgpd cargó el módulo:
sudo vtysh -c "show rpki cache-server"
Si el comando se ejecuta sin error (la salida puede estar vacía antes de configurar un caché), el módulo está cargado. Si obtienes % Unknown command, falta el flag -M rpki o frr-rpki-rtrlib no está instalado.
Configurar el caché RTR
Entra en vtysh y configura:
sudo vtysh
configure terminal
rpki
rpki cache 127.0.0.1 3323 preference 1
rpki polling_period 300
rpki expire_interval 7200
rpki retry_interval 600
exit
Nota: FRR 9.x+ usa la sintaxis rpki cache tcp 127.0.0.1 3323 preference 1 (con la palabra clave tcp explícita). FRR 8.x usa rpki cache 127.0.0.1 3323 preference 1 sin ella. Comprueba tu versión con vtysh -c "show version".
Crear route-maps para los estados RPKI
route-map rpki-filter permit 10
match rpki valid
set local-preference 200
exit
route-map rpki-filter deny 20
match rpki invalid
exit
route-map rpki-filter permit 30
match rpki notfound
exit
Esto acepta rutas válidas con local-pref elevado, rechaza rutas inválidas y acepta rutas not-found con el local-pref por defecto.
Aplicar la route-map a tu vecino BGP
router bgp 213279
neighbor 198.51.100.1 remote-as 64500
address-family ipv4 unicast
neighbor 198.51.100.1 route-map rpki-filter in
neighbor 198.51.100.1 soft-reconfiguration inbound
exit-address-family
address-family ipv6 unicast
neighbor 2001:db8::1 route-map rpki-filter in
neighbor 2001:db8::1 soft-reconfiguration inbound
exit-address-family
exit
soft-reconfiguration inbound es obligatorio. Sin esta opción, FRR no puede reevaluar las rutas existentes cuando el caché RPKI se actualiza. FRR almacena las rutas sin modificar recibidas del peer y vuelve a aplicar la route-map cuando los VRPs cambian.
Guarda la configuración:
write memory
end
Verificar
Comprueba la conexión RTR:
sudo vtysh -c "show rpki cache-connection"
Busca (connected) en la salida. Luego comprueba la tabla de prefijos:
sudo vtysh -c "show rpki prefix-table" | head -20
Filtra las rutas BGP por estado de validación:
sudo vtysh -c "show bgp ipv4 unicast rpki valid" | head -20
sudo vtysh -c "show bgp ipv4 unicast rpki invalid"
La salida de invalid debería mostrar las rutas que estás rechazando activamente.
BIRD2 vs FRR: configuración RPKI de un vistazo
| Característica | BIRD2 | FRR |
|---|---|---|
| Instalación del módulo | Integrado (sin paquete extra) | Paquete frr-rpki-rtrlib + flag -M rpki |
| Configuración RTR | Bloque protocol rpki con remote |
Comando rpki cache en vtysh |
| Tablas ROA | Tablas roa4/roa6 explícitas |
Interno, no expuesto directamente |
| Mecanismo de filtrado | roa_check() en filtro de importación |
match rpki en route-map |
| Reevaluación automática | Directiva import table |
soft-reconfiguration inbound |
| Mostrar recuento ROA | birdc show route table roa_v4 count |
vtysh show rpki prefix-table |
| Mostrar validación | birdc show route ... all (campo ROA) |
vtysh show bgp rpki valid/invalid/notfound |
¿Cómo verificar el estado RPKI de tu prefijo?
Después de crear los ROAs y configurar la validación, verifica desde múltiples puntos.
Verificación local
En el propio router, comprueba que tu prefijo aparece como válido:
BIRD2:
sudo birdc show route 192.0.2.0/24 all | grep -i roa
FRR:
sudo vtysh -c "show rpki prefix-table 192.0.2.0/24"
Ambos deberían mostrar tu ASN como origen autorizado.
API HTTP de Routinator
curl -s "http://127.0.0.1:8323/api/v1/validity/AS213279/192.0.2.0/24"
Devuelve JSON con el estado de validación, los VRPs coincidentes y el trust anchor de origen.
bgp.tools
Abre https://bgp.tools/prefix/192.0.2.0/24 en un navegador. La columna RPKI muestra un escudo verde para Valid, rojo para Invalid o gris para Not Found. Espera de 15 a 30 minutos después de la creación del ROA para que las herramientas externas detecten el cambio.
RIPE Stat
Consulta la API de validación RPKI:
curl -s "https://stat.ripe.net/data/rpki-validation/data.json?resource=AS213279&prefix=192.0.2.0/24" | python3 -m json.tool
Busca "status": "valid" en la respuesta. RIPE Stat también muestra qué ROAs cubren el prefijo y si el maxLength coincide.
Repetir para IPv6
Ejecuta las mismas comprobaciones con tu prefijo IPv6. Todos los comandos anteriores aceptan prefijos IPv6. Sustituye 192.0.2.0/24 por 2001:db8::/48 y verifica que ambas familias de direcciones están cubiertas.
¿Por qué evitar un maxLength mayor que la longitud de tu prefijo?
Establece maxLength igual a la longitud de tu prefijo. Esta es la recomendación de la RFC 9319 (que actualiza y extiende la RFC 7115).
Cuando estableces maxLength en /24 en un ROA /20, autorizas a tu ASN a originar el /20 y cada more-specific hasta /24. Eso significa que 16 /24 están cubiertos. Un atacante que secuestre uno de esos /24 con tu ASN como origen pasará la validación RPKI como "Valid." El more-specific secuestrado gana por longest-match routing, y el ROA no puede ayudarte porque autorizaste esa longitud.
Esto se llama forged-origin sub-prefix hijack. Mediciones de 2017 citadas en la RFC 9319 encontraron que el 84% de los ROAs que usaban maxLength eran vulnerables a este ataque.
Ejemplo concreto:
| ROA | Lo que autoriza |
|---|---|
192.0.2.0/20, maxLength /20 |
Solo 192.0.2.0/20 desde tu ASN. Seguro. |
192.0.2.0/20, maxLength /24 |
/20, /21, /22, /23, /24 desde tu ASN. Cualquier /24 puede ser secuestrado falsificando tu ASN de origen. |
¿Cuándo es aceptable maxLength > longitud del prefijo? Solo cuando realmente desagregas en producción (por ejemplo, anunciando un /20 y /24 específicos para traffic engineering) y necesitas que cada prefijo desagregado se valide. En ese caso, crea ROAs individuales para cada prefijo anunciado en lugar de un ROA con maxLength amplio. Un ROA por anuncio es el patrón más seguro.
Excepción para mitigación DDoS: si usas un servicio como un centro de scrubbing que reanuncia tus more-specifics desde tu ASN, podrías necesitar maxLength para cubrir esos prefijos. Documenta esta excepción y audítala regularmente.
¿Qué ocurre cuando el caché RPKI deja de funcionar?
Cuando Routinator se detiene o se vuelve inaccesible, el comportamiento de tu router depende del temporizador de expiración.
BIRD2 mantiene la última tabla ROA conocida en memoria durante la duración de expire (por defecto 7200 segundos / 2 horas). Durante esta ventana, la validación continúa normalmente con datos obsoletos. Tras la expiración, BIRD elimina todas las entradas ROA y cada ruta vuelve a "Not Found." No se rechazan rutas por ser inválidas, pero tampoco obtienen el bonus de local-pref de válidas.
FRR se comporta de manera similar. El rpki expire_interval controla cuánto tiempo los VRPs en caché permanecen utilizables después de que la conexión RTR se corte.
Reducir el riesgo
Ejecuta una segunda instancia de Routinator o usa un validador diferente (StayRTR, Fort) en una máquina separada. Configura ambos como fuentes RTR.
BIRD2 soporta múltiples bloques protocol rpki:
protocol rpki rpki_backup {
roa4 { table roa_v4; };
roa6 { table roa_v6; };
remote 10.0.0.2 port 3323;
retry keep 90;
refresh keep 600;
expire keep 7200;
}
FRR soporta múltiples servidores caché con diferentes preferencias:
rpki
rpki cache 127.0.0.1 3323 preference 1
rpki cache 10.0.0.2 3323 preference 2
exit
Los valores de preferencia más bajos se intentan primero. FRR recurre al secundario si el primario cae.
Monitoriza el estado de Routinator. Comprueba systemctl status routinator y el endpoint de estado de la API HTTP con tu sistema de monitorización. Alerta ante caídas en el recuento de VRPs (una caída repentina a cero significa fallo de sincronización) y ante pérdidas de conexión RTR visibles en journalctl -u routinator.
Solución de problemas
El ROA muestra "Not Found" después de crearlo. La propagación tarda de 10 a 20 minutos. Routinator se sincroniza cada 10 minutos por defecto (refresh = 600). Fuerza un reinicio de sincronización: sudo systemctl restart routinator, luego espera a que la sincronización inicial se complete.
birdc muestra 0 entradas en la tabla ROA. Comprueba birdc show protocols all rpki_routinator. Si el estado no es "Established", verifica que Routinator está ejecutándose y escuchando en el puerto 3323: ss -tlnp | grep 3323.
FRR "Unknown command" para rpki. Falta el flag -M rpki en /etc/frr/daemons o frr-rpki-rtrlib no está instalado. Instala el paquete, añade el flag, reinicia FRR.
Las rutas no se reevalúan después de un cambio de ROA. En BIRD2, añade import table; al canal BGP. En FRR, activa soft-reconfiguration inbound en el vecino.
Todas las rutas muestran Invalid. Tu ROA puede tener el ASN o prefijo incorrecto. Revisa en el portal de RIPE. Verifica también que el ASN de tu router coincide con el que pusiste en el ROA.
Siguientes pasos: combina la validación RPKI con prefix-lists y filtros AS-path para defensa en profundidad . Monitoriza las alertas de RPKI invalid para tus prefijos con BGPalerter .
Copyright 2026 Virtua.Cloud. Todos los derechos reservados. Este contenido es una obra original del equipo de Virtua.Cloud. La reproducción, republicación o redistribución sin permiso escrito está prohibida.
¿Listo para probarlo?
Despliega tu propio servidor en segundos. Linux, Windows o FreeBSD.
Ver planes VPS