Categorie: Administration système

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
 
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
 
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

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
 
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
 
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.

De Debian GNU/Linux Squeeze à Wheezy

Le Debian nouveau est arrivé. Malgré que j'ai attendu 2 semaines pour que d'autres essuient les plâtres, j'ai rencontré des problèmes lors de la mise à jour et je vais vous raconter tout ça.

Table des matières

Pour la procédure de mise à jour en elle-même, c'est du classique : apt-get update && apt-get dist-upgrade, changer le sources.list, apt-get update && apt-get upgrade puis dist-upgrade. apt-get install -f pour terminer l'installation après correction d'un truc qu'à foiré. Pour la version longue, voir, par exemple : De Squeeze à Wheezy… sur le blog de NicoLargo.

Backports

Attention, les backports ne sont plus sur une grappe de serveurs séparée des dépôts principaux. Voir l'annonce officielle.

Dovecot

On passe de la branche 1.x à la branche 2.x (sauf ceux qui utilisaient les backports). Le format du(des) fichier(s) de configuration a changé donc vous allez avoir des erreurs du genre :

doveconf: Warning: Obsolete setting in /etc/dovecot/dovecot.conf:1 ...

Ce n'est pas bloquant mais pour éviter de futurs problèmes, il suffit de suivre la procédure : Upgrading Dovecot v1.2 to v2.0. Faites quand même une copie du fichier de configuration actuel, au cas-où.

php5-suhosin

Si vous utilisez suhosin, vous allez avoir cette erreur durant le redémarrage d'Apache pendant la mise à jour :

PHP Warning:  PHP Startup: Unable to load dynamic library '/usr/lib/php5/20100525/suhosin.so' - /usr/lib/php5/20100525/suhosin.so: cannot open shared object file: No such file or directory in Unknown on line 0

La raison est que suhosin n'est plus disponible dans les dépôts Debian. J'avoue ne pas avoir creusé le sujet concernant la raison (manque de support/réactivité ? plus de mainteneurs Debian ? fonctionnalités de suhosin intégrées nativement dans PHP 5.4 ? Autre ?).

Il suffit donc de désinstaller php5-suhosin :

apt-get remove --purge php5-suhosin

sudo

NicoLargo prévient d'un problème avec sudo. En prévision, j'ai gardé une copie du fichier /etc/sudoers puis j'ai procédé à la mise à jour. Durant celle-ci, on m'a proposé de garder ma version de ce fichier ou de la remplacer. J'ai accepté le remplacement. J'ai ensuite remis mes 2 lignes personnelles (issues de l'ancienne version) dans la nouvelle version du fichier. Je ne déplore aucun dysfonctionnement.

rsyslog

Si vous n'avez pas accepté le remplacement du fichier /etc/logrotate.d/rsyslog (je l'ai refusé car j'avais effectué des modifs), vous aurez le message suivant :

Usage: /etc/init.d/rsyslog {start|stop|rotate|restart|force-reload|status}
invoke-rc.d: initscript rsyslog, action "reload" failed.
error: error running non-shared postrotate script for /var/log/syslog of '/var/log/syslog

La raison est que rsyslog n'a plus de commande « restart » mais « rotate ». Il suffit donc de corriger ça dans la directive « postrotate » de /etc/logrotate.d/rsyslog :

invoke-rc.d rsyslog rotate > /dev/null

Apache (dans un chroot uniquement)

Si vous avez créé un chroot pour votre Apache, vous aurez cette erreur :

Cannot load /lib/libnss_dns.so.2. into server: /lib/libnss_dns.so.: cannot open shared object file: No such file or directory

Cette lib a tout simplement été déplacée vers /lib/x86_64-linux-gnu/libnss_dns.so. Il suffit donc de corriger le fichier de configuration de votre Apache pour en tenir compte.

Mysql (dans un chroot uniquement)

Si vous avez créé un chroot pour votre MySQL, vous aurez cette erreur :

[ERROR] Error message file '/usr/share/mysql/english/errmsg.sys' had only 641 error messages, but it should contain at least 728 error messages.
Check that the above file is the right version for this program!

Il suffit de procéder à une mise à jour d'un fichier du chroot :

cp /usr/share/mysql/english/errmsg.sys /chemin/vers/le/chroot/usr/share/mysql/english/errmsg.sys

Sympa (plus précisément : wwwsympa, l'interface web)

Après la mise à jour, on a une erreur 500 et le log Apache qui dit :

(104)Connection reset by peer: mod_fcgid: error reading data from FastCGI server
Premature end of script headers: wwsympa-wrapper.fcgi

et le log sympa indique :

err [robot ] Config error: wwsympa should run with UID 112 (instead of 33). *** Switching to maintenance mode. ***

J'utilise le wrapper donc je vérifie les permissions de /usr/lib/cgi-bin/sympa/wwsympa-wrapper.fcgi : sympa:sympa/755 ... De tête, là comme ça, je ne vois rien de nouveau. Ha si, il manque le setuid bit. Corrigeons :

chmod u+s /usr/lib/cgi-bin/sympa/wwsympa-wrapper.fcgi

Je redémarre sympa, je kill le processus wwwsympa et je reteste : l'erreur 500 est toujours là, l'erreur dans le log Apache aussi. Par contre, l'erreur a disparu du log de sympa.

Je vérifie les permissions dans /usr/lib/cgi-bin/sympa/ et /etc/sympa/. Dans le premier, tout est en sympa:sympa/755. Dans le second, tout est en sympa:sympa/644. Je ne vois rien à redire.

Quelques recherches sur le web plus tard, j'ai trouvé la solution : sympa: mod_fcgid fails with "Premature end of script headers: wwsympa-wrapper.fcgi".

ÉDIT du 16/06/2013 à 15h30 :
Si un /usr/sbin/apache2ctl graceful tue votre apache (« [apache2] <defunct> ») et que vous avez l'erreur suivante dans le log d'erreur apache (error.log par défaut) :

[error] FastCGI process 13780 still did not exit, terminating forcefully

Alors, il faut activer suexec :

a2enmod suexec && service apache restart

Fin de l'édit

OpenDNSSEC

Je ne m'attendais pas à avoir de problèmes avec ce logiciel qui passe de la version 1.3.2-1 à 1.3.9-5 (numéro de version mineur tout ça tout ça) et pourtant, ils ont changé le schéma de la base de données sqlite utilisée comme le dit le message durant la mise à jour :

ERROR: database version number incompatible with software; require 2, found 1. Please run the migration scripts

Je n'ai trouvé que très peu d'infos, même le changelog, /usr/share/doc/opendnssec/changelog.gz, n'indique pas un changement de la base de données.

Apparemment, le script de migration est : /usr/share/opendnssec/migrate_keyshare_sqlite3.pl. Évidemment, ce script n'a pas fonctionné chez moi sinon c'est trop facile.

J'ai remarqué que la commande « ods-ksmutil key list » ne m'affiche plus les clés/les zones et qu'un message apparaît dans le log opendnssec :

Key (xxx) has gone straight to active use without a prepublished phase

Le problème est sérieux et je n'ai trouvé aucune solution viable : toutes mes tentatives, forcer la signature des zones, nettoyer les états, ..., ont échouées.

Partant du principe que tout était perdu (OpenDNSSEC n'a plus l'état des zones, les clés associées, rien ...), j'ai décidé de faire une sauvegarde des fichiers de configuration actuels, de réinstaller complètement OpenDNSSEC et de refaire la configuration à partir des fichiers sauvegardés. Solution radicale (mes zones sont temporairement invalides) mais efficace.

ÉDIT du 24/05/2013 à 19h30 :

LXC et le réseau

Suite à la mise à jour, il m'étais parfois impossible de me connecter en SSH avec l'un de mes conteneurs LXC. Une connexion SSH vers l'hôte fonctionnait parfaitement et le conteneur était démarré. J'allais commencer à chercher une solution à ce problème quand j'ai reçu un mail du robot de sécurité d'OVH qui me disait que, depuis quelques temps, ma machine émettait de la merde en ARP. Il s'avére que ce « quelques temps » était en fait depuis le moment où j'ai redémarré mon serveur (nouveau noyau tout ça tout ça) après la mise à jour. Étrange coïncidence, non ?

Je regarde les IPs mentionnées dans les requêtes ARP ... Tiens, le premier octet est toujours le même que l'IP du conteneur LXC : 87. ... À qui sont allouées ces IPs ? Des whois plus tard, je trouve de tout : OVH, Bouygues et d'autres ... Bizarre ... Mon conteneur se croirait-il sur un réseau /8 alors que son IP est explicitement configurée comme étant un /32 (config IP FailOver chez OVH) ? Vérification avec ifconfig : oui. Et c'est ainsi pour tous mes conteneurs LXC. Pourtant, les fichiers /etc/network/interfaces sont corrects et n'ont pas changé d'un octet ...

En fait, dans le fichier de config de chacun de mes conteneurs, j'avais une ligne comme ça :

lxc.network.ipv4 = 87.98.183.12

Elle n'a jamais posé problème avant, le fichier /etc/network/interfaces devait prendre le relai lors du démarrage du conteneur, je ne sais pas ... LXC doit désormais intervenir plus tard dans le processus de boot et doit croire qu'il s'agit d'une IP du réseau 87/8. Préciser un /32 dans ce fichier ne change rien car les up/down script prévus dans /etc/network/interfaces ne seront pas exécutés donc la gateway ne sera pas précisée donc vous perdrez l'accès à votre conteneur depuis l'extérieur.

La solution est bêtement de commenter la ligne ci-dessus. On ne touche pas aux autres lignes concernant le réseau (type, flags, link, ...), hein ! Un redémarrage des LXC plus tard, le problème est résolu.
Fin de l'édit

Forcer l’IPv4 avec apt-get

Sur certaines de mes machines, j'ai de la connectivité IPv6, que ça soit en natif ou via un tunnel 6in4. Parfois, des hôtes ne sont pas atteignables en IPv6. Et quant ça arrive avec les serveurs de dépôt des paquets logiciels, comment on fait ?

Cela m'est arrivé avec backports.debian.org dont une des IPv6s du pool n'est pas atteignable depuis un bloc OVH mais ne pose pas de problème à travers un tunnel 6in4 HE. Cela arrive en ce moment avec mirror.ovh.net et, pour une fois, ça n'arrive pas qu'à moi, j'ai vu l'info tourner hier. Par contre, rien ne semble avoir été remonté à OVH ...

Je suis sûr qu'il y a plusieurs solutions pour forcer l'usage d'IPv4 avec apt-get (apt.conf, résolution, gai.conf ...).

Personnellement, j'ai choisi d'utiliser le processus de résolution du nom pour résoudre ce problème. En gros, je rajoute les lignes suivantes dans mon /etc/hosts :

#Modification temporaire (17/03/2013)
91.121.125.139 mirror.ovh.net
91.121.124.139 debian.mirrors.ovh.net

Et ça marche.

Dès la fin de l'apt-get update, je commente les lignes ajoutées afin de ne pas avoir une rustine qui me créera des problèmes dans le futur.

Quand root n’a plus l’autocomplétion

Depuis peu, sur mes machines, l'utilisateur root, via su/sudo (mais le problème doit être le même en login direct), l'autocomplétion, même des commandes les plus basiques, ne fonctionne plus.

J'ai résolu le problème en copiant le petit morceau suivant, que l'on trouve dans le .bashrc de tout utilisateur, dans le .bashrc de root (/root/.bashrc) :

# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
if ! shopt -oq posix; then
  if [ -f /usr/share/bash-completion/bash_completion ]; then
    . /usr/share/bash-completion/bash_completion
  elif [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
  fi
fi

L'autocomplétion juste re-fonctionnera dès le prochain login.

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 :D . Oui, un rien m'amuse. :P

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 comptabilité 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 --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 .