SwordArMor

Vérification RPKI avec routinator, bird et IOS-XE

Quand on fait partie de la DFZ, on peut en gros annoncer n’importe quel préfixe tant que ça passe les filtres de nos pairs, et on est censé leur faire plus ou moins confiance. Sauf que croire encore à ce système de la confiance en 2020, c’est aussi croire aux bisounours. Il est bien plus sûr de faire des filtres qui taillent dans le vif et de vérifier que les préfixes que l’on reçoit viennent du bon AS. Pour ça, il est possible de signer ses annonces avec RPKI (ou de faire signer par son LIR si l’on n’en est pas un) et aussi de vérifier les signatures RPKI sur les préfixes que l’on reçoit. Les deux étant complètement décorrélés, un peu à la manière de DNSSEC : on peut très bien vérifier les signatures DNSSEC mais ne pas signer son domaine et inversement.

Comme les signatures RPKI sont cryptographiques et que les CPUs des routeurs ne sont pas des foudres de guerre (l’intérêt des routeurs c’est de profiter des ASIC/FPGA, pas de router via le CPU), il existe RPKI-RTR. C’est un protocole qui permet de faire les vérifications sur un serveur (avec un CPU puissant, donc) puis d’envoyer les données pré-machées au routeur pour qu’il puisse les traiter avec très peu de CPU.

J’ai testé deux implémentations : rpki-client d’OpenBSD et routinator de NLnetLabs. Comme je suis feignant, j’ai d’abord regardé si l’une des deux étaient déjà packagée pour gentoo. Ce fut un échec total. À vue de nez, rpki-client me semblait plus simple, j’ai donc commencé par cela, mais j’ai rencontré plusieurs soucis :

  1. La numérotation des versions est un peu étrange, 0_3_0 au lieu de 0.3.0, que je règle en créant une variable $VERSION tirée de $PV : VERSION="VERSION_${PV//./_}", que je ré-utilise donc en lieu et place de $PV dans le reste de l’ebuild
  2. La liste des préfixes pour bird était vide : bug #12
  3. Encore plus gênant, je n’ai jamais réussi à importer la liste générée dans bird, pour cause d’erreur de syntaxe dans le fichier : /var/db/rpki-client/bird:1:1 syntax error, unexpected CF_SYM_UNDEFINED. La première ligne étant roa table roa {. D’après la documentation, le symbole roa est censé se trouver dans un protocole RPKI, sauf que ce protocole demande un serveur RPKI-RTR.
Il était donc assez clair que cette implémentation n’était pas la bonne approche pour mon besoin. De plus, elle n’est pas compatible avec IOS-XE, et j’ai également envie de vérifier RPKI dessus et pas seulement sur les bird.

Je me suis donc lancé dans le packaging de routinator. Comme il est écrit en rust, j’ai utilisé cargo-ebuild afin qu’il me génère un ebuild avec tous les dépendances rust qui ne sont pas dans l’arbre de portage. Et là premier souci : la façon d’installer les binaires a changée.

J’ai donc attendu un peu que la fonction soit mise à jour, puis j’ai pu l’installer. Là je me suis rendu compte que je n’avais pas fait de fichier de configuration ni de script d’init, je les ai donc écris et intégrés dans mon ebuild. À ce stade j’ai maintenant un routinator qui démarre, récupère les TALs et écoute sur le port standard. Il est maintenant temps de passer à l’intégration avec les routeurs.

Pour le cas de bird, la configuration se présente comme suit avec un serveur sur mon infrastructure propre et un autre sur celle de grifon :

roa4 table r4;
roa6 table r6;

protocol rpki rpki_msi {
        roa4 { table r4; };
        roa6 { table r6; };

        remote "msi.no.swordarmor.fr";
}

protocol rpki rpki_conan {
        roa4 { table r4; };
        roa6 { table r6; };

        remote "conan.grifon.fr";
}
Ici, je crée deux tables (r4 et r6) et je demande à mes protocoles de mettre les préfixes RPKI dedans.

Et là, en rechargeant la configuration, je tombe sur une erreur de syntaxe du même type que pour rpki-client. C’est pas très gentil… La vraie raison est que bird a besoin de la libssh pour activer le protocole RPKI. Et ce, même si on utilise le transport TCP classique. C’est un bug connu qui sera corrigé. Il se trouvait donc que l’ebuild de bird n’avait pas cette option. J’ai donc ajouté le USE flag libssh, recompilé et redémarré bird.

Maintenant, les nouveaux protocoles apparaissent puis… rien. Je me suis donc fendu d’un sur la ML de bird et Ondrej Zajicek m’a dirigé vers un commit de leur git qui corrige ce bug. J’ai donc généré le .patch entre ce commit et celui du tag de la version 2.0.7, je l’ai ajouté à mon ebuild, puis j’ai de nouveau recompilé et relancé mon bird. (quand on aime on ne compte pas) Et là, alléluia, j’ai bien les préfixes signés dans mon bird \o/

Il est maintenant temps de passer aux choses sérieuses : le filtrage des préfixes invalides. Pour ce faire, j’ai repris la configuration du LG du NLnog ring que j’ai adaptée à mon besoin :

function check_import_ipv4_rs()
[…]
        # scrub Origin Validation State Extended Community
        bgp_ext_community.delete((unknown 0x4300, 0, 0));
        bgp_ext_community.delete((unknown 0x4300, 0, 1));
        bgp_ext_community.delete((unknown 0x4300, 0, 2));

        # set RPKI Origin Validation State Extended Community
        case roa_check(r4, net, bgp_path.last_nonaggregated) {
                ROA_VALID:
                        /* add rfc8097 marker to routes for which a valid matching ROA exists */
                        bgp_ext_community.add((unknown 0x4300, 0, 0));
                        igp_metric = 10;
                ROA_INVALID:
                        bgp_ext_community.add((unknown 0x4300, 0, 2));
                        return false;
                else:
                        /* add rfc8097 marker to routes for which no covering ROA exists */
                        bgp_ext_community.add((unknown 0x4300, 0, 1));
                        igp_metric = 20;
        }

        return generic_check_import();
}
function check_import_ipv6_rs()
[…]
        # scrub Origin Validation State Extended Community
        bgp_ext_community.delete((unknown 0x4300, 0, 0));
        bgp_ext_community.delete((unknown 0x4300, 0, 1));
        bgp_ext_community.delete((unknown 0x4300, 0, 2));

        # set RPKI Origin Validation State Extended Community
        case roa_check(r6, net, bgp_path.last_nonaggregated) {
                ROA_VALID:
                        /* add rfc8097 marker to routes for which a valid matching ROA exists */
                        bgp_ext_community.add((unknown 0x4300, 0, 0));
                        igp_metric = 10;
                ROA_INVALID:
                        bgp_ext_community.add((unknown 0x4300, 0, 2));
                        return false;
                else:
                        /* add rfc8097 marker to routes for which no covering ROA exists */
                        bgp_ext_community.add((unknown 0x4300, 0, 1));
                        igp_metric = 20;
        }

        return generic_check_import();
}
La fonction roa_check est déjà intégrée dans bird, il n’y a pas besoin de la déclarer. De cette façon, je rejette les annonces invalides, je marque chaque préfixe en fonction de son type de validité et, en modifiant la métrique IGP, je préfère une annonce valide à une annonce non déclarée si le chemin BGP et la MED sont égales entre les deux annonces. Ce cas devrait arriver très rarement, uniquement quand un préfixe vient d’être signé et qu’une route n’a pas encore été mise à jour.

Si jamais vous avez une route par défaut, vous avez certainement envie de marquer la route comme injoignable (dest = RTD_UNREACHABLE) pour éviter d’utiliser la route par défaut si cette route plus spécifique n’est pas dans la FIB.

Pour le cas d’IOS XE, c’est beaucoup plus simple, il suffit de quelques lignes de configuration :

conf t
 router bgp 204092
  bgp rpki server tcp 2A00:5884::8 port 323 refresh 600
  bgp rpki server tcp 2A0E:F42::1 port 323 refresh 600
  address-family ipv4
   neighbor GRIF-iBGP-IPv4 send-community both
   neighbor GRIF-iBGP-IPv4 announce rpki state
  address-family ipv6
   neighbor GRIF-iBGP-IPv6 send-community both
   neighbor GRIF-iBGP-IPv6 announce rpki state
Nous sommes obligés de mettre l’IP directement, c’est spécifié dans la documentation. Les options sur les neighbors permettent de garder la communauté correspondant à la validité RPKI lors de l’export vers les autres routeurs du réseau.

Remplir la table SNMP ifAlias sous Linux

Il m’arrive encore de jouer avec des routeurs soft, et comme à chaque fois il faut bricoler pour avoir ce que l’on veut, ici avoir une table ifAlias correcte. À une époque je le faisais avec FreeBSD, et j’avais trouvé la parade en utilisant un proxy vers …

Lire la suite

Afficher correctement les AS 32 bits avec mtr

mtr est un outil très pratique qui permet de faire un traceroute, mais en mieux. La sortie est beaucoup plus lisible, et ça utilise de l’ICMP par défaut (au lieu de l’UDP sous Linux/*BSD). Seulement, dans les anciennes versions, les AS 32 bits sont mal gérés : ils …

Lire la suite

Sauvegarder la configuration de ses switches et routeurs avec expect

Si vous ne connaissez pas expect, c’est un outil qui permet d’écrire des scripts en TCL qui iront interagir avec telnet/ssh et d’autres trucs du genre, sans que vous ayez à le faire vous même. C’est donc très pratique pour écrire des scripts qui seront …

Lire la suite

Utiliser socat pour contourner les limiations SSL d’un navigateur moderne

Depuis quelque temps, les navigateurs bloquent les requêtes SSL/TLS utilisant des versions de protocoles et suites cryptographiques jugées trop faibles.

Cependant, on peut avoir besoin de se connecter à de vieux équipements qui eux ne supportent que ces vielles versions ; en l’occurrence, un PDU. En théorie, ils supportent …

Lire la suite

Proxy SNMP pour une table précise (ifAlias)

État des lieux

Sous FreeBSD nous avons le choix entre deux implémentations SNMP : bsnmp et le classique net-snmp. Le premier a des soucis sur les modèles de CPU et ne remonte pas les IPv6 des interfaces, alors que le second ne remonte pas la description des interfaces.
En clair, on …

Lire la suite

Ebuild pour la suite de tests blaeu pour les sondes RIPE Atlas

Blaeu

Blaeu permet de créer des tests en utilisant l’API d’Atlas puis d’afficher le résultat de manière lisible pour un humain ou une machine.
Une description complète est disponible dans le README du répertoire git .

L’installation sous gentoo

L’ebuild est dans mon overlay personnel, il …

Lire la suite

Le nœud AS112 chez grifon et Breizh-IX

Le rôle de l’AS112

Cet AS est assez spécial, il sert de trou noir du DNS. C’est aussi l’une des premières utilisations de l’anycast.

En regardant la charge des serveurs racines du DNS, les administrateurs se sont rendu compte qu’une quantité non négligeable de requêtes …

Lire la suite

De l’IPv6 dans du PPP

Que j’aurais aussi pu titrer « Pourquoi aucun opérateur ne fait de collecte IPv6 en PPP ».

Tout d’abord, un rappel du fonctionnement d’une collecte PPP s’impose

  1. L’abonné entre ses identifiants PPP dans son modem,
  2. De l’autre côté, le BAS regarde le realm, et l’envoie …

Lire la suite

Configuration dhcpcd pour le DHCPv6 de online

En voulant faire un peu de ménage sur mon disque dur, je suis tombé sur une vielle sauvegarde d’une gentoo que j’avais chez online. Cela m’a permis de tomber sur une configuration DHCPv6 pour dhcpcd (le daemon DHCP « par défaut » de gentoo) fonctionnelle chez online. La configuration …

Lire la suite

Page 1 / 5 »