Il y a deux ans de cela j'avais tenté d'expliquer comment Signer une zone DNS sous Bind9.

Je soulevais le point suivant dans ma conclusion :

Comment gérer convenablement cette pléthore de clé ?

Car en effet, la gestion au quotidien de ces clés et des rotations nécessaires sont une procédure lourde. Il y a un risque non négligeable d'oublier de renouveler une clé et d'avoir des erreur comme celle ci

<debian@daedelys.org>: Host or domain name not found. Name service error for
    name=daedelys.org type=MX: Host not found, try again

De plus comme les enregistrements DNSSEC commencent seulement à être vérifiés, cette erreur peut être difficile à corriger.

Des sites web permettent de visualiser l'état d'une zone :

Après avoir remarqué que ces sites vous crachaient des insanités diverses et variées sur le status DNSSEC de votre zone et comme vous êtes un fidèle lecteur du blog de Stéphane Bortzmeyer vous avez décider d'utiliser OpenDNSSEC pour occuper vos nuits à faire autre chose que de la cryptographie.

OpenDNSSEC va donc s'occuper de gérer les signatures et les clés (KSK et ZSK) nécessaire pour un bon état DNSSEC de la zone. Il va également s'occuper des rotations nécessaires pour les différents types de clés.

Comme tout bon logiciel de cryptographie, il est paranoïaque et donc requiert un stockage sécurisé pour ces clés de type Hardware Security Module accessible par une interface PKCS#11 .

Pour les rares personnes ne disposant pas d'une HSM dans leur cuisine, le projet OpenDNSSEC propose un Software Security Module équivalent donc d'une HSM sous forme logicielle : SoftHSM. L'utilisation d'un logiciel rend la sécurité des clés plus faible comme il y a toute la pile du système d'exploitation ( donc ses potentiels bogues et failles de sécurité ) en plus de la partie matériel ( et de ses potentiels bogues et failles de sécurité).

Configuration de SoftHSM

J'ai installé SoftHSM par les paquets d'une Debian 7 «Wheezy». Cette documentation est donc valable pour la version 1.3.31 :

softhsm --version
1.3.3

Tout d'abord nous allons renseigner le fichier /etc/softhsm/softhsm.conf avec le chemin de la HSM :

# SoftHSM configuration file
#
# Format:
# <slot id>:<path to the token database >
#
# The given paths are just an indication to SoftHSM on where it should 
# store the information for each token.

0:/var/lib/softhsm/OpenDNSSEC.db

Le slot id 0 va être utilisée pour référencer dans les commandes la HSM

Le chemin /var/lib/softhsm/OpenDNSSEC.db indique le chemin de la base de Donnée SQLite utilisée.

Une fois ce fichier renseigné, nous allons faire l'initialisation de la base de donnée proprement dite :

softhsm --init-token --slot 0 --label "OpenDNSSEC"
The SO PIN must have a length between 4 and 255 characters.
Enter SO PIN: 
The user PIN must have a length between 4 and 255 characters.
Enter user PIN: 
The token has been initialized.

Le SO PIN permet de réinitialiser la HSM.

Le user PIN est utilisé lui pour les opérations courantes, Il sera utilisé par OpenDNSSEC.

Le label est la référence qui sera utilisée par openDNSSEC pour choisir la HSM.

Configuration de OpenDNSSEC

J'ai installé OpenDNSSEC par les paquets d'une Debian 7 «Wheezy». Cette documentation est donc valable pour la version 1.3.9

ods-ksmutil --version
opendnssec version 1.3.9

Fichier de configuration : conf.xml

Nous allons d'abord renseigner le fichier /etc/opendnssec/conf.xml pour indiquer que nous utilisons un référentiel SoftHSM :

<Configuration>

    <RepositoryList>


        <Repository name="SoftHSM">
            <Module>/usr/lib/softhsm/libsofthsm.so</Module>
            <TokenLabel>label</TokenLabel>
            <PIN>user PIN</PIN>
            <SkipPublicKey/>
        </Repository>

L'attribut name est le nom du référentiel ici SoftHSM.

La balise TokenLabel est le label donné lors de l'initialisation de la base de donnée SoftHSM.

La balise PIN est le user PIN à connaitre pour utiliser la HSM.

Attention :, il faut également que l'utilisateur utilisé par OpenDNSSEC (sous debian opendnssec) ait les droits unix d'accés au fichier de base de donnée, cela se fait par exemple en octroyant le groupe softhsm à l'utilisateur opendnssec et en donnant les droits en écriture au groupe sur le fichier :

ls -la /var/lib/softhsm/
total 40
drwxrws---  2 root softhsm  4096 Dec 10 08:45 .
drwxr-xr-x 45 root root     4096 Oct  1 20:52 ..
-rw-r--r--  1 root softhsm 30720 Dec 10 08:45 OpenDNSSEC.db
adduser opendnssec softhsm
Adding user `opendnssec' to group `softhsm' ...
Adding user opendnssec to group softhsm
Done.
chmod g+w /var/lib/softhsm/OpenDNSSEC.db

Fichier de configuration : kasp.xml

Après nous allons renseigner le fichier /etc/opendnssec/kasp.xml. Celui ci indique la façon de signer les zones. Je vous incite fortement à lire la documentation relative à ce fichier.

Les paramètres relatifs à la politique sont dans une balise Policy :

<KASP>

    <Policy name="default">
        <Description>Policy Daedelys</Description>

L'attribut name est le nom de la politique de signature.

Suivent ensuite les paramètres de gestions des signatures :

        <Signatures>
            <Resign>PT2H</Resign>
            <Refresh>P3D</Refresh>
            <Validity>
                <Default>P7D</Default>
                <Denial>P7D</Denial>
            </Validity>
            <Jitter>PT12H</Jitter>
            <InceptionOffset>PT3600S</InceptionOffset>
        </Signatures>

La documentation se trouve ici Elements of the kasp.xml file : Signatures

Après se trouve le paramétrage de la preuve authentifiée de non-existence soit par la méthode NSEC3 ou la méthode NSEC

 <Denial>
            <NSEC3>
                <!-- <OptOut/> -->
                <Resalt>P100D</Resalt>
                <Hash>
                    <Algorithm>1</Algorithm>
                    <Iterations>5</Iterations>
                    <Salt length="8"/>
                </Hash>
            </NSEC3>
        </Denial>

De même la documentation se trouve ici Elements of the kasp.xml file : Authenticated Denial of Existence

Nous pouvons ensuite paramétrer les clés, dans la balise Keys

<Keys>
            <!-- Parameters for both KSK and ZSK -->
            <TTL>PT3600S</TTL>
            <RetireSafety>PT3600S</RetireSafety>
            <PublishSafety>PT3600S</PublishSafety>
            <Purge>P14D</Purge>

        <!-- Parameters for KSK only -->
            <KSK>
                     <Algorithm length="2048">8</Algorithm>
                <Lifetime>P1Y</Lifetime>
                <Repository>SoftHSM</Repository>
            </KSK>

La balise KSK indique que les paramètres s'appliquent au clé de type KSK.

La balise Repository indique le nom du référentiel à utiliser défini dans le fichier conf.xml pour gérer ces clés, ici SoftHSM.

La balise Lifetime indique la durée de vie de la clé, ici 1 an (1 Year) sous le format ISO 8601.

La balise Algorithm indique le numéro d'algorithme cryptographique à utiliser (le numéro est normalisé par l'IANA).

Attention : Nous devrons utiliser ce numéro lorsque nous importerons les clés dans le référentiel. Dans le cadre d'une migration, il faut s'assurer que l'algorithme utilisé dans l'ancien système ( et fourni pour les KSK au domaine parent) et le même que celui configuré dans OpenDNSSEC.

Sinon il va avoir une incohérence sur les signatures cryptographiques lors du renouvellement des clés. OpenDNSSEC se plaindra alors avec un message comme-celui ci :

daedelys.org : RRSIGS should include algorithm RSASHA256 for daedelys.org, DNSKEY, have : RSASHA1

Dans ce cas le domaine daedelys.org avait des anciennes clés avec l'algorithme RSASHA1 (5) et openDNSSEC était configuré avec l'algorithme RSASHA256 (8). Dans le cadre d'une migration depuis Bind, il suffit de regarder le fichier .private de la clé que l'on voudra migrer :

grep Algorithm Kdaedelys.org.+005+51516.private
Algorithm: 5 (RSASHA1)

Attention :De même tout les algorithmes ne sont pas supportés avec la preuve authentifiée de non-existence.

ods-kaspcheck
/etc/opendnssec/conf.xml validates
/etc/opendnssec/kasp.xml validates
ERROR: In policy default, incompatible algorithm (5) used for KSK NSEC3 in /etc/opendnssec/kasp.xml - should be 6,7,8 or 10

Dans ce cas, il faut soit :

  • garder le même numéro d'algorithme utiliser NSEC comme preuve authentifiée de non-existence ( mais cela permet d'énumérer le contenu d'une zone)
  • Soit remplacer le numéro d'algorithme par son alias NSEC3 :
    • DSA/SHA(3) est remplacé par DSA-NSEC3-SHA1 (6)
    • RSA/SHA-1(5) est remplacé par RSASHA1-NSEC3-SHA1 (7)

Ce changement d'alias permet aux résolveurs implémentant DNSSEC mais pas NSEC3 de ne pas valider ces enregistrements en utilisant le mécanisme décrit dans le rfc4035 : Authenticating Referrals

Il y a logiquement le même type de configuration pour les clés de type ZSK :

 <!-- Parameters for ZSK only -->
            <ZSK>
                    <Algorithm length="1024">8</Algorithm>

                <Lifetime>P30D</Lifetime>
                <Repository>SoftHSM</Repository>
            </ZSK>

Des détails techniques sur la zone sont inscrits à l'intérieur de la balise Zone

   <Zone>
            <PropagationDelay>PT43200S</PropagationDelay>
            <SOA>
                <TTL>PT3600S</TTL>
                <Minimum>PT3600S</Minimum>
                <Serial>datecounter</Serial>
            </SOA>
        </Zone>

La balise PropagationDelay utilise le délai de propagation de la zone sur tout les serveurs DNS secondaires, ici 12h (43200 secondes).

La sous-balise TTL de la balise SOA indique le Time To Live de la ressource SOA (ie la durée maximum de vie en cache DNS).

La sous-balise Minimum de la balise SOA indique la durée maximum de vie en cache DNS de la non existence d'un enregistrement.

La sous-balise Serial de la balise SOA indique comment incrementer le numéro de série de la Zone. Ici datecounter indique d'utiliser un format de type date ie YYYYMMDDXX avec :

  • YYYY l'année sur quatre chiffres
  • MM le mois sur deux chiffres
  • DD le jour sur deux chiffres
  • XX un compteur sur deux chiffre permettant plusieurs numéro de série par jour.

Attention : il faut s'assurer que le choix de la méthode soit cohérente avec celui du fichier de zone non signé :

il faut que le numéro de série donnée par la méthode soit supérieure à celui du fichier de zone non signé, dans le cas contraire OpenDNSSEC donnera l'avertissement suivant :

daedelys.org : SOA differs : from 2013072334 to 2013103002

Fichier de configuration : zonelist.xml

Finalement le fichier /etc/opendnssec/zonelist.xml définit les zones à gérer :

<ZoneList>
    <Zone name="daedelys.org">
        <Policy>default</Policy>
        <SignerConfiguration>/var/lib/opendnssec/signconf/daedelys.org.xml</SignerConfiguration>
       <Adapters>
           <Input>
              <File>/var/lib/opendnssec/unsigned/db.world.daedelys.org</File>
           </Input>
           <Output>
              <File>/var/lib/opendnssec/signed/db.world.daedelys.org.signed</File>
           </Output>
       </Adapters>
   </Zone>
</ZoneList>

L'attribut name de la balise Zone identifie la zone.

La balise Policy indique la nom de la politique de signature à utiliser pour cette zone.

La balise SignerConfiguration indique un fichier de travail entre le processus Enforcer et le processus Signer.

La sous-balise File de la balise Input indique le fichier de zone non-signée.

La sous-balise File de la balise output indique le fichier de zone signée.

Initialisation d'OpenDNSSEC

Tout d'avord nous allons vérifier qu'il n'y a pas d'erreur de configuration flagrante en utilisant ods-kaspcheck:

ods-kaspcheck                  
/etc/opendnssec/conf.xml validates
/etc/opendnssec/kasp.xml validates

Attention : pour que ods-kaspcheck fonctionne il faut que les utilitaires suivants soient installés :

  • xmllint (paquet debian libxml2-utils)
  • xsltproc (paquet debian xsltproc)

Si l'un des deux utilitaires manque, le résultat est trompeur

ods-kaspcheck
echo  $?
0

Nous allons pouvoir maintenant mettre initialiser OpenDNSSEC :

ods-ksmutil setup
*WARNING* This will erase all data in the database; are you sure? [y/N] y
fixing permissions on file /var/lib/opendnssec/db/kasp.db
zonelist filename set to /etc/opendnssec/zonelist.xml.
kasp filename set to /etc/opendnssec/kasp.xml.
Repository SoftHSM found
No Maximum Capacity set.
RequireBackup NOT set; please make sure that you know the potential problems of using keys which are not recoverable
Policy default found
Info: converting P1Y to seconds; M interpreted as 31 days, Y interpreted as 365 days
Zone daedelys.org found
Policy set to default.
Added zone daedelys.org to database

Importer les clés depuis BIND

Si aucune clés n'est à importé dans OpenDNSSEC, nous pouvons démarrer les services tout de suite.

Exporter les clés

Nous allons pour cela utiliser softhsm-keyconv

softhsm-keyconv --topkcs8 --in Kdaedelys.org.+005+27862.private --out 27862.pem
Error: Unrecognized input line 14
Error: --> Inactive: 20140913153703
Error: Unrecognized input line 15
Error: --> Delete: 20150312153703
The key has been written to 27862.pem

L'option --topkcs8 permet d'indiquer une conversion du format de Bind vers le format PKCS#8

L'option --in précise le fichier d'entrée, donc le fichier .private de la clé (généralement sous la forme K<domaine><Algorithme>+<Empreinte>+.private)

L'option --out précise le fichier de sortie, ici nous avons repris l'empreinte de la clé comme nom soit 27862.pem

Importer les clés dans SoftHSM

Pour importer les clés dans SoftHSM, nous allons utiliser softhsm :

softhsm --import 27862.pem --slot 0 --pin <user PIN> --label 'Bind Key 27862' --id B1
The key pair has been imported to the token in slot 0.

L'option --import permet d'indiquer que nous voulons importer le fichier exporté précédemment.

L'option --slot indique le slot id de la HSM à utiliser pour stocker la clé.

L'option --pin indique l'user PIN à utiliser pour permettre l'accés à cette HSM

L'option --label est un intitulé arbitraire pour cette clé, ici nous indiquons que la clé vient de Bind soit Bind Key 27862

L'option --id indique l'identifiant de la clé dans la base de donnée HSM. Il doit être unique. Nous prenons ici arbitrairement la valeur B1

Importer les clés dans OpenDNSSEC

Pour importer les clés dans OpenDNSSEC, nous allons utiliser ods-ksmutil :

ods-ksmutil key import --cka_id B1 --repository SoftHSM --zone daedelys.org --bits 2048 --algorithm 8  --keystate active --keytype KSK --time 20130720153703

key import permet de dire que nous voulons importer une clé pour l'utiliser dans OpenDNSSEC

--repository indique le nom du référentiel à utiliser, ici SoftHSM

--cka_id permet de préciser l'identifiant de la clé à utiliser, ici B1

--zone est l'identifiant de la zone pour laquelle nous allons utiliser cette clé, ici daedelys.org

--bits est la taille de la clé, ici 2048

Elle peut être retrouvée en utilisant la commande ods-hsmutil list <référentiel>

ods-hsmutil list SoftHSM
Listing keys in repository: SoftHSM
1 keys found.

Repository            ID                                Type      
----------            --                                ----      
SoftHSM               b1                                RSA/2048  

Ici, nous avons une taille de clé de 2048

--keytype indique quel est l'usage de cette clé, ici une KSK

--algorithm indique l'algorithme avec lequel cette clé est utilisé. Vérifiez bien que la valeur correspond à la valeur indiquée dans le fichier kasp.xml pour le type de clé (ici KSK), ici c'est l'algorithme 8 soit RSASHA256

--keystate indique le statut de la clé, ici nous importons la clé déjà utilisé pour signer avec KSK, le statut est donc active

--time indique la date de la derniére transition de la clé, ici la clé a été passé au statut active le 20 juillet 2013 à 15:37:03

cette valeur peut être retrouvé en regardant le fichier .private crée par bind. Pour le statut active, cela correspond à la ligne Activate :

grep Activate  Kdaedelys.org.+005+27862.private 
Activate: 20130720153703

Pour vérifier la bonne importation, nous pouvons utiliser la commande ods-ksmutil key list avec l'option --verbose

ods-ksmutil key list --verbose
SQLite database set to: /var/lib/opendnssec/db/kasp.db
Keys:
Zone:                           Keytype:      State:    Date of next transition:  CKA_ID:                           Repository:                       Keytag:
daedelys.org                    KSK           active    2014-07-20 15:37:03       B1                                SoftHSM                           27865
daedelys.org                    ZSK           active    2014-01-17 19:46:05       def545201fa332afdd2229a800127b7a  SoftHSM                           47301

Nous pouvons faire de même pour une clé de type ZSK :

ods-ksmutil key import --cka_id B2 --repository SoftHSM --zone daedelys.org --bits 1024 --algorithm 8  --keystate active --keytype ZSK --time 20131030192951

Démarrer le service OpenDNSSEC

Lorsque nous avons tout configuré, nous pouvons lancer les services OpenDNSSEC :

  • Le service de gestion des clés
    service opendnssec-enforcer start
    OpenDNSSEC ods-enforcerd started (version 1.3.9), pid 26195
    
    
  • Le service de signature des zones
    service opendnssec-signer start
    OpenDNSSEC signer engine version 1.3.9
    

Ce qui devrait se traduire dans les journaux par des messages comme ceux ci:

ods-enforcerd: opendnssec starting...
ods-enforcerd: opendnssec Parent exiting...
ods-enforcerd: opendnssec forked OK...
ods-enforcerd: group set to: opendnssec (118)
ods-enforcerd: user set to: opendnssec (116)
ods-enforcerd: opendnssec started (version 1.3.9), pid 26195
ods-enforcerd: HSM opened successfully.
ods-enforcerd: Checking database connection...
ods-enforcerd: Database connection ok.
ods-enforcerd: Reading config "/etc/opendnssec/conf.xml"
ods-enforcerd: Reading config schema "/usr/share/opendnssec/conf.rng"
ods-enforcerd: Communication Interval: 3600
ods-enforcerd: No DS Submit command supplied
ods-enforcerd: SQLite database set to: /var/lib/opendnssec/db/kasp.db
ods-enforcerd: Log User set to: local0
ods-enforcerd: Switched log facility to: local0
ods-enforcerd: Connecting to Database...
ods-enforcerd: Policy default found.
ods-enforcerd: Key sharing is Off.
ods-enforcerd: Purging keys...
ods-enforcerd: zonelist filename set to /etc/opendnssec/zonelist.xml.
ods-enforcerd: Zone daedelys.org found.
ods-enforcerd: Policy for daedelys.org set to default.
ods-enforcerd: Config will be output to /var/lib/opendnssec/signconf/daedelys.org.xml.
ods-enforcerd: No change to: /var/lib/opendnssec/signconf/daedelys.org.xml
ods-enforcerd: Disconnecting from Database...
ods-enforcerd: Sleeping for 3600 seconds.
ods-signerd: [hsm] libhsm connection opened succesfully
ods-signerd: [engine] signer started

Et vous devriez avoir une zone signée :

head -n2 /var/lib/opendnssec/signed/db.world.daedelys.org.signed 
daedelys.org.   3600    IN      SOA     daedelys.org. root.daedelys.org. 2014010607 86400 3600 2419200 3600
daedelys.org.   3600    IN      RRSIG   SOA 8 2 3600 20140113223041 20140106164832 47301 daedelys.org. pXeveU67AVQRx/6uMvjVzTB6AtLmHGpsd/wQqGYTsoLVTy7psKzstaWX2ZcVPQYBx4Dxu/s8IPg/0PC6orYUfxW5SH0REjLKn1hVZY//RDE5/Cs8d29Q1P1cGGNHLACWM2dvB1F8fMrH6EmkLZHnjORvrPOiudc3UxLMUmrMuZ0=

Il ne reste plus qu'à fournir cette zone à votre serveur DNS préféré. Par exmple pour Bind :

cp /var/lib/opendnssec/signed/db.world.daedelys.org.signed /var/cache/bind
/usr/sbin/rndc reload daedelys.org

Vous pouvez également automatiser cette démarche avec la sous-balise NotifyCommmand de la balise Signer du fichier conf.xml

Notes

1 Parce que vous êtes des personnes sérieuses qui vérifient la validité d'une documentation avant de l'utiliser, n'est ce pas ?