Kerberos

Published: 12-01-2015

Updated: 10-09-2016

By: Maxime de Roucy

tags: kerberos nfs

J’utilise uniquement mit-krb5 (je n’utilise pas l’implémentation Heimdal). Ici je ne détaillerai que le cas d’une architecture non répliquée.

Le poste client et sous Archlinux, le serveur sous Gentoo. Le but de cette installation est de sécuriser un partage NFSv4 (serveur NFS sous Gentoo et client Archlinux, ce sont les deux même machines).

Vérifier les informations trouvées sur le net (y compris ce wiki) sur la documentation officiel correspondant à votre version de krb5.

Notes générales

Qu’est ce que Kerberos

Outre le fait que ce soit le chien des enfers, Kerberos est un système d’authentification SSO (single-sign-on).

Quelle meilleur description que celle de ceux qui l’on inventé et implémenté :

Kerberos V5 is an authentication system developed at MIT. Kerberos is named for the three-headed watchdog from Greek mythology, who guarded the entrance to the underworld.

[…] Since Kerberos negotiates authenticated, and optionally encrypted, communications between two points anywhere on the internet, it provides a layer of security that is not dependent on which side of a firewall either client is on. Since studies have shown that half of the computer security breaches in industry happen from inside firewalls, MIT’s Kerberos V5 plays a vital role in maintaining your network security.

[…] Kerberos V5 is a single-sign-on system, which means that you have to type your password only once per session, and Kerberos does the authenticating and encrypting transparently.

Fonctionnement général

Le fonctionnement général de kerberos est assez bien décrit sur Wikipedia et dans la documentation officiel.

Under Kerberos, a client (generally either a user or a service) sends a request for a ticket to the Key Distribution Center (KDC). The KDC creates a ticket-granting ticket (TGT) for the client, encrypts it using the client’s password as the key, and sends the encrypted TGT back to the client. The client then attempts to decrypt the TGT, using its password. If the client successfully decrypts the TGT (i.e., if the client gave the correct password), it keeps the decrypted TGT, which indicates proof of the client’s identity.

The TGT, which expires at a specified time, permits the client to obtain additional tickets, which give permission for specific services. The requesting and granting of these additional tickets is user-transparent.

Ticket

Un ticket est un « passe » (fichier ou élément en mémoire) permettant d’authentifier un principal sur un réseau (et donc au final de lui permettre d’accéder à certain services).

La notion de ticket est assez bien décrite dans la documentation officiel mais reste très théorique.

D’après ce que j’ai pu lire jusqu’à présent et ce que j’en ai compris un ticket contient :

Le tout chiffré avec la clé du principal auquel il est destiné.

TGT

C’est premier le ticket ticket qu’un principal obtient. Il permet au client d’obtenir d’autre ticket permettant d’accéder à des services.

Un exemple ; je dispose d’un serveur NFSv4 qui requière une authentification via Kerberos.

Sur le client, montage de la partition NFSv4 sécurisée

max@laptop % mount /mnt/ddext
max@laptop % ls /mnt/ddext
ls: impossible d'ouvrir le répertoire /mnt/ddext: Permission non accordée

max@laptop % klist

klist: Credentials cache file '/tmp/krb5cc_1000' not found

Pour l’instant je (utilisateur max soit principal max@LAN) ne suis pas authentifié sur le serveur Kerberos. Je ne peut donc pas accéder au partage même si celui-ci est monté.

max@laptop % kinit

Password for max@LAN:

max@laptop % klist

Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: max@LAN

Valid starting       Expires              Service principal
16/11/2014 18:24:19  17/11/2014 18:24:19  krbtgt/LAN@LAN

Je me suis authentifié et ai donc obtenu un TGT (krbtgt/LAN@LAN).

max@laptop % ls /mnt/ddext
bd/  Bouquins/  Images/  …

max@laptop % klist

Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: max@LAN

Valid starting       Expires              Service principal
16/11/2014 18:24:19  17/11/2014 18:24:19  krbtgt/LAN@LAN
16/11/2014 18:24:32  17/11/2014 18:24:19  nfs/server.lan@LAN

Je peux maintenant accédé au partage. La commande ls a aussi déclenché la requête d’un ticket spécifique au service NFS. Le système a automatiquement utilisé le TGT pour obtenir le ticket nfs/server.lan@LAN.

La machine cliente a eu besoin de s’authentifier pour faire le mount (principal nfs/laptop.lan@LAN ; voir l’article stockage_reseau) mais ça n’est pas important dans cette exemple.

principal

La meilleur description que j’ai pu trouvé est celle de la documentation officiel.

A Kerberos principal is a unique identity to which Kerberos can assign tickets. Principals can have an arbitrary number of components. Each component is separated by a component separator, generally ‘/’. The last component is the realm, separated from the rest of the principal by the realm separator, generally ‘@’. If there is no realm component in the principal, then it will be assumed that the principal is in the default realm for the context in which it is being used.

Traditionally, a principal is divided into three parts: the primary, the instance, and the realm. The format of a typical Kerberos V5 principal is primary/instance@REALM.

En résumé, un principal un utilisateur, une machine… un élément unique auquel Kerberos peux assigné un ticket. Il possède une clé secrète. Dans l’exemple de Wikipedia, le client et le serveur d’impression sont des principals.

Realm

Littéralement le « royaume », c’est une parti du principal. On peut le voir comme un nom de domaine. Dans le principal le realm est positionné après le @ et est généralement en lettres capitales.

KDC

Key Distribution Center distribue les clés et les tickets. Englobe le serveur d’authentification et le TGS.

TGS

Ticket-Granting Server distribue les tickets.

keytab

C’est un fichier où sont stocké les clés de certains principals. Cela permet à ces principals d’obtenir leur tickets sans avoir a rentrer un mot de passe (encore faut-il qu’il ait accès au keytab).

Par défaut le fichier keytab est /etc/krb5.keytab.

Implémentation

mit-krb5 se compose de plusieurs démons et d’outils clients. Les principaux sont :

Installation

Pas de version client, un seul paquet pour les outils clients et les serveurs.

Archlinux :

root@laptop # pacman -S krb5

Gentoo :

root@server # emerge krb5

Configuration

Synchronisation ntp

Il faut que l’horloge du serveur et du client soit synchronisée.

Je passe les détails mais en gros, sur le serveur Gentoo (si celui-ci utilise openntpd) :

max@laptop % cat /etc/ntpd.conf
listen on *
servers 0.gentoo.pool.ntp.org
servers 1.gentoo.pool.ntp.org
servers 2.gentoo.pool.ntp.org
servers 3.gentoo.pool.ntp.org

Sur le client Archlinux, /etc/systemd/timesyncd.conf :

[Time]
NTP=server.lan
FallbackNTP=0.arch.pool.ntp.org 1.arch.pool.ntp.org 2.arch.pool.ntp.org 3.arch.pool.ntp.org

Sinon on peut mettre n’importe quelle source du moment que c’est la même.

Client (Archlinux)

/etc/krb5.conf

[libdefaults]
    default_realm = LAN
[realms]
    LAN = {
        admin_server = server.lan
        default_domain = lan
        kdc = server.lan
    }
[domain_realm]
    .lan = LAN
    lan  = LAN

Sources :

Serveur (Gentoo)

Général

Le serveur et le client doivent utiliser le même fichier de configuration général krb5.conf.

KDC

Le programmes relatif au KDC (uniquement utilisé sur le serveur) parse les fichier krb5.conf et kdc.conf. Il est donc possible, mais non recommandé de placer des section du kdc.conf dans le krb5.conf.

/etc/kdc.conf

[kdcdefaults]

[realms]
    LAN = {
        database_name = /var/lib/krb5kdc/principal
        acl_file = /var/lib/krb5kdc/kadm5.acl
        key_stash_file = /var/lib/krb5kdc/.k5.LAN
        kdc_ports = 88
        max_life = 10h 0m 0s
        max_renewable_life = 7d 0h 0m 0s
    }
[logging]
    kdc = SYSLOG
    admin_server = SYSLOG
    default = SYSLOG

Sources :

Base de données

Création de la base de données utilisé par le KDC :

root@server # kdb5_util create -s

-s permet de stocker la clé du KDC (le mot de passe) dans un fichier (stash file, .k5.LAN). Ça évite d’avoir a entrer la clé (le mot de passe) à chaque démarrage du KDC.

La base sera créé dans le dossier /var/lib/krb5kdc/.

Source : documentations officiel d’installation (version 1.12)

Principal d’administration

Création du principal d’administration root/admin@LAN qui va nous servir à configurer les autres.

root@server # kadmin.local
Authenticating as principal root/admin@LAN with password.

kadmin.local:  addprinc root/admin@LAN

WARNING: no policy specified for root/admin@LAN; defaulting to no policy
Enter password for principal "root/admin@LAN":
Re-enter password for principal "root/admin@LAN":
Principal "root/admin@LAN" created.

kadmin.local:  quit

On pourrait penser que cette partie n’est pas obligatoire, vu qu’il est possible de d’administrer la base via kadmin.local. C’est effectivement le cas si on utilise pas le fichier keytab sur les client. Si on veut utiliser le keytab sur un poste client (c’est le cas dans une conf NFSv4), on va devoir utiliser l’outil kadmin sur le poste client (seul outil, à ma connaissance, capable d’écrire dans le keytab) et donc on va avoir besoins d’un principal d’administration (cf. section keytab dans « Création de principals »).

Sources :

ACL

On donne tous les droits à ce principal. C’est pas très safe mais j’en ai rien à foutre.

root@server # cat /var/lib/krb5kdc/kadm5.acl
root/admin@LAN        *
root@server # chmod 600 /var/lib/krb5kdc/kadm5.acl

Sources :

Démarrage des démons

systemd

Pour ceux qui ont fait le choix d’utiliser systemd. Au 30/01/2015, sous Gentoo les fichiers d’intégration de mit-krb5 à systemd sont manquant.

J’ai donc utilisé et adapté ceux utilisé sous ArchLinux.

root@server # pwd
/etc/systemd/system
root@server # tail -n +1 krb5-k*
==> krb5-kadmind.service <==
[Unit]
Description=Kerberos 5 administration server

[Service]
ExecStart=/usr/sbin/kadmind -nofork

[Install]
WantedBy=multi-user.target

==> krb5-kdc.service <==
[Unit]
Description=Kerberos 5 KDC

[Service]
ExecStart=/usr/sbin/krb5kdc -n
Restart=always

[Install]
WantedBy=multi-user.target

==> krb5-kpropd.service <==
[Unit]
Description=Kerberos 5 propagation server

[Service]
ExecStart=/usr/sbin/kpropd -S

[Install]
WantedBy=multi-user.target

==> krb5-kpropd@.service <==
[Unit]
Description=Kerberos 5 propagation server
Conflicts=krb5-kpropd.service

[Service]
ExecStart=/usr/sbin/kpropd
StandardInput=socket
StandardError=syslog

==> krb5-kpropd.socket <==
[Unit]
Description=Kerberos 5 propagation server

[Socket]
ListenStream=754
Accept=yes

[Install]
WantedBy=sockets.target
root@server # systemctl daemon-reload
root@server # systemctl start krb5-kdc.service
root@server # systemctl enable krb5-kdc.service
root@server # systemctl start krb5-kadmind.service

Il n’est pas nécessaire de faire en sorte que mit-krb5kadmind tourne tous le temps. Il doit seulement être démarré quand on utilise kadmin.

OpenRC

Si vous utilisez OpenRC et non systemd.

root@server # /etc/init.d/mit-krb5kadmind start
root@server # rc-update add mit-krb5kdc

mit-krb5kadmind dépend de mit-krb5kdc donc la première commande va démarrer les deux démon.

Il n’est pas nécessaire de faire en sorte que mit-krb5kadmind tourne tous le temps. Il doit seulement être démarré quand on utilise kadmin.

Vérifications

Vérifié que vos logs contiennent bien les lignes :

krb5kdc[…]: krb5kdc: starting...
kadmind[…]: kadmind: starting...

Ces ligne indique que le KDC et kadmind on bien terminé leurs phase d’initialisation et qu’il sont prêt à gérer les requêtes. Attention, kadmind génère la ligne de log kadmind[…]: starting. Ce n’est pas celle qu’on cherche.

J’ai eu quelques soucis avec kadmind. Celui-ci n’émettait aucune erreur dans les logs mais ne répondait pas. Après quelques investigations je me suis aperçus qu’il été bloqué sur la lecture de /dev/random. Mon serveur n’est soumis qu’a peut d’activité et celui-ci ne généré pas assez d’entropie. kadmind mettait plusieurs minutes à démarrer.

J’ai résolue le problème en utilisant haveged.

Création de principals

Le but de cette installation de Kerberos de mettre en place un serveur NFSv4 avec authentification. Pour cette exemple je vais donc créer deux principals max@LAN et nfs/laptop.lan@LAN qui me serviront dans la mise en place du NFS.

Soit :

root@server # kadmin
Authenticating as principal root/admin@LAN with password.
Password for root/admin@LAN:

kadmin:  addprinc max

WARNING: no policy specified for max@LAN; defaulting to no policy
Enter password for principal "max@LAN":
Re-enter password for principal "max@LAN":
Principal "max@LAN" created.

kadmin:  addprinc -randkey nfs/laptop.lan

WARNING: no policy specified for nfs/laptop.lan@LAN; defaulting to no policy
Principal "nfs/laptop.lan@LAN" created.

kadmin:  quit

keytab

Pour pouvoir récupérer la clé de principal nfs/laptop.lan@LAN et la stocker dans le fichier keytab du client nous devons nous connecter à la console d’administration à partir du poste client.

root@laptop # kadmin

Authenticating as principal root/admin@LAN with password.
Password for root/admin@LAN:

kadmin:  ktadd nfs/laptop.lan

Entry for principal nfs/laptop.lan with kvno 3, encryption type aes256-cts-hmac-sha1-96 added to keytab FILE:/etc/krb5.keytab.
Entry for principal nfs/laptop.lan with kvno 3, encryption type aes128-cts-hmac-sha1-96 added to keytab FILE:/etc/krb5.keytab.
Entry for principal nfs/laptop.lan with kvno 3, encryption type des3-cbc-sha1 added to keytab FILE:/etc/krb5.keytab.
Entry for principal nfs/laptop.lan with kvno 3, encryption type arcfour-hmac added to keytab FILE:/etc/krb5.keytab.

kadmin:  quit
Vérifications
root@laptop # klist -k

Keytab name: FILE:/etc/krb5.keytab
KVNO Principal
---- --------------------------------------------------------------------------
   3 nfs/laptop.lan@LAN
   3 nfs/laptop.lan@LAN
   3 nfs/laptop.lan@LAN
   3 nfs/laptop.lan@LAN

Le keytab contient bien la clé du principal nfs/laptop.lan@LAN.

Le fait qu’elle soit présente plusieurs fois est normal. Elle est chiffrée avec plusieurs algorithmes :

root@laptop # klist -k -e

Keytab name: FILE:/etc/krb5.keytab
KVNO Principal
---- --------------------------------------------------------------------------
   3 nfs/laptop.lan@LAN (aes256-cts-hmac-sha1-96)
   3 nfs/laptop.lan@LAN (aes128-cts-hmac-sha1-96)
   3 nfs/laptop.lan@LAN (des3-cbc-sha1)
   3 nfs/laptop.lan@LAN (arcfour-hmac)

obtention de tickets

Pour vérifier que tout à bien fonctionné nous allons faire des demandes de tickets.

max@LAN

Je suis loggé en temps que max, par défaut kinit demande donc un ticket pour max@LAN. Je n’ai pas besoins de lui spécifier d’option.

max@laptop % klist
klist: Credentials cache file '/tmp/krb5cc_1000' not found

max@laptop % kinit
Password for max@LAN:

max@laptop % klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: max@LAN

Valid starting       Expires              Service principal
16/11/2014 15:25:59  17/11/2014 15:25:59  krbtgt/LAN@LAN
nfs/laptop.lan@LAN

Ici je doit spécifier à kinit d’utiliser le principal nfs/laptop.lan@LAN mais aussi de chercher la clé dans le keytab. Je suis loggé en root car le fichier keytab n’est accessible qu’à cette utilisateur.

root@laptop # klist
klist: Credentials cache file '/tmp/krb5cc_0' not found

root@laptop # kinit -k -p nfs/laptop.lan

root@laptop # klist

Ticket cache: FILE:/tmp/krb5cc_0
Default principal: nfs/laptop.lan@LAN

Valid starting       Expires              Service principal
16/11/2014 15:58:15  17/11/2014 15:58:15  krbtgt/LAN@LAN

Firewall

Sur le serveur, j’accepte les requêtes au KDC (kerberos : 88), les connexion à kadmind (kerberos-adm : 749) et les requêtes de changement de mot de passe (kpasswd : 464). Les port kerberos-adm et kpasswd sont écouté par le démon kadmind.

-A INPUT -s 192.168.1.0/24 -p tcp --dport kerberos -j ACCEPT
-A INPUT -s 192.168.1.0/24 -p udp --dport kerberos -j ACCEPT
-A INPUT -s 192.168.1.0/24 -p tcp --dport kerberos-adm -j ACCEPT
-A INPUT -s 192.168.1.0/24 -p udp --dport kerberos-adm -j ACCEPT
-A INPUT -s 192.168.1.0/24 -p tcp --dport kpasswd -j ACCEPT
-A INPUT -s 192.168.1.0/24 -p udp --dport kpasswd -j ACCEPT

Sources :

Réinitialisation

Si vous avez merdé quelque part et que vous voulez repartir sur un truc propre.

Sur le serveur :

root@server # kdestroy
root@server # /etc/init.d/mit-krb5kdc stop
root@server # kdb5_util destroy
root@server # rm /var/lib/krb5kdc/.k5.LAN
root@server # rm /etc/krb5.keytab

Dans l’ordre j’ai :

Sur le client :

root@laptop # kdestroy
root@laptop # rm /etc/krb5.keytab

Commandes

Résumé :

max@laptop % ls /tmp/krb5cc*
max@laptop % mount /mnt/server
max@laptop % ls /tmp/krb5cc*
/tmp/krb5ccmachine_LAN
max@laptop % sudo klist -c /tmp/krb5ccmachine_LAN
Ticket cache: FILE:/tmp/krb5ccmachine_LAN
Default principal: nfs/laptop.lan@LAN

Valid starting       Expires              Service principal
31/01/2016 19:01:13  01/02/2016 19:01:13  krbtgt/LAN@LAN
31/01/2016 19:01:13  01/02/2016 19:01:13  nfs/server.lan@LAN
max@laptop % ls /mnt/server
… FAIL …
max@laptop % kinit
Password for max@LAN:
max@laptop % ls /tmp/krb5cc*
/tmp/krb5cc_1000  /tmp/krb5ccmachine_LAN
max@laptop % klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: max@LAN

Valid starting       Expires              Service principal
31/01/2016 20:13:55  01/02/2016 20:13:55  krbtgt/LAN@LAN
max@laptop % ls /mnt/server
… OK …

TODO

Normalement les client kerberos font des résolutions DNS forward & inverse (pour avoir le nom canonique CNAME de la machine). Pourtant, après avoir supprimé les entrées correspondant au DNS inverse de mon serveur DNS, kerberos fonctionne toujours parfaitement.

Je suppose que la résolution DNS inverse n’est effectué que si le forward DNS trouve un CNAME. Dans mon installation interne je n’utilise pas de CNAME.

Il faudrait que je vérifie ça dans le code source.

Source : Kerberos documentation : Principal names and DNS