lalahop
Categorie: Sécurité

Sécuriser le routage sur Internet

Aujourd'hui, je vous propose un long billet sur le routage inter-domaine, sa sécurité actuelle et RPKI+ROA.

Attention : RPKI+ROA est encore un mécanisme tout jeune. Cela signifie que, bien que les concepts de base ne changeront pas et que ce travail date de mai 2013, certaines informations contenues dans ce travail vont devenir obsolètes à plus ou moins long terme.

Pour lire la version HTML, il suffit de cliquer sur le lien "Lire la suite" (et/ou de poursuivre ci-dessous). Pour ceux qui préfèrent lire un si gros pavé en PDF, c'est par là : Sécuriser le routage sur Internet.

Je mets également les sources LaTeX à votre disposition. Ces sources LaTeX peuvent servir de base à d'autres documents. Sources.

Vous pouvez également récupérer la maquette (vous comprendrez la raison de son existence en lisant le pavé). Elle peut servir pour mieux visualiser le fonctionnement de RPKI+ROA ou pour simplement tester ses différents composants. Elle repose sur LXC. Le tar contient la maquette compressée avec LZMA ainsi que les instructions d'utilisation au format texte. Maquette RPKI-ROA. Les fichiers de configurations principaux pour refaire une maquette from scratch sont disponibles ici : Fichiers de configuration principaux de ma maquette RPKI-ROA.

Et pour terminer, vous pouvez également récupérer le visuel projeté durant ma soutenance et ses sources. Support visuel soutenance | Sources LaTeX support visuel soutenance.

Si vous voulez tout récupérer (maquette, sources, pdf) en un seul coup, c'est par là : Sécuriser le routage sur Internet - Pack all-in-one.

Le tout (les sources LaTeX, les pdf, les images, la maquette, ...) est diffusé sous la licence habituelle de ce blog, à savoir : CC-BY-SA 3.0 France.

Lire la suite >>

VPN unipersonnel avec une IP failover en sortie sur un dédié OVH

L'idée du jour est d'utiliser un serveur dédié chez OVH, qui sert déjà à autre chose, pour se monter un petit VPN sans prétention. Quand je dis sans prétention c'est que je prévois ça pour un usage personnel (unipersonnel) et que je ne destine pas ce VPN à l'échange de flux hautement confidentiels (sinon ça ne finirait pas chez OVH ...). Donc si vous cherchez une config blindée haute sécurité, ce n'est pas le bon billet, désolé.

Le but de ce VPN est de me permettre d'être sur Internet quand je veux faire des tests ou autre (exemple parmi d'autres : nmap mon serveur pour vérifier qu'il n'y a rien de plus qui est ouvert sur l'extérieur que ce que je veux). Quand je dis « être sur Internet », je veux dire : être capable d'émettre et de recevoir tous les contenus et toutes les applications de mon choix sans aucune discrimination ... Ho ! La définition de la neutralité du net ! Donc éviter un filtrage de ports à la con, éviter un filtrage du type "ne peut entrer sur le réseau que ce qui fait suite à une demande de l'intérieur" (en gros : impossible d'héberger des services). C'est rageant d'être perturbé par notre FAI (Fournisseur d'Accès à son Intranet dans le cas présent) quand on veut observer quelque chose ou monter un service.

Attention toutefois : le tunnel sort chez OVH et il faut savoir qu'OVH n'a pas un impératif de neutralité. Des usages y sont clairement interdits ou soumis à autorisation préalable comme, par exemple mais pas seulement, héberger un proxy ou un nœud TOR ou utiliser des logiciels de P2P. OVH duplique aussi tous les mails sortants pour analyse temps-réel anti-spam ... Donc si vous cherchez un vrai accès à Internet, même grâce à un VPN, je vous conseille plutôt les FAI associatifs de la Fédération FDN qui offrent une telle garantie par design, sans fluctuation ni d'exceptions.

En supplément, "depuis Internet", je souhaite que mon client VPN soit vu avec une IP distincte de celle attribuée à mon dédié. Il faut donc utiliser une IP failover et faire en sorte que le trafic sorte et entre de notre dédié par cette IP.

Table des matières

Sources

Je clos le suspens de suite : ce billet est une arnaque ! On trouve les morceaux de config OpenVPN à peu près partout : OpenVPN sur le Wiki Debian.org, le man d'OpenVPN est bien fait et le petit morceau de conf pour sortir du VPN avec une IP failover est aussi documenté un peu partout : Utiliser un VPN avec une IP failover chez kdecherf (git)-[blog] %. Pour ceux qui cherchent comment monter un serveur VPN sur leur dédié OVH sans utiliser une IP failover : il suffit de faire un SNAT sur l'IP principale du serveur ou, au pire, un masquerade sur ce qui sort par eth0.

Néanmoins, parce que je vous aime bien, je vais vous donner ma config.

Trêve de blabla, on commence !

Au niveau d'OVH, c'est très simple : il suffit de commander une IP failover, de la payer et d'attendre un peu qu'elle soit active. Rien de plus. Pas besoin de définir une MAC virtuelle, rien.

Point to point et chiffrement symétrique

Dans un premier temps, on va utiliser une topologie point à point avec un chiffrement symétrique.

On installe OpenVPN sur le serveur et sur le client :

apt-get install openvpn

Pour l'instant, je ne veux pas qu'OpenVPN démarre tout seul au boot donc je lance la commande suivante sur le client et sur le serveur :

update-rc.d -f openvpn remove

On crée la config côté serveur (dans /etc/openvpn/server.conf par exemple) :

proto udp
port 1194
dev tun
 
mode p2p
 
cipher AES-256-CBC
secret /etc/openvpn/vpn-static.key
 
comp-lzo
 
verb 3
 
persist-key
persist-tun
ping 30
ping-exit 90
 
user openvpn
group openvpn
 
ifconfig 198.18.0.1 198.18.0.2
script-security 2
up /etc/openvpn/up.sh
plugin /usr/lib/openvpn/openvpn-down-root.so /etc/openvpn/down.sh

Quelques remarques :

  • Pour le choix tun/tap (route/bridge) : comme je n'ai pas besoin des fonctionnalités des tap (broadcast, donc possibilité d'utiliser NetBios/SMB, possibilité d'utiliser d'autres protocoles de couches 3, ... voir : What is the difference between bridging and routing? sur OpenVPN.net et Bridging vs. routing sur OpenVPN community), j'ai choisi le mode router/tun pour profiter de l'avantage d'avoir moins d'overhead.
  • Pour l'adressage du serveur et du client, j'ai pris le bloc 198.18.0.0/30 qui est englobé dans 198.18.0.0/15 qui est un préfixe réservé à l'IANA pour les tests et qui n'est donc pas annoncé globalement. L'idée est d'éviter une éventuelle collision. Comme ce préfixe est méconnu et que les réseaux auxquels je me connecte sont adressés soit en public soit avec les préfixes du RFC 1918, je suis sûr d'être tranquille. Notez qu'il serait peut-être plus pertinent de prendre un préfixe /30 dans 10.0.0.0/8. La probabilité de se connecter à un réseau existant utilisant ce bloc me paraît faible.
  • « plugin /usr/lib/openvpn/openvpn-down-root.so » permet de lancer le script de fin avec les droits root alors même qu'OpenVPN a, comme on le lui a demandé, perdu, dès la fin de l'initialisation, les droits root pour ceux de l'utilisateur openvpn et du groupe openvpn.

On crée la config côté client (dans /etc/openvpn/client.conf, par exemple) :

proto udp
remote <IP PRINCIPALE DU SERVEUR> 1194
dev tun
 
cipher AES-256-CBC
secret /etc/openvpn/vpn-static.key
 
comp-lzo
 
verb 3
 
persist-key
persist-tun
ping 30
ping-exit 90
explicit-exit-notify
 
user openvpn
group openvpn
 
ifconfig 198.18.0.2 198.18.0.1
route-gateway 198.18.0.1
redirect-gateway def1

Sur le serveur, on crée les deux scripts. D'abord /etc/openvpn/up.sh :

#!/bin/bash
 
ifconfig br0:1 <IP FAILOVER> netmask 255.255.255.255 broadcast <IP FAILOVER>
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s 198.18.0.0/30 ! -d 198.18.0.0/30 -j SNAT --to-source <IP FAILOVER>
iptables -t nat -A PREROUTING -d <IP FAILOVER> -j DNAT --to-destination 198.18.0.2

Puis le script /etc/openvpn/down.sh :

#!/bin/bash
 
echo 0 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -D POSTROUTING -s 198.18.0.0/30 ! -d 198.18.0.0/30 -j SNAT --to-source <IP FAILOVER>
iptables -t nat -D PREROUTING -d <IP FAILOVER> -j DNAT --to-destination 198.18.0.2
ifconfig br0:1 down

Quelques remarques :

  • J'ai configuré l'alias sur br0 car j'utilise un bridge pour attribuer des IP failover à mes conteneurs LXC (voir Utiliser LXC sur un Kimsufi). Si vous n'avez pas de VM ni de conteneurs LXC ou autre montage du même type, alors il faudra faire l'alias sur eth0.
  • Mapper 198.18.0.2 à l'IP failover suffit amplement. Néanmoins, j'ai voulu jouer la prudence au cas où le serveur émettrait avec 198.18.0.1 car je ne suis pas sur mon réseau mais sur celui d'OVH. Ce scénario ne devrait jamais se produire mais bon ... Au moins comme ça, ça sortira avec l'IP failover. Si je ne vois rien passer, OVH m'indiquera un problème avec cette IP et je saurai alors que le problème vient du VPN sans passer 5h à essayer de comprendre. Indiquer un /30 est par facilité : on pourrait utiliser les ip set pour indiquer les 2 adresses IP précises.
  • Le DNAT n'est clairement pas obligatoire. C'est pour le fun ... Si l'on veut héberger des services derrière ce VPN ...
  • Pour ceux qui cherchent comment monter un serveur VPN sur leur dédié OVH sans utiliser une IP failover, seule la première règle iptables change :
    iptables -t nat -D POSTROUTING -s 198.18.0.0/30 ! -d 198.18.0.0/30 -j SNAT --to-source <IP PRINCIPALE DU SERVEUR>

    Forcement, vous ne pouvez alors pas faire de DNAT complet mais uniquement pour certains ports que vous voulez rediriger vers votre client VPN.

  • Enfin, selon votre configuration, il faudra autoriser le transfert de paquet depuis/vers le VPN. Cela se fait dans chaîne FORWARD. 😉

On rend ces scripts exécutables :

chmod u+x /etc/openvpn/up.sh /etc/openvpn/down.sh

On crée l'utilisateur openvpn :

adduser openvpn --disabled-login --no-create-home --shell /bin/false

Sur le serveur ou sur le client, on génère la clé cryptographique :

openvpn --genkey --secret /etc/openvpn/vpn-static.key

Il faudra la transférer à l'autre partie via scp puis vérifier les droits : root:root 400.

Enfin, selon votre configuration, il faudra autoriser OpenVPN dans votre firewall, en entrée et en sortie. Aussi bien sur le client que sur le serveur.

Il est l'heure de tester (sur le client et le serveur) :

service openvpn restart

Normalement, le client doit pouvoir pinguer 198.18.0.1 et sortir sur Internet par le VPN. L'adresse IP "vue depuis Internet" doit être l'IP failover.

Pour ceux que ça intéresse, voici les configurations équivalentes mais en utilisant des tap. Pour le serveur :

proto udp
port 1194
dev tap
 
cipher AES-256-CBC
secret /etc/openvpn/vpn-static.key
 
comp-lzo
 
verb 3

persist-key
persist-tun
ping 30
ping-exit 90
 
user openvpn
group openvpn
 
ifconfig 198.18.0.1 255.255.255.252
script-security 2
up /etc/openvpn/up.sh
plugin /usr/lib/openvpn/openvpn-down-root.so "/etc/openvpn/down.sh"

Pour le client :

proto udp
remote <IP PRINCIPALE DU SERVEUR> 1194
dev tap
 
cipher AES-256-CBC
secret /etc/openvpn/vpn-static.key
 
comp-lzo
 
verb 3
 
persist-key
persist-tun
ping 30
ping-exit 90
explicit-exit-notify
 
user openvpn
group openvpn
 
ifconfig 198.18.0.2 255.255.255.252
route-gateway 198.18.0.1
redirect-gateway def1

Point to point et chiffrement asymétrique

L'inconvénient principal du chiffrement symétrique est qu'il ne permet pas le perfect forward secrecy. Bien que, comme je l'ai déjà dit, mon VPN me servira principalement à faire des tests et ne transmettra pas mes secrets les plus noirs, je me dis qu'il peut être intéressant de faire une topologie p2p avec du chiffrement asymétrique, for ze fun.

Attention à ne pas mal interpréter mes propos : le chiffrement du tunnel reste symétrique. Simplement, lors de l'initialisation, le client et le serveur échangent une clé temporaire pour réaliser le chiffrement symétrique du tunnel. C'est cet échange initial qui se fait en utilisant la cryptographie asymétrique.

Pour cette partie, on va suivre d'encore plus près le tutoriel du wiki Debian.org. Reportez-vous à la partie « TLS-enabled VPN ». La génération du matériel cryptographique est identique : seules les configurations d'OpenVPN sont différentes.

Voici la configuration du serveur :

proto udp
port 1194
dev tun
 
mode p2p
tls-server
 
ca      /etc/openvpn/easy-rsa/keys/ca.crt
cert    /etc/openvpn/easy-rsa/keys/server.crt
key     /etc/openvpn/easy-rsa/keys/server.key
dh      /etc/openvpn/easy-rsa/keys/dh1024.pem
 
comp-lzo
 
verb 3
 
persist-key
persist-tun
ping 30
ping-exit 90
 
user openvpn
group openvpn
 
ifconfig 198.18.0.1 198.18.0.2
script-security 2
up /etc/openvpn/up.sh
plugin /usr/lib/openvpn/openvpn-down-root.so /etc/openvpn/down.sh

ÉDIT du 12/01/2014 à 13h10 : Il faudra prévoir la révocation des certificats clients. C'est toujours utile en cas de perte ou de compromission, surtout si vous distribuez des accès (oui oui, le titre du billet cause d'un VPN unipersonnel mais bon) ... Fin de l'édit

La configuration du client :

proto udp
remote <IP PRINCIPALE DU SERVEUR> 1194
dev tun
 
tls-client
 
ca /etc/openvpn/easy-rsa/keys/ca.crt
cert /etc/openvpn/easy-rsa/keys/guigui.crt
key /etc/openvpn/easy-rsa/keys/guigui.key
 
comp-lzo
 
verb 3
 
persist-key
persist-tun
ping 30
ping-exit 90
explicit-exit-notify
 
user openvpn
group openvpn
 
ifconfig 198.18.0.2 198.18.0.1
route-gateway 198.18.0.1
redirect-gateway def1

Il est l'heure de tester (sur le client et le serveur) :

service openvpn restart

Normalement, le client doit toujours pouvoir pinguer 198.18.0.1 et sortir sur Internet par le VPN. L'adresse IP "vue depuis Internet" doit toujours être l'IP failover.

La même mais en mieux

Le résultat obtenu jusque-là est pas mal. Il manque un dernier truc : pour l'instant le script up est exécuté au démarrage d'OpenVPN, juste après la création de l'interface tun. Le down est exécuté à l'arrêt d'OpenVPN. J'aimerais que les scripts soient exécutés uniquement lorsque le client se connecte/déconnecte de mon VPN. Comme ça, le reste du temps je n'ai pas de la config qui ne sert à rien en train de tourner. Pas de forward, pas de NAT, l'IP failover ne répond pas aux requêtes, ...

Cela est possible en utilisant les directives client-connect/client-disconnect d'OpenVPN. Mais elles sont disponibles uniquement en mode serveur. Ce mode requiert l'utilisation d'un /29 au minimum donc on va avoir un peu de configuration à changer.

On s'occupe d'abord des configurations OpenVPN. Celle du serveur :

proto udp
port 1194
 
dev tun
 
mode server
max-clients 1
 
tls-server
ca      /etc/openvpn/easy-rsa/keys/ca.crt
cert    /etc/openvpn/easy-rsa/keys/server.crt
key     /etc/openvpn/easy-rsa/keys/server.key
dh      /etc/openvpn/easy-rsa/keys/dh1024.pem
 
comp-lzo
 
verb 3
 
persist-key
persist-tun
ping 30
ping-exit 90
 
user openvpn
group openvpn
 
ifconfig 198.18.0.1 198.18.0.2
ifconfig-pool 198.18.0.5 198.18.0.6
route 198.18.0.0 255.255.255.240
push "route 198.18.0.1"
 
script-security 2
client-connect "/usr/bin/sudo -u root /etc/openvpn/up.sh"
client-disconnect "/usr/bin/sudo -u root /etc/openvpn/down.sh"

La configuration du client :

proto udp
port 1194
 
dev tun
 
client
remote <IP PRINCIPALE DU SERVEUR> 1194
 
tls-client
ca /etc/openvpn/easy-rsa/keys/ca.crt
cert /etc/openvpn/easy-rsa/keys/guigui.crt
key /etc/openvpn/easy-rsa/keys/guigui.key
 
comp-lzo
 
verb 3
 
persist-key
persist-tun
ping 30
ping-exit 90
explicit-exit-notify
 
user openvpn
group openvpn
 
redirect-gateway def1

On remarque l'utilisation de sudo pour lancer les scripts à la connexion/déconnexion. En effet, lorsque le client se connectera ou se déconnectera, il y aura longtemps que le serveur OpenVPN aura perdu les droits root. Il faut donc rajouter une ligne au fichier /etc/sudoers :

openvpn		ALL=(root)	NOPASSWD:/etc/openvpn/up.sh,/etc/openvpn/down.sh

Les scripts d'up et de down changent aussi : on NAT le /29 et on redirige le DNAT sur la bonne IP :

#!/bin/bash
 
echo 0 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -D POSTROUTING -s 198.18.0.0/29 ! -d 198.18.0.0/29 -j SNAT --to-source <IP FAILOVER>
iptables -t nat -D PREROUTING -d <IP FAILOVER> -j DNAT --to-destination 198.18.0.6
ifconfig br0:1 down

Les mêmes modifications s'appliquent au script de déconnexion. Je vous laisse faire les changements. 🙂

Voilà, on arrive à la fin de ce que je voulais vous montrer.

DNSSEC pour 2013

Pour cette nouvelle année, j'ai décidé de signer mes zones avec DNSSEC. Ce billet est donc une sorte de retour d'expérience/un avis mélangé à un semblant de tutoriel OpenDNSSEC.

Table des matières

Ressources

Si vous débutez avec DNSSEC, je vous recommande la lecture suivante : DNS : Attaques et sécurisation.

Pour suivre la suite de ce billet, je vous recommande aussi la lecture suivante : RFC 6781: DNSSEC Operational Practices, Version 2 chez Stéphane Bortzmeyer.

Objectifs

  • Signer une zone qui se trouve en dessous de fr. La zone fr. est signée et accepte les délégations signées donc pas de problèmes particuliers à envisager.
  • Signer guiguishow.info. La zone info. est signée mais la soumission de délégations signées n'est pas encore ouverte à tous. Il faudra donc utiliser DLV et le registre DLV de l'ISC.
  • Signer des zones reverse v6 déléguées par Hurricane Electric (6in4). HE ne signe pas ses zones donc il faudra utiliser DLV et le registre DLV de l'ISC.

Note : je crois savoir que DLV est très peu utilisé. DNSSEC étant peu utilisé, en utilisant DLV, vous vous adressez à une masse très faible car l'usage de DLV pour valider une zone nécessite que le résolveur soit configuré et ce, avec le même registre DLV que celui que vous avez choisi pour soumettre votre délégation signée. Mais c'est "fun" en attendant la signature de plus de TLD.

Motivations

Mes motivations sont très simples : "for ze fun" et confronter la théorie (mes connaissances) à la réalité. Pour ce dernier point, un labo Netkit n'est pas suffisant pour avoir une vision complète de tous les aspects de DNSSEC et notamment de l'aspect opérationnel.

Données techniques

Avant de commencer, il faut bien comprendre que les bonnes valeurs pour ces "paramètres" ne sont pas fixes : les valeurs optimales sont encore en discussion (quelle durée de vie pour une clé ? pour une signature ?) et les valeurs réelles dépendent de vos choix et de votre infrastructure.

On veut que les zones soient signées hors-ligne : ce n'est pas le serveur maître faisant autorité sur la zone qui la signera.

Les paramètres des clés sont classiques. Choix du modèle à 2 paires de clés (qui n'est pas obligatoire). KSK : RSASHA256 - 2048 bits - durée de vie d'un an. ZSK : RSASHA256 - 1024 bits - durée de vie d'un mois.

Pour les signatures, une durée de vie d'une semaine est convenable.

Pour la preuve de non-existence, j'utiliserai NSEC3 : 10 itérations, sel de taille 8 changeant tous les 7 jours. Une sorte de compromis entre le RFC 5155 qui recommande de changer le sel à chaque signature et le RFC 6781 qui recommande de le changer en même temps que la ZSK. Après coup, l'usage de NSEC3 est le choix que je pourrais le plus remettre en question : mes zones sont "banales" : un SOA, des NS, un MX, un CNAME www, un A, un AAA, ... Rien qui ne puisse pas être très facilement deviné. L'usage de NSEC n'aurait pas été un problème.

La cryptographie employée par DNSSEC demande de la rigueur et une maintenance régulière sous peine de rendre une zone non-validable pendant un certain temps (qui dépend des TTL). J'ai décidé d'utiliser OpenDNSSEC, un logiciel qui prend en charge un certain nombre d'opérations (génération des clés, signature des zones, remplacement (rollover) des clés) et permet de limiter les erreurs lors de la réalisation des tâches restantes (soumission du DS à la zone parente lors d'un rollover de la KSK, par exemple).

Avant de continuer, je vous conseille les documentations suivantes concernant OpenDNSSEC :

Valider une zone en utilisant le registre DLV de l'ISC

Avant de commencer, nous allons voir comment configurer Unbound pour valider une zone en utilisant le registre DLV de l'ISC. Ce registre est simplement le plus gros registre DLV d'où mon choix pour publier mes DLV dans ce registre.

Dans un premier temps, il faut ajouter une ligne dans le fichier de configuration (/etc/unbound/unbound.conf) :

dlv-anchor-file: "/var/lib/unbound/dlv.isc.org.key"

Ensuite, il faut créer le fichier dlv.isc.org.key. Pour trouver la dernière KSK du registre en usage, voir : ISC's DLV Registry. À l'heure actuelle, mon fichier contient donc :

dlv.isc.org. IN DNSKEY 257 3 5 BEAAAAPHMu/5onzrEE7z1egmhg/WPO0+juoZrW3euWEn4MxDCE1+lLy2 brhQv5rN32RKtMzX6Mj70jdzeND4XknW58dnJNPCxn8+jAGl2FZLK8t+ 1uq4W+nnA3qO2+DL+k6BD4mewMLbIYFwe0PG73Te9fZ2kJb56dhgMde5 ymX4BI/oQ+cAK50/xvJv00Frf8kw6ucMTwFlgPe+jnGxPPEmHAte/URk Y62ZfkLoBAADLHQ9IrS2tryAe7mbBZVcOwIeU/Rw/mRx/vwwMCTgNboM QKtUdvNXDrYJDSHZws3xiRXF1Rf+al9UmZfSav/4NWLKjHzpT59k/VSt TDN0YUuWrBNh

Un reload d'Unbound et c'est prêt.

Pour tester : DLV Test Zones par DNS-OARC. Il faut que dig vous affiche le flag "AD". Si vous avez envie de rigoler, je vous conseille de résoudre les autres noms (a.nsec.dlvtest.dns-oarc.net txt, b.nsec.dlvtest.dns-oarc.net txt, ..., z.nsec.dlvtest.dns-oarc.net txt) car chaque lettre = un mot 😀 . Oui, un rien m'amuse. 😛

Installer OpenDNSSEC

L'installation d'OpenDNSSEC sous Debian est capricieuse : le dépôt squeeze-backports fournit une version en état de marche. Pour les dépôts stable et testing, je reste bloqué sur une erreur lors du lancement des démons :

ods-enforcerd: Error creating key in repository SoftHSM
ods-enforcerd: generate key pair: CKR_GENERAL_ERROR

Ce message d'erreur apparaît sur la liste de diffusion des devs d'OpenDNSSEC mais elle n'est pas résolue. Me concernant, ce n'est pas bien grave puisque j'utilise les backports pour installer certaines des applications de mon serveur, mais je voulais le signaler.

ÉDIT du 19/05/2013 à 21h00 : Une solution se trouve en filigrane ici : [Opendnssec-user] Error creating key in repository SoftHSM. On ajoute donc l'user opendnssec au groupe softhsm (voir ci-dessous) puis on applique les droits nécessaires sur le fichier /var/lib/softhsm/slot0.db : root:softhsm, 664. Ça marche sans problèmes sur une Wheezy sans avoir recours aux backports. Fin de l'édit

Sur une Squeeze utilisant les backports, l'installation se résume à :

apt-get install softhsm opendnssec
adduser opendnssec softhsm

L'ajout de l'utilisateur opendnssec au groupe softhsm permet d'éviter l'erreur :

SoftHSM: C_Initialize: Could not open the config file: /etc/softhsm/softhsm.conf

Mise en place basique du couple SoftHSM/OpenDNSSEC

Tout est détaillé sur le blog de S. Bortzmeyer dont j'ai donné le lien ci-dessus donc je vais être léger sur cette partie.

SoftHSM

Initialiser notre HSM :

softhsm --init-token --slot 0 --label "OpenDNSSEC"

Une petite astuce en passant : si vous oubliez le SO PIN de votre SoftHSM, il y a moyen de rattraper le coup (de reinit le SO PIN). D'abord, il faut calculer le hash de votre mot de passe via la fonction de hachage sha256 :

echo -n "123412341234" | shasum -a 256 | tr '[:lower:]' '[:upper:]'

Attention : ici, le SO PIN est 1234, pas 123412341234 ! Il faut chaîner 3 fois de suite le SO PIN désiré pour obtenir le bon hash.

Ensuite, comme SoftHSM utilise, par défaut, sqlite, le changement du SO PIN revient à une simple requête SQL (en root, bien sûr) :

sqlite3 /var/lib/softhsm/slot0.db
sqlite> UPDATE Token SET value='66C782E8F95BA958F28ADAAE576C42A263C2449AF416FB844499BEF7FD41B2D0' WHERE variableID='1';

Bien sûr, remplacez "66C782E8F95BA958F28ADAAE576C42A263C2449AF416FB844499BEF7FD41B2D0" par le hash obtenu à l'étape précédente.

OpenDNSSEC

D'abord, il faut modifier le fichier /etc/opendnssec/conf.xml pour décommenter le bloc concernant le dépôt SoftHSM et changer l'user PIN voire le label du token si vous l'avez modifié lors de l'initialisation de softhsm :

        <RepositoryList>
 
                <Repository name="SoftHSM">
                        <Module>/usr/lib/softhsm/libsofthsm.so</Module>
                        <TokenLabel>OpenDNSSEC</TokenLabel>
                        <PIN>1234</PIN>
                        <SkipPublicKey/>
                </Repository>

<!--
                <Repository name="sca6000">
                        <Module>/usr/lib/libpkcs11.so</Module>
                        <TokenLabel>Sun Metaslot</TokenLabel>
                        <PIN>test:1234</PIN>
                        <Capacity>255</Capacity>
                        <RequireBackup/>
                        <SkipPublicKey/>
                </Repository>
-->
 
        </RepositoryList>

Tant qu'on est dans conf.xml, on décommente la ligne suivante :

<NotifyCommand>/usr/sbin/rndc reload %zone</NotifyCommand>

À la fin de la signature, OpenDNSSEC demandera à BIND de recharger la zone.

Ensuite, on ajoute les zones à gérer en modifiant le fichier /etc/opendnssec/zonelist.xml ou en les ajoutant avec la commande ods-ksmutil zone add. Exemple :

ods-ksmutil zone add -z example.com -p default -i /etc/bind/master.example.com -o /etc/bind/master.example.com.signed

Ici, on ajoute la zone example.com, avec la politique "default", en indiquant que le fichier de zone est /etc/bind/master.example.com et que le fichier de zone signé devra être mis dans /etc/bind/master/master.example.com.signed.

On met en place les politiques :

ods-ksmutil setup

Avant-dernière tâche, on pense à éditier son named.conf pour indiquer que le fichier à servir est celui signé, pas l'autre !

Enfin, on lance les démons OpenDNSSEC :

ods-control start

Normalement, la machine se met en route : OpenDNSSEC génère des clés (vérifiable avec la commande ods-ksmutil key list), signe la zone, la fait recharger. Votre serveur de noms devrait maintenant servir des informations signés.

Note : Si vous avez une erreur de ce type dans les logs :

stderr from sorter: /chemin/vers/le/fichier/de/zone/à/signer:7: Unknown RR:

Cela signifie que votre fichier de zone contient des lignes qui contiennent uniquement des espaces et des tabulations. Supprimez les lignes de ce genre et le signer n'échouera plus.

Utilisation de base

Pour finir la mise en place de DNSSEC, il faut attendre que les résolveurs prennent connaissances des clés. OpenDNSSEC indique la date/heure de la prochaine étape :

ods-ksmutil key list
Keys:
Zone:                           Keytype:      State:    Date of next transition:      
guiguishow.info                 KSK           publish   2013-01-11 10:32:52       
guiguishow.info                 ZSK           active    2013-02-01 23:16:34

La clé est publiée dans la zone mais ne sert pas encore à la valider. À la date indiquée, OpenDNSSEC indiquera :

ods-ksmutil key list
Keys:
Zone:                           Keytype:      State:    Date of next transition:      
guiguishow.info                 KSK           ready     waiting for ds-seen ...
guiguishow.info                 ZSK           active    2013-02-01 23:16:34

OpenDNSSEC attend donc de savoir quand le DS apparaîtra dans la zone parente. Il est temps de demander le DS et d'effectuer sa soumission dans la zone parente.

Rappel : un RR DLV a le même format qu'un RR DS, seul le type change.

D'abord, on récupère le DS :

ods-ksmutil key export --ds
;ready KSK DS record (SHA1):
guiguishow.info.	3600	IN	DS	8785 8 1 e46b5d056b0c0bb1176aaa5781676c212e6926a2

;ready KSK DS record (SHA256):
guiguishow.info.	3600	IN	DS	8785 8 2 905147c7558bf1abcc5c6accbc2ad6fcff05d2c82cb18f77e30bf2b9b981427e

Note : chez moi, DNSSEC n'a pas voulu me communiquer les DS alors qu'il m'indiquait un "waiting for ds-seen" ... Je l'ai donc forcé avec "--keystate ready".

Rappel : la soumission d'un DS avec 2 algorithmes de hachage n'est pas obligatoire mais seulement recommandée pour des questions de compatibilité avec les résolveurs qui ne supportent pas SHA256. Comme je considère qu'à moment donné, il faut faire évoluer son parc et comme ma zone utilise RSASHA256, j'ai décidé de ne publier que le hash issu de SHA256 (même si le registre DLV de l'ISC a généré celui issu de la fonction SHA1 et l'a publié automatiquement ...).

Ensuite, on soumet ce DS à la zone parente. La procédure dépend de votre bureau d'enregistrement. Dans le cas de Gandi, il faut la clé publique. Pour cela, rien de mieux, à mon avis, qu'un dig :

dig @127.0.0.1 DNSKEY guiguishow.info
guiguishow.info.	1327	IN	DNSKEY	256 3 8 AwEAAcFrABdS/Osvt5rY20EPOldFT7pFA+ubLRe4+03NpkGkUX52BBFr Vj5Ozj+9MeMC1Q40bNT/cQMZsT3YT/oVcvo+Z+WHhiaI6LV97rlOpbBS 7Q3CwEGJYIy/Qj8PeapLDvzHmXILgJypAvDUNKVPUG7JCn86VnIzesmN wEfN4MhF
guiguishow.info.	1327	IN	DNSKEY	257 3 8 AwEAAbWAbGnXj4waroP0NZLm2RD6AgITwj2wZAvA0y9pKRJRrX946yoO k6p6+gJyqmJ3A+bkEaKUySSlXSPw7xDQJFKx1lz7hivURd1K3LZM+Yc6 sNvw2QPbmpBzNIE2Y9gvrf91DjR638AEHFdVUbg/AACQLzMR16BSQKer n8Za+l+hRwTDvn4+18bXPcnt2Ju5OMssolwwKCIdKlc2VWa4pHA4nDSl o+aylNe2Y6O6jsRRM40o2AE8VAjzFgR17h+4nyT/U9M3D7j/tMo6UaZK 3LNGUB5ZaqNq5/BNumRBqbNOGnszwljWuVwF126KtlO5CzL9RufLjzoR 5ezMs8RJZ7U=

Je devais donc donner ce qui suit à Gandi :

AwEAAbWAbGnXj4waroP0NZLm2RD6AgITwj2wZAvA0y9pKRJRrX946yoO k6p6+gJyqmJ3A+bkEaKUySSlXSPw7xDQJFKx1lz7hivURd1K3LZM+Yc6 sNvw2QPbmpBzNIE2Y9gvrf91DjR638AEHFdVUbg/AACQLzMR16BSQKer n8Za+l+hRwTDvn4+18bXPcnt2Ju5OMssolwwKCIdKlc2VWa4pHA4nDSl o+aylNe2Y6O6jsRRM40o2AE8VAjzFgR17h+4nyT/U9M3D7j/tMo6UaZK 3LNGUB5ZaqNq5/BNumRBqbNOGnszwljWuVwF126KtlO5CzL9RufLjzoR 5ezMs8RJZ7U=

Le registre DLV de l'ISC est moins exigeant : un DS ou un DNSKEY brut fait l'affaire. Exemple :

guiguishow.info.	3600	IN	DS	8785 8 2 905147c7558bf1abcc5c6accbc2ad6fcff05d2c82cb18f77e30bf2b9b981427e

Note : je ne détaille pas la soumission d'une délégation signée au registre DLV de l'ISC car c'est simple (création d'un compte, création de la zone, soumission de la clé, vérification de l'identité, fin) et l'interface web est vraiment bien faite.

On attend que le DS/DLV apparaisse dans la zone parente en testant régulièrement :

dig DLV guiguishow.info.dlv.isc.org 
dig DS mondomaine.fr

Quand la réponse est positive (prenez garde au cache "négatif"), il faut prévenir OpenDNSSEC :

ods-ksmutil key ds-seen --zone guiguishow.info --keytag 8785

OpenDNSSEC marquera la clé comme étant active (clé utilisée pour signer).

Il faudra soit réaliser ces opérations lors de chaque roulement des clés, soit automatiser la procédure (soumettre la clé via l'API de votre bureau d'enregistrement + vérification automatisée de l'apparition du DS/DLV). Pour ce dernier cas, S. Bortzmeyer pointe des scripts qui réalisent ces opérations.

Lorsque vous effectuez des modifications dans vos fichiers de zones, pensez à demander un re-signe de la zone concernée :

ods-signer sign <zone>

Configuration avancée

conf.xml

Pour ma part, je n'ai pas beaucoup modifié ce fichier.

J'ai simplement :

  • activé le dépôt softHSM ;
  • changé la facility syslog (local2) afin de pouvoir séparer les logs d'OpenDNSSEC dans un fichier distinct. Une ligne ressemblant à ça suffit ensuite dans la configuration de rsyslog :
    if $syslogfacility-text == 'local2' or $programname == 'ods-enforcerd' then /var/log/opendnssec.log
    & ~
  • activé l'avertissement des rollover : OpenDNSSEC, prévient, à l'avance, via les logs, d'un rollover "imminent".
    <RolloverNotification>P7D</RolloverNotification>
  • tous les modules d'OpenDNSSEC (enforcer, signer, auditor), fonctionne avec des droits restreints. À ce sujet, la restriction de droits de l'enforcer m'a posé problème : l'enforcer ne pouvait plus se lancer. "ods-enforcerd: /var/lib/opendnssec/db/kasp.db.our_lock could not be opened" apparaissait dans les logs. Il suffit de supprimer le fichier kasp.db.lock et de relancer les démons OpenDNSSEC. En effet, ce fichier appartenait à root quand l'enforcer tournait avec les droits root. L'enforcer, qui a maintenant des droits restreints, ne peut donc pas le modifier/supprimer.

kasp.xml

C'est ici qu'il faut définir les politiques qui seront appliquées aux zones.

Sur la version issue des squeeze-backports, l'algorithme à utiliser pour les clés n'est pas celui que nous voulons : il faut mettre 8 pour RSASHA256. Il me semble aussi que la validité des signatures est trop haute (attaque par rejeu, tout ça, ...). Le reste se configure selon vos propres choix et selon les valeurs recommandées par les RFC/les usages. La documentation officielle est relativement bien faite pour vous accompagner (et ce n'est pas souvent que je dis ça).

Sur les versions récentes (debian testing, ...), les algorithmes et la durée de vie des signatures sont ceux conseillés. Il ne vous reste donc plus qu'à régler selon vos choix, selon votre zone et selon votre zone parente.

Dans mes politiques, j'ai passé le nombre d'itération NSEC3 de 5 à 10 (compromis entre les énormes valeurs conseillées par les RFC (150, ...) et les valeurs appliquées par les TLD (5, 1 voire 0)). J'ai demandé un sel NSEC3 différent tous les 7 jours. Je n'ai pas utilisé de clé de réserve car la fonctionnalité ne semble pas totalement prête (dixit la doc officielle). J'ai utilisé le format de serial "datecounter" pour mes zones.

Ensuite, j'ai adapté les paramètres de ma zone. Cela permet à DNSSEC de mieux calculer le moment propice pour chaque étape d'un remplacement des clés, par exemple. J'ai mis un PropagationDelay à PT12600S pour ma zone : refresh + retry de mes zones + marge supplémentaire. J'ai indiqué un TTL et un minimum de 3600, car c'est les valeurs utilisées par défaut par OpenDNSSEC.

Ensuite, j'ai adapté les paramètres de la zone parente. Là encore, cela permet à DNSSEC de mieux calculer le moment propice pour chaque étape d'un remplacement des clé et surtout de la KSK. J'ai laissé le PropagationDelay à PT9999S car je fais la soumission du DS à la main, donc OpenDNSSEC doit m'attendre et prendre en compte ma disponibilité dans ses calculs. Pour le TTL du DS et du SOA, il est de 172800 secondes pour la zone fr. et de 3600 pour le registre DLV de l'ISC. Pour le paramètre minimum du SOA, il est de 5400 dans la zone fr. et de 3600 dans le registre DLV de l'ISC.

Pour résumer, j'ai conservé la politique par défaut et ajouté deux politiques : une pour ma zone sous fr. et une autre pour mes autres zones car elles utilisent toutes le registre DLV de l'ISC et partagent donc les mêmes paramètres. Par contre la zone fr. n'a pas les mêmes TTL/refresh, ... que le registre DLV de l'ISC, d'où la nécessaire séparation.

zonefetch.xml

OpenDNSSEC n'est pas obligé de récupérer les fichiers de zones à signer sur le système de fichier local : il peut aussi utiliser un transfert de zone (AXFR). Comme cette fonctionnalité ne me convient pas totalement (réglagles globaux, pas par zone, imposible de dire si telle zone doit être récupérée via AXFR alors que telle autre zone doit être récupérée sur le système de fichier local, ...), je ne l'ai pas utilisé. Notons que l'arrivée des adapters dans la version 1.4 devrait permettre d'élargir grandement les possibilités de récupération/d'envoi des zones.

zonelist.xml

Il s'agit simplement des zones à gérer, avec leur politique, le fichier d'entrée et le fichier de sortie. Pas de piège, rien. Du coup, je ne pense pas qu'il soit utile que je détaille.

Conseil

Mon seul conseil sera de bien réfléchir à vos politiques avant de commencer à signer "pour de vrai". Je ne sais pas si je m'y suis mal pris, mais j'ai eu quelques déboires en changeant la politique associée à une zone en cours de route. Et quand je dis "déboirs", je veux dire destruction des clés par OpenDNSSEC et génération d'un nouveau trousseau. Si ça avait été en production à ce moment là, ça aurait causé un roulement brutal et forcé des clés et donc une incapacité à valider la zone temporairement et donc une incapacité d'accéder à mes "services" pour les systèmes/personnes qui utilisent un résolveur validant.

Mon utilisation

Les lecteurs habituels ne doivent pas être surpris que mon utilisation sorte du cadre de base. Je vais détaillé ma situation et ma configuration en me disant que ça peut toujours servir.

Mon infra

J'ai un seul serveur physique. À ce niveau, je n'ai aucun "service" ouvert vers l'exterieur. Viennent ensuite 2 conteneurs LXC qui contiennent chacun un domaine et les services associés.

La solution que j'ai choisi est d'installer OpenDNSSEC directement sur le serveur. Il signera ainsi les zones des 2 domaines. Les avantages sont :

  • Isolation : impossible d'apercevoir OpenDNSSEC depuis les LXC.
  • Accès aux systèmes de fichiers des LXC donc possiblité de lire les fichiers de zones "clairs" et de déposer les fichiers de zones signés.

L'inconvénient est que l'on reste sur la même machine. Je ne suis donc pas dans une signature totalement offline (serveur isolé d'Internet, sauf le temps de transférer le résultat de la signature). Je n'ai pas une grosse infrastructure de dingue, comme certains 😉 donc il m'est difficile d'isoler plus. Il faut nuancer en disant qu'il faut être root ou l'utilisateur opendnssec pour récupérer les clés. Si un attaquant arrive à obtenir une élévation de privilèges, il faut considérer que tout est déjà perdu, et pas seulement les parties privées des clés DNSSEC.

En gros, le scénario est le suivant : OpenDNSSEC générera les fichiers de zones signés dans un dossier de travail (/var/lib/opendnssec, comme par défaut) puis ferra appel à un script bash. Ce script demandera une élévation de privilèges, copiera les fichiers de zones aux bons endroits dans les conteneurs LXC, se connectera, en SSH, dans les conteneurs pour demander à BIND de recharger les zones modifiées.

Mise en place

Sur le signeur

On crée un script que l'on mettra dans /etc/opendnssec avec les bonnes permissions (root - opendnssec - 750) ;

#!/bin/bash
 
if [ $# -ne 1 ]

then
        echo "Trop ou pas assez d'arguments"
        logger "DNSSEC commande-lanceur - Trop ou pas assez d'arguments"
        exit 2
fi

 
if [ $1 = "guiguishow.info" ]
then
        sudo cp /var/lib/opendnssec/signed/guiguishow.info /chemin/vers/le/conteneur/lxc/bind/guiguishow.info.signed
        ssh -i /etc/opendnssec/ssh.key opendnssec@viki.guiguishow.info 'sudo rndc reload && sudo rndc notify guiguishow.info'

        exit 0
fi

Pour chaque nouveau serveur, il suffit d'ajouter un bloc if-then-fi et pour ajouter une zone supplémentaire à un serveur existant, il suffit d'ajouter une condition "OU" au if et les instructions qui vont bien dans le bloc de traitement.

Pour l' élévation de privilèges, il faut modifier le fichier /etc/sudoers en y ajoutant :

opendnssec      ALL=(root)      NOPASSWD:/bin/cp

Enfin, il faut modifier le fichier /etc/opendnssec/conf.xml pour lui indiquer la bonne commande à executer après une signature, c'est-à-dire, appeler notre script bash :

<NotifyCommand>bash /etc/opendnssec/wrapper %zone</NotifyCommand>
Sur les serveurs de noms

On a déjà vu les principaux elements dans un autre billet : DDNS indépendant sur OpenWRT.

Il faut créer un utilisateur dédié (opendnssec, par exemple) sur chaque serveur, lui associer une connexion par clé publique, restreindre ses actions possibles dans la configuration de (ForceCommand) et modifier le sudoers en conséquence pour donner le droit à cet utilisateur de faire recharger les zones. Par exemple :

opendnssec ALL=(root) NOPASSWD:/usr/sbin/rndc

DDNS

Les lecteurs habituels le savent : j'utilise du DDNS pour connaître l'adresse IP de mon OpenWRT qui se trouve derrière une ligne ADSL avec une IP pas toujours fixe.

OpenDNSSEC ne semble pas gérer le DDNS. La fonctionnalité zonefetch permettrait de se mettre en attente d'un notify, de demander la zone via AXFR et de la signer. Mais comme je l'ai déjà dit, zonefetch ne me convient pas dans l'état actuel car une fois la fonctionnalité configurée, OpenDNSSEC tente de récupèrer toutes les zones par ce biais et non une seule ...

Du coup, je me retrouve à bidouiller encore du script shell pas propre. On va suivre approximativement le même plan que dans DDNS indépendant sur OpenWRT : la création d'un utilisateur dédié et la mise en place d'une connexion automatisée par clé publique restent inchangée.

Le script devient :

#!/bin/bash

if [ $# -ne 1 ]
then
        echo "Trop ou pas assez d'arguments"
        logger "DDNS openwrt - Trop ou pas assez d'arguments"
        exit 2
fi
 
 
echo $1 | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" > /dev/null
if [ $? -ne 0 ]
then
        echo "Le paramètre \"IP\" n'est pas au bon format."
        logger "DDNS openwrt - Paramètre IP pas au bon format."
        exit 2
fi
 
# Un petit délire pas utile : vérifier rapidement l'AS à qui a
# été distribuée l'IP passée en paramètre. Si ce n'est pas l'AS
# de mon FAI, alors soit il y a une erreur, soit le script
# n'est pas lancé depuis l'OpenWRT ... de là à parler d'une
# usurpation ... 
as=`whois $1 | grep origin | grep -oE "AS[0-9]{1,5}"`
if [ $? -eq 0 ] && [ $as != "AS5410" ]
then
        logger "DDNS openwrt - Usurpation"
        exit 1
fi
 
# Si l'IP passée en paramètre est la même que celle 
# actuellement dans la zone, on ne fait rien.
ipActuelle=`dig +short @viki.guiguishow.info openwrt.guiguishow.info`
if [ $ipActuelle !=  $1 ]
then
        sudo sed -i "s/^openwrt[ \t]\{2\}60[ \t]A[ \t]\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}/openwrt\t\t60\tA\t$1/g" /chemin/à/travers/container/LXC/vers/fichier/de/zone/guiguishow.info
 
        sudo ods-signer sign guiguishow.info
 
        logger "DDNS openwrt - MAJ OK"

fi

Note : 60 secondes est un TTL qui est trop court pour une zone signée car le résolveur doit demander l'enregsitrement, la signature et remonter la chaîne de confiance asssez souvent. Mais comme je suis censé être le seul utilisateur, je pense qu'un TTL de 60 secondes est tolérable.

Note 2 : On ne fait pas l'incrémentation du serial du SOA car OpenDNSSEC le fait pour nous.

L'utilisateur dédié (openwrt) doit avoir le droit d'écrire dans le fichier de zone ainsi que d'appeler le signer. Il faut donc lui donner la possiblité d'avoir plus de droits tout en limitant les actions possibles. Avec sudo, on rajoute une ligne dans le fichier /etc/sudoers :

openwrt            ALL=(root)      NOPASSWD:/bin/sed,/usr/sbin/ods-signer

Tester

Pour tester votre déploiement, je vous conseille :

  • Zonecheck.
  • dnsviz.net (prend en compte le registre DLV de l'ISC).
  • Le debugger des Verisign Labs (attention : il ne prend en compte le registre DLV de l'ISC).
  • dig sur un résolveur qui fait de la validation. Vous vérifiez toute la chaîne et devez obtenir le drapeau "AD" partout.

Améliorations

La première amélioration que je vise est dépendante de l'évolution d'OpenDNSSEC. En effet, la version 1.4 devrait intégrer des i/o adapters. Ils devraient permettre de faire des choses assez dingues sans bidouilles (sous-entendu bricolage en bash). Je vois la possibilité de faire du DDNS correctement avec une notification de la part du serveur primaire et ods-signer re-signe une zone. Je vois aussi la possibilité de faire une signature offline quasi-parfaite : OpenDNSSEC signe les zones et joue le rôle d'un master furtif. Les zones sont ensuite distribuées par un master et un slave de manière classique.

DNSSEC est mouvant, il faut donc le surveiller. La supervision est donc clairement, une prochaine étape de mon installation. On trouve, sans trop de difficultés, des morceaux de code, par-ci, par-là pour vérifier que les signatures ne sont pas expirées, que les clés correspondent entre une zone parente et une zone enfant, ...

Revoir à la hausse les TTL de mes zones. Je suis parti du TTL appliqué de base par OpenDNSSEC (3600 secondes). Je monterai cette valeur plus tard, pour absorber la charge (si charge il y a). Pour l'instant, en dehors de mon flux RSS, je n'ai pas un afflux massif répété de multiples visiteurs sur mon blog donc mettre un TTL d'une heure ou de 3 heures ou de 5 heures ne changerait rien (sauf à supposer que tous mes visiteurs sont derrière le même résolveur validant) et ferait perdre en souplesse lors d'un changement dans la zone.

Augmenter la sécurité de la partie privée de mes clés (ZSK/KSK). J'ai d'abord pensé à séparer les KSK et les ZSK dans des dépôts différents de mon softhsm. En pratique, OpenDNSSEC permet cela : on peut indiquer, dans chaque politique, où seront stockées les KSK et les ZSK. Néanmoins, avec SoftHSM, tout revient à une base sqlite et donc à une problèmatique de gestion des droits sur un système de fichiers. Si le dépôt contenant les ZSK est compromis, c'est que l'attaquant à la volonté et les droits pour s'y attaquer. Donc, le dépôts des KSK sera trouvé et percer. En pratique, seuls les utilisateurs opendnssec/root ont accès aux dépôts. L'attaquant aura probablement attaqué le compte root avant d'attaquer le compte opendnssec. Et à ce stade là, il y a mieux à faire et il faut considérer que tout, et pas seulement DNSSEC est perdu. J'ai alors pensé à isoler la KSK, plus sensible et plus difficile à remplacer, sur une autre machine. Le problème est que cette clé doit signer le RRset DNSKEY. Les signatures ayant lieu, par défaut, tous les 4 jours, ça nécessite de positionner les clés au bon endroit, au bon moment.

DNSSEC, ce futur

Comme je l'ai écrit en juin 2012, je reste dubitatif sur le déploiement massif de DNSSEC dans les années à venir, encore plus que pour IPv6.

Je vais tenter d'argumenter (peu de noms car je fais des statistiques, pas une distribution de blâmes) :

  • Fin décembre 2012, on parle d'environ 80 TLD signés sur environ 325. Tous les gTLD ne sont pas signés ou n'acceptent pas de délégations signées. C'est encore pire pour les ccTLD. ÉDIT du 12/01/2013 à 12h30 : S. Bortzmeyer me fait remarquer qu'il faudrait nuancer ce point : ".NG [Nigeria] ou .BD [Bangladesh] ne sont pas signés". Fin de l'édit. Il faudrait aussi évaluer les bureaux d'enregistrement pour se faire une idée. ÉDIT du 19/05/2013 à 18h35 : Un lien intéressant pour prendre connaissance des TLD signés : TLD DNSSEC Report. Fin de l'édit.
  • Sous les TLD signés, la quantité de zones signées varie. On parle d'environ 1,3 million de délégations signées sous le ccTLD nl contre environ 20000 sous le ccTLD fr.
  • DNSSEC est une chaîne : un serveur faisant autorité qui diffuse une zone (données utiles + signatures) et des résolveurs qui valident les données obtenues lors de chaque résolution. On a vu, ci-dessus, quelques stats pour les serveurs faisant autorité. Pour le nombre de résolveurs qui font de la validation DNSSEC, on peut regarder (et participer à) l'expérience des Verisign labs. Sur le panel constitué, on constate qu'actuellement, environ 4% des résolveurs font de la validation DNSSEC. D'autres mesures, par pays, sont disponibles ici : Measuring Occurrence of DNSSEC Validation (page 7, figure 8 pour les pressés).
  • Mais DNSSEC, c'est aussi des gens pour le déployer et le maintenir. Je ne suis pas compétent pour estimer les personnes compétentes "en DNSSEC" sur le marché du travail. Par contre, je suis compétent pour "évaluer" l'offre de formation française. Un IUT ne le fait pas étudier en DUT Informatique. Sur 3 parcours (dont 2 orientés réseaux et/ou sécurité) dispensés par des facultés (L1 -> M2), aucun n'inclut DNSSEC. Dans une école d'ingénieur en informatique, pas un mot non plus sur DNSSEC. Je n'ai pas eu de retour sur les formations professionnelles de type L3 Pro/Master Pro. Je sais que certains nuanceront ce point en disant que l'enseignement supérieur donne les bases et que c'est à l'étudiant de faire sa veille technologique. Je n'ai rien contre ce principe, au contraire mais ça va retarder le déploiement d'une évolution comme DNSSEC.

Je vous donne rendez-vous le 01/02/2012, date du rollover ZSK, mais surtout en début d'année prochaine, date du rollover KSK, pour voir si j'ai raté une ou plusieurs étape(s) :P.

Domain Name System : Attaques et sécurisation

Aujourd'hui, je vous propose un long billet sur le DNS, DNSSEC et les attaques que l'on peut mener contre ce protocole et son extension de sécurité.

Ce travail a été réalisé conjointement avec Hamza HMAMA.

Pour lire la version HTML, il suffit de cliquer sur le lien "Lire la suite" (et/ou de poursuivre ci-dessous). Pour ceux qui préfèrent lire un si gros pavé en PDF, c'est par là : Domain Name System : Attaques et sécurisation.

Nous mettons également les sources LaTeX à votre disposition. Nous avons pris le plus de soin possible pour respecter les règles typographiques et l'esprit de LaTeX. Les seules erreurs restantes sont relatives à des underfull/overfull et sont causées par les URLs/Références. Si jamais notre code choque des gourous LaTeX, qu'ils n'hésitent pas à nous corriger. En attendant, ces sources LaTeX peuvent servir de base à d'autres projets. Sources.

Vous pouvez également récupérer les laboratoires Netkit (vous comprendrez la raison de leur existence en lisant le pavé). Ils peuvent servir pour des expériences DNS/DNSSEC. Labo DNS | Labo DNSSEC | Les deux.

Et pour terminer, vous pouvez également récupérer le visuel projeté durant notre soutenance et ses sources. Par contre, ne faites pas attention aux private jokes qui se sont incrustées. Support visuel soutenance | Sources LaTeX support visuel soutenance.

Si vous voulez tout récupérer (labos, sources, pdf) en un seul coup, c'est par là : DNS - Attaques et securisation - Pack all-in-one.

Le tout (les sources LaTeX, les pdf, les images, les labos Netkit, ...) est diffusé sous la licence habituelle de ce blog, à savoir : CC-BY-SA 3.0 France.

Lire la suite >>

Les portails captifs …

Avant de commencer, je tiens à préciser que ce billet sera forcement incomplet. En effet, il existe une multitude de portails captifs en circulation et je n'ai pas la prétention de tous les connaître ni de faire un billet qui les concerne tous. Sans compter les configurations qui varient d'un intégrateur à un autre pour un même produit ... Dans ce billet, je vais vous donner les bases, des pistes de recherche et rien de plus. Ne soyez donc pas déçus.

Ha ! Les portails captifs ! Entre ceux qui les croient indétrônables et les mettent en place sans d'autres mesures complémentaires de sécurité et de bon sens et ceux qui passent leur temps à pester contre eux lorsqu'ils les empêchent de faire des actions légitimes ...

Dans ce billet, je parlerai des portails captifs que l'on trouve sur les accès WiFi "ouverts" comme des portails captifs que l'on trouve sur des connexions filaires. Leurs principes de fonctionnement sont identiques.

Table des matières

Contourner un portail captif

Je sais que les poils de certains lecteurs se sont hérissés à la lecture du mot "contourner". Bah oui, contourner un portail captif c'est forcement dans une mauvaise intention. Bah non ... On peut contourner un portail captif pour des raisons légitimes. Au pif : vous attendez toujours votre identifiant alors que vous avez payé il y a déjà longtemps votre accès à internet ou bien le portail captif déconne une fois de plus et vous avez besoin de vous informer, de vous divertir, de communiquer rapidement, ou que sais-je.

Plusieurs méthodes de contournement (= accéder à internet sans identifiant) sont possibles, parmi lesquelles :

  • Pinger une machine du réseau et dès qu'elle ne répond plus, on peut considérer que son propriétaire l'a éteinte sans pour autant prendre le temps de se déconnecter du portail captif. Il suffit donc de prendre l'adresse IP de cette machine et vous serez authentifié. Sisi, une technique aussi basique fonctionne encore en 2011 (à qui la faute ?) !
  • Pour les portails captifs un peu plus exigeants, il suffira d'usurper l'adresse MAC en même temps que l'adresse IP. Un ping puis un arp -a suffira à découvrir l'association entre adresse IP et adresse MAC sans utiliser d'analyseur réseau (vous savez, des logiciels comme tcpdump ou wireshark). Cette usurpation peut même avoir lieu pendant que la victime est connectée.
  • Normalement, tous les ports sont verrouillés tant que le client ne s'est pas authentifié. Mais parfois, on trouve des portails captifs avec des ports standards ouverts. Il suffit d'héberger, en dehors du réseau, un serveur (SSH/OpenVPN si c'est un port TCP qui est ouvert, OpenVPN pour un port UDP) et de faire du tunneling. Ex. : si vous constatez que le port 53/UDP est ouvert, montez un serveur OpenVPN sur le port 53/UDP. Attention toutefois à ce que le trafic ne soit pas dirigé vers une destination précise (ex. : le port 53/UDP est ouvert, mais le trafic sur ce port est dirigé vers un vrai résolveur DNS qui ne laissera pas passer votre connexion VPN). Ce genre de manipulation peut être démasquée en regardant le TTL des réponses. Si le paquet de retour a un TTL de 1, 2 ou une valeur faible, le serveur est tout près et il ne s'agit probablement pas du serveur que vous cherchez à atteindre.
  • Si aucun port n'est ouvert ou si le trafic est redirigé vers une machine particulière, il vous reste le tunneling plus avancé : TCP over ICMP, TCP over DNS, etc. De mon expérience, si le protocole ICMP est souvent bloqué, le protocole DNS n'est jamais bloqué afin de permettre la redirection du navigateur vers la page d'authentification (votre navigateur cherchera à résoudre google.fr, par exemple. S'il n'y parvient pas, il n'enverra pas de message HTTP et la passerelle ne pourra donc pas le rediriger vers la page d'authentification). Parmi les logiciels utilisables, on peut citer iodine (mon préféré), heyoka, ozymanDNS, dnstunnel ... Je vous propose ce site : Le Blog du grand loup Zeur comme tutoriel. Attention toutefois : ce genre de tunneling n'est pas le plus efficace qui soit en terme de débit (pour avoir essayé ...). De plus, vous pouvez facilement être détecté par une analyse volumétrique du trafic dû au flot inhabituel que vous allez générer sur le service choisi (ex. : DNS). À noter que ce type de tunneling permet aussi de passer les firewalls qui inspectent le contenu des paquets pour vérifier qu'ils sont conformes aux spécifications du protocole auxquels ils prétendent appartenir (ex. : iodine passera très bien un l7-filter configuré pour ne laisser passer que le protocole DNS sur le port UDP/53).
  • À l'aide d'un "complice" ayant une machine authentifiée sur le réseau, il suffit d'utiliser le NAT. Le concept est simple : sa machine est authentifiée et va transmettre vos paquets sous son identité. Vous allez peut-être vous dire qu'il faut donc que votre complice ait une machine avec deux cartes réseau ou qu'il vous faut un commutateur. Que nenni ! Vous vous raccordez sur le même réseau, via les prises murales ou que sais-je. Sur le serveur (la machine de votre "complice"), vous passerez en IP fixe (en utilisant évidemment une IP routable sur le réseau, c'est-à-dire une IP appartenant au même réseau que la passerelle), vous activez l'ip forwarding et le NAT. Pour accroître un peu la sécurité, le serveur ne NATera que les paquets dont l'IP source est celle de votre machine (le client). Ceci est à configurer dans la chaîne FORWARD pour les iptabliens. Cela évite que quelqu'un d'autre, par hasard, comprenne le truc et utilise aussi le serveur sans votre accord. Sur le client, il suffit de passer en IP fixe (= pas de DHCP), de déclarer le serveur comme route par défaut et en avant ! Cette technique peut également vous servir si vous avez deux machines vous appartenant pour un seul identifiant. Si en plus vous avez deux machines pour une seule prise murale, alors là oui, vous aurez besoin d'un commutateur.
  • ...

S'authentifier automatiquement sur un portail captif

Il n'y a rien de plus embêtant que de devoir se connecter à chaque fois au portail captif. Pour éviter ce calvaire, il suffit d'utiliser curl (mon préféré) ou wget pour qu'ils vous connectent automatiquement.

Il suffit d'analyser le code source de la page d'authentification et notamment les paramètres passés via l'URL ou via un formulaire. Méfiez-vous des champs cachés qui doivent tout de même être remplis ! Faites également attention aux pages qui s'enchainent (ex. : une page vous demande votre login, puis une page qui vous demande votre mot de passe, seule la deuxième page est importante). Il suffit ensuite de créer une commande curl qui remplira le formulaire pour vous et vous connectera automatiquement.

Par exemple, si la page d'authentification ressemble à ça (exemple inventé) et se trouve sur le serveur 10.123.36.254) :

<form method="post" action="fin.html">
	<input type="hidden" name="url" value=""/>

	<input type="hidden" name="meth" value="radius"/>
	<input type="hidden" name="action" value="login"/>

	<input type="hidden" name="time" value="360"/>
	<input type="password" name="pswd" size="25"/>

	<input type="hidden" name="uid" value="mon_login"/>
 
	<input type="submit" value="OK">

	<input type="button" value="Annuler" onclick="window.location='/';">
</form>

Alors la commande curl correspondante ressemblera à :

curl -m 5 -d pswd=votre_mdp -d uid=mon_login -d time=360 -d url= -d meth=radius -d action=login "http://10.123.36.254/fin.html" > /dev/null 2>&1

Le paramètre "-d" permet de remplir les champs, "-m 5" demande à curl d'arrêter sa tentative de connexion après 5 secondes.
À vous d'adapter ceci à votre situation. Exemple : si votre portail captif utilise le protocole HTTPS et un certificat non valable, utilisez l'option "-k" de curl pour contourner ce problème.

Il ne vous reste plus qu'à mettre cette commande dans un script shell, de le rendre exécutable et d'utiliser le Network Manager ou bien Wicd (Propriété de la connexion, Scripts, Exécuter un script après connexion) ou le fichier de configuration /etc/network/interfaces et son paramètre "post-up" pour exécuter le script dès la connexion au réseau et ainsi être authentifié automatiquement.

Maintenir l'authentification

Il n'y a rien de plus chiant que d'interrompre une séance productive de travail pour se reconnecter, car le timeout du portail captif a expiré ! Le pire, c'est quand le timeout est démesurément court : 30 minutes, par exemple (sisi, j'ai déjà vu ça !).

La solution est simple, il suffit d'utiliser cron pour lancer le script d'autoconnexion écrit ci-dessus à un intervalle régulier. Pour cela, il suffit d'utiliser la commande :

crontab -e

Et de rajouter une ligne. Par exemple (le script sera lancé toutes les 5 heures) :

* */5 * * * /home/guigui/.autoconnect

Astuces bonus

Il arrive que le portail captif refuse de vous identifier alors que vous avez fourni les bons identifiants. Deux causes peuvent expliquer cela : le fait que le serveur DHCP vous ai attribué une autre IP (lors d'une demande post-reboot ou lors d'un renouvellement) et la cause inconnue.

Dans le premier cas, il suffit de reprendre votre ancienne IP et si vous le souhaitez, de vous déconnecter du portail, de reprendre l'IP qui vous a été attribué en dernier et vous authentifier. Le problème étant de connaître son avant-dernière IP. La commande suivante devrait vous aider à connaître les 2 dernières IPs qui vous ont été attribuées :

sudo grep "dhclient: bound to" /var/log/daemon.log | tail -2

Dans le deuxième cas, j'ai constaté que ce problème se résout en envoyant plusieurs requêtes d'authentification au portail captif. Il suffit donc de répéter la commande d'autoconnexion plusieurs fois. Par exemple :

#!/bin/bash
 
while let 1; 
do 
	curl -d pswd=votre_mdp -d uid=mon_login -d time=360 -d url= -d meth=radius -d action=login "http://10.123.36.254/fin.html";

done

Vous vous apercevrez que le portail va très rapidement (dans les 30 secondes) vous authentifier puis repartir en erreur. Mais ce n’est pas grave : vous serez authentifié ! Contrairement aux apparences, ce script n'est pas une boucle infinie destructrice : un simple CTRL+C suffit pour tout arrêter). Faites quand même attention lorsque vous l'employez afin que l'on ne vous accuse pas de porter atteinte à un système automatisé de traitement de données alors que vous cherchez simplement à rétablir un service légitime. C'est la mode ces temps-ci ...

Se déconnecter automatiquement (ÉDIT du 03/04/2012 à 18h15)

Je vous ai montré, plus haut, l'importance de se déconnecter du portail captif avant de quitter le réseau mais je ne vous ai pas encore montré comment vous déconnecter de manière automatique. Nous allons faire en sorte de vous déconnecter automatiquement durant l'arrêt de votre machine.

La première étape est de trouver l'url et les paramètres de déconnexion. Tout comme pour la connexion automatique, il va falloir y aller au reverse. Tout comme pour la connexion automatique, vous devez obtenir une commande curl.

La deuxième étape est de modifier le fichier /etc/network/interfaces et d'y ajouter une ligne "pre-down". Cela demandera à la commande ifdown de vous déconnecter du portail avant de rendre l'adresse (si vous êtes en DHCP) et d'éteindre la carte réseau. Exemple :

allow-hotplug eth0
iface eth0 inet dhcp
pre-down curl -d pswd=votre_mdp -d uid=mon_login -d time=360 -d url= -d meth=radius -d action=logout "http://10.123.36.254/fin.html"

Attention : pour ceux qui seraient tenter d'utiliser wicd ou le network-manager, ce n'est pas une bonne idée. Ces deux services seront stoppés lors de l'arrêt et ne vous déconnecteront donc pas.

Pour ceux qui veulent se déconnecter sans pour autant éteindre leur machine, il n'y a pas, à ma connaissance, de méthode 100% automatique. Si vous avez modifié le fichier /etc/network/interfaces comme ci-dessus, vous pouvez lancer la commande "sudo ifdown eth0". Vous pouvez également placer votre commande dans wicd (Propriété de la connexion, Scripts, Exécuter un script avant déconnexion) ou le network-manager comme ça, vous aurez juste à cliquer sur "Déconnexion" pour quitter le portail captif et le réseau. Cette deuxième possibilité n'impose pas d'avoir modifié le fichier interfaces.

SSH auto-reconnect

Je vais finir ce billet en vous parlant d'un sujet qui n'a qu'un rapport limité avec le thème de ce billet : la reconnexion automatique à un serveur SSH. Vous savez que, sur les réseaux insécures, je fais transiter tout mon trafic par un tunnel SSH en attendant de me monter un VPN. Le keepalive permet de maintenir la connexion. Mais si jamais un problème survient sur le réseau et provoque une déconnexion, cela peut-être ennuyeux si je ne suis pas là pour relancer la connexion et si je télécharge un fichier, par exemple. C'est dans cette optique que j'ai créé le script suivant.

Il me semble au point, mais il y a sans doute moyen de l'améliorer. Il peut être simplifié, car il est, pour le moment, adapté à mes besoins/désirs :

  • Connexion au serveur SSH via le système clé publique/clé privée.
  • Je vérifie que la connexion est down via une requête DNS effectuée via mon tunnel SSH. Il est nécessaire d'ouvrir deux tunnels distincts (un pour le test, l'autre pour les données), car sinon, en cas de trafic dans le tunnel, la requête DNS de test n'est pas prioritaire et le script diagnostique, à tort, une connexion SSH down. Il est bien sûr possible de contrôler l'état de la connexion SSH via d'autres moyens comme la surveillance des connexions actives avec netstat ou la surveillance des processus actifs avec ps. Mais ces méthodes sont plus lentes pour détecter une connexion down du fait des timers, entre autres.
  • Ce script suppose que le serveur SSH soit configuré dans votre fichier ssh_config.
  • Ce script suppose que le port 53/tcp soit redirigé, grâce à iptables, sur le port 5300. je vous avais déjà expliqué pourquoi.
  • La commande dig doit être disponible sur le système (package dnsutils sous Debian).
  • Le résolveur DNS utilisé doit accepter les requêtes DNS sur le port 53/tcp.
  • Ce script est complexifié par le fait que le serveur SSH procède à un bannissement par IP après deux tentatives de connexion en moins de 10 minutes. C'est pourquoi le script attend 12 minutes en cas de reconnexion ratée et a été adapté à cette contrainte.

Trêve de parole, voici le script que j'utilise :

#!/bin/bash
 

ssh-add /chemin/vers/la/cle/privee
 
while true

do
	dig google.fr @127.0.0.1 +tcp > /dev/null 2>&1
	if [ $? -eq 0 ]; then

		echo "La connexion SSH est OK."
		sleep 60
	else
		echo "La connexion SSH semble KO."

		dig google.fr @127.0.0.1 +tcp > /dev/null 2>&1
		if [ $? -ne 0 ]; then

			echo "La connexion SSH est réellement KO."
			killall ssh > /dev/null 2>&1

 
			echo "Tentative de reconnexion ..."
			ssh -N -D6666 serveur &
			ssh -N -L5300:resolveurDNS:53 serveur &

 
			sleep 60   
			dig google.fr @127.0.0.1 +tcp > /dev/null 2>&1

			if [ $? -ne 0 ]; then
				echo "Tentative de reconnexion KO, attente de 12 minutes."

				killall ssh > /dev/null 2>&1
				sleep 720

			else
				echo "Tentative de reconnexion OK."
				sleep 60
			fi
		else

			echo "Mais la connexion est OK en fait."   
			sleep 60
		fi
	fi
done