SwordArMor

Gestion automatique de DNSSEC avec knot

Depuis quelques temps, je me suis mis au DNSSEC. Comme je suis feignant, je n’ai pas envie de re-signer ma zone à la main tous les jours :) Comme j’ai vu que knot savait gérer la signature DNSSEC comme un grand, je me suis dit que ça serait une bonne occasion de l’essayer.

Installation

Je suis sous FreeBSD et j’ai choisi d’utiliser les ports.


root@kaiminus:~ # cd /usr/ports/dns/knot2/
root@kaiminus:/usr/ports/dns/knot2 # make install clean
root@kaiminus:~ # echo 'knot_enable="YES"' >> /etc/rc.conf
root@kaiminus:~ # echo 'knot_config="/usr/local/etc/knot/knot.conf"' >> \
	/etc/rc.conf

Configuration de base

La configuration de knot se base sur du YAML. Il faut commencer par définir les options du serveur en lui-même, puis les serveurs avec qui le votre va parler (pour la réplication de zone), mettre en place des acl et enfin définir les zone.


# configuration de base
server:
    # Listen on all configured IPv4 interfaces.
    listen: 0.0.0.0@53
    # Listen on all configured IPv6 interfaces.
    listen: ::@53
    # User for running the server.
    user: knot:knot

log:
    # Log info and more serious events to syslog.
  - target: /var/log/knot.log
    any: info

# Si on veut utiliser une clé pour s’authentifier auprès d’autres serveurs
key:
  - id: blah
    algorithm: hmac-sha256
    secret: "blah2="

# On précise les autres serveurs
remote:
  - id: ttn
    address: 2a01:6600:8081:c600::1

  - id: bulbizarre
    address: 2a01:240:fe00:82af:764f:b47e:d131:85e4
    key: blah

# Les ACL (tout le monde n’a pas le droit de faire le même chose)
  - id: acl_allow_nokey
    address: [2a01:6600:8081:c600::1]
    action: [transfer, notify]

  - id: acl_key
    address: 2a01:240:fe00:82af:764f:b47e:d131:85e4
    action: transfer
    key: blah

# Slave zones
# reverse jaguar (florizarre)
  - domain: 4.0.8.0.0.4.1.8.8.5.0.0.a.2.ip6.arpa
    master: bulbizarre
    acl: acl_key

# Master zones
zone:
  - domain: swordarmor.fr
    file: "/usr/local/etc/knot/swordarmor.fr.zone"
    acl: [acl_allow_nokey, acl_key]
    notify: [ttn, bulbizarre]

Signature automatique DNSSEC

Pour ma part, j’ai choisi la gestion automatique des clés. La façon de faire varie légèrement en fonction de la version (post ou pre 2.3). Je ne sais pas exactement si c’est à cette version que ça a changé, mais en tout cas c’est là que je m’en suis rendu compte.

Avant la version 2.3

Il nous faut d’abord dire à knot où seront stockées les clés.


template:
  - id: default
    storage: "/usr/local/etc/knot/var"
    kasp-db: kasp
Si votre knot ne tourne pas en root (ce qui est une bonne idée), ce répertoire doit appartenir à knot (ou à l’utilisateur qui le fait tourner si ce n’est pas knot).

# mkdir -p /usr/local/etc/knot/var/kasp
# chown -R knot /usr/local/etc/knot/var
# cd /usr/local/etc/knot/var/kasp
# keymgr init
# keymgr policy add rsa algorithm RSASHA256 zsk-size 1024 ksk-size 2048
# keymgr zone add swordarmor.fr policy rsa
Ensuite, on rajoute dnssec-signing: on au domaine swordarmor.fr et knot se débrouille tout seul :)

À partir de la 2.3

À partir de cette version nous n’avons même plus besoin de nous soucier de la gestion de la base de clés ; knot gère tout. Il suffit juste d’ajouter dnssec-signing: on et dnssec-policy: default à notre configuration de zone et le tour est joué.
Ça doit donner un truc qui ressemble à ça au final :


zone:
  - domain: log.bzh
    file: "log.bzh.zone"
    acl: acl_slaves
    notify: [togepi, bihen]
    dnssec-signing: on
    dnssec-policy: default

Publication de la clé DNSSEC dans la zone parente

Il existe deux types de bureaux d’enregistrement : ceux qui veulent un DS et ceux qui veulent une clé DNSKEY complète.

Publication de la clé DNSKEY

Ancienne méthode

Si l’on a précisé une kasp dans la conf, les clés se trouveront dedans. Si ce n’et pas le cas, ce sera dans storage. Comme à l’époque où je me suis mis à knot il fallait un kasp, mes clés sont dans /usr/local/etc/knot/var/kasp/. Dedans nous trouvons un fichier avec un nom qui ressemble à zone_swordarmor.fr.json. Nous l’ouvrons (par exemple avec cat) et dans ce JSON, nous avons une section "ksk": true. On y trouve aussi l’entrée algorithm et public_key. Il faut prendre ces deux valeurs et les reporter dans l’interface de son bureau d’enregistrement.
Vous devez donc avoir un formulaire qui ressemble à la capture si dessous. Il faut bien faire attention à préciser le bon algorithme et à publier la KSK.

Nouvelle méthode

Depuis knot 2.4, les clés ne sont plus stockées dans un JSON, il n’est donc plus possible d’utiliser un bête cat. Par contre, il est maintenant possible d’interroger knot en local pour avoir la clé actuellement servie :

root@argall:~# knotc zone-read swordarmor.fr @ DNSKEY
[swordarmor.fr.] swordarmor.fr. 10800 DNSKEY 256 3 13 iy2V9pyqoImi7WmhTbkjntLt8FyK4/XxPrN9kDsweXjnKtl8DvFStjKwgMnJNKpkr3U2xsWx70o2xbLHLdgUbg==
[swordarmor.fr.] swordarmor.fr. 10800 DNSKEY 257 3 13 Uk3Vek1FDqzgQlI39NZcPKfpVex8unrHPmBLURrZCSjyje2o2UQel2FBGl2xHMl7qtcijj0x/g1n6tEmpio/jQ==

Pour lire cet enregistrement, il faut se référer aux RFC 4034, section 2 et 6605. L’enregistrement qui nous intéresse est le second, avec le tag 257, il correspond à notre KSK.
Ici nous avons donc affaire à une clé condensée avec ECDSAP256SHA256 (algorithme 13) et dont le résultat est « Uk3Vek1FDqzgQlI39NZcPKfpVex8unrHPmBLURrZCSjyje2o2UQel2FBGl2xHMl7qtcijj0x/g1n6tEmpio/jQ== ».

Exemple avec gandi

Ensuite, nous nous rendons sur la page « Gérer le DNSSEC » de son domaine et nous remplissons les champs en fonction du résultat des champs précédents.
J’ai délibérément fait le choix de ne pas publier la ZSK, car ce n’est pas nécessaire pour que le DNSSEC fonctionne correctement et qu’elle change régulièrement.

(cette capture étant plus vielle que la mise a jour de l’article, elle contient une clé différente de l’extraction avec la nouvelle méthode)

Publication d’un DS

Knot ne gère pas nativement l’export du DS, mais nous pouvons utiliser la commande drill du paquet ldnsutils à la place.

argall ~ # apt-get install ldnsutils 
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following NEW packages will be installed:
  ldnsutils
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 142 kB of archives.
After this operation, 561 kB of additional disk space will be used.
Get:1 http://mirrors.online.net/debian/ jessie/main ldnsutils amd64 1.6.17-5+b1 [142 kB]
Fetched 142 kB in 0s (2,857 kB/s) 
Selecting previously unselected package ldnsutils.
(Reading database ... 37564 files and directories currently installed.)
Preparing to unpack .../ldnsutils_1.6.17-5+b1_amd64.deb ...
Unpacking ldnsutils (1.6.17-5+b1) ...
Processing triggers for man-db (2.7.0.2-5) ...
Setting up ldnsutils (1.6.17-5+b1) ...
argall ~ # drill @argall.gozmail.bzh -s gozmail.net DNSKEY
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 15512
;; flags: qr aa rd ; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 
;; QUESTION SECTION:
;; gozmail.net.	IN	DNSKEY

;; ANSWER SECTION:
gozmail.net.	10800	IN	DNSKEY	256 3 13 mIXljDQFdQIqixY5pmsgzCewpK+KbQPS1lqFV5iGzxXez8mTBT30uquL7Y/K2ZWGk3vMkGc/MN5Kp0hLVFS5Pg== ;{id = 34200 (zsk), size = 256b}
gozmail.net.	10800	IN	DNSKEY	257 3 13 xi9r6PYVykH5Xxk7iJKL9LfS6B/ka04+aJW5E8O2At+sdfwhH2wTvX+pPUYBjTNI54iRFofJFOajrIPrdzcV2g== ;{id = 28666 (ksk), size = 256b}

;; AUTHORITY SECTION:

;; ADDITIONAL SECTION:

;; Query time: 0 msec
;; SERVER: 127.0.0.1
;; WHEN: Thu Nov 17 15:28:58 2016
;; MSG SIZE  rcvd: 189
;
; equivalent DS records for key 34200:
; sha1: gozmail.net.	10800	IN	DS	34200 13 1 01349b4563e4f7ab36b2b4b54f98c300d6a8da88
; sha256: gozmail.net.	10800	IN	DS	34200 13 2 a8a6af6404675d34bd933fbdb8539118d021bcd56876dcccaebeb777c21eb28b
;
; equivalent DS records for key 28666:
; sha1: gozmail.net.	10800	IN	DS	28666 13 1 888e055ab8523ac1c847fd12d8d03ec88586c3b7
; sha256: gozmail.net.	10800	IN	DS	28666 13 2 a211a1a59a553710f127cf77b1a09af8eaf0249b505337bd8e3dc615f649014d
Ici la clé qui nous intéresse est la 28666 puisque c’est elle la KSK (cf. les commentaires des enregistrements DNSKEY).
Dans mon exemple, argall.gozmail.bzh est le master et gozmail.net est le domaine dont on veut avoir le DS.

Je vous déconseille très fortement d’utiliser le DS sha1. Cet algorithme de condensat est en effet considéré comme ayant des risques de collision. Par contre le sha256 n’a pas (encore) ce souci.

Mise à jour d’une zone

La méthode normale pour mettre à jour une zone avec knot est l’utilisation de la commande knotc avec les actions zone-begin, zone-set, zone-commit, etc. C’est très pratique dans un script, ça évite du sed, grep, awk, mais je trouve ça lourd de manière interactive.
Il n’est pas possible non plus d’éditer directement le fichier de zone renseigné dans la conf car knot réorganise le fichier à sa façon et on ne retrouve plus ses petits.

Je filoute donc un peu en ayant un fichier de zone non signé (en .zone.nodnssec) dont je fais ce que je veux et un fichier de zone qui est dans la conf de knot (en .zone) et qui est donc signé régulièrement.
Ce que je fais, c’est que j’édite la zone non-signée, je mets ce que je veux dedans, je la copie par dessus la zone signée, puis je demande à knot de recharger la zone. Il va alors la modifier comme il veut pour ajouter les signatures DNSSEC et l’avoir à sa convenance, mais j’aurais toujours ma zone non-signée à côté, avec mon organisation à moi. Par contre, il ne faut pas oublie d’incrémenter le serial, de préférence sous le format YYYYMMDDNN car les renouvellements de clé DNSSEC font incrémenter le sérial même si on ne modifie pas la zone à la main.
Par exemple, si je veux modifier la zone log.bzh stockée dans /var/lib/knot/log.bzh.zone ça done un truc comme ça :

master # vim /var/lib/knot/log.bzh.zone.nodnssec
Là je fais les modifs que je veux.
master # cp /var/lib/knot/log.bzh.zone.nodnssec /var/lib/knot/log.bzh.zone
master # knotc zone-reload log.bzh
OK
Et hop, j’ai ma zone modifiée, re-signée, et distribuée sur tous les masters.

Premières impressions sur knot

C’est la première fois que j’utilise knot, et je dois dire que je ne suis pas déçu. Le seul souci que j’ai eu était sur le choix de l’adresse IP source lors des réponses, et ce bug a été corrigé à peu près une semaine après l’avoir reporté.
De plus, leur doc est vraiment très bien faite, et ça c’est pas négligeable.