L'Internet Rapide et Permanent

Le protocole Kerberos

Christian Caleca

Vous disposez d'une connexion permanente et rapide… et maintenant, vous êtes perdu dans la technique…

Cette série « L'Internet Rapide et Permanent », que Christian Caleca nous a aimablement autorisé à reproduire, est là pour répondre à quelques-unes de ces questions. Cet article parlera du protocole d'authentification Kerberos.

N'hésitez pas à commenter cet article ! Commentez

- Titre : L'Internet Rapide et Permanent

- Auteur : Christian Caleca

- Parution : 6 mars 2010

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2010 Christian Caleca. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- 1. Introduction

- 2. Principe général

- 2-1. Authentification

- 2-2. Demande de ticket de service

- 2-3. Authentification du client auprès du service

- 2-3-1. Et GSSAPI ?

- 2-4. Bref…

- 3. Démonstration simple

- 3-1. Le décor de la scène

- 3-1-1. Ça ne marche pas

- 3-1-2. Risette à Kerberos

- 3-2. Faisons le point

- 4. Installation de la plate-forme

- 4-1. L'architecture

- 4-1-1. Les fils du Temps

- 4-1-2. Le « KDC »

- 4-1-3. Le client

- 4-1-4. Le serveur

- 5. Configuration du KDC

- 5-1. Le krk5.conf

- 5-2. Le kdc.conf

- 5-3. Création de la base du royaume

- 5-4. Un administrateur du royaume

- 6. La station de travail

- 6-1. Vérifications diverses

- 6-2. Le kinit

- 7. Configuration d'apache-krb

- 7-1. Installation d'Apache

- 7-2. Configuration de Kerberos

- 7-2-1. Sur kerberos.maison.mrs

- 7-2-2. Sur apache-krb

- 7-3. Configuration d'Apache

- 7-3-1. Configuration du client

- 7-3-2. Vérifions

- 7-3-2-1. Dans une console

- 7-3-2-2. Dans Firefox

- 7-3-2-3. Re dans la console

- 7-3-2-4. Le snif

- 8. Compléments

- 8-1. Pam et Kerberos

- 8-2. Proxy Squid

- 8-2-1. Le KDC

- 8-2-2. squid.conf

- 8-2-3. Environnement de Squid

- 8-3. Et encore…

- 9. Le Kerberos d'Active Directory

- 9-1. L'authentification d'un utilisateur

- 9-2. L'enregistrement d'un service

- 9-2-1. Samba

- 9-3. Un ssh avec 3 têtes

- 9-4. Un calmar avec trois têtes

- 9-4-1. Accès au keytab

- 9-4-2. Environnement de la pieuvre

- 9-4-3. Configuration de la seiche

- 9-5. Pour finir

- 10. Remerciements Developpez

1 - Introduction

Cerbère (Κέρβερος en grec ancien), animal mythique représenté par un chien à plusieurs têtes (traditionnellement trois), a pour principale fonction d'empêcher ceux que Charon a menés de l'autre côté du Styx de faire le voyage dans l'autre sens. Éventuellement, il peut même servir à empêcher les touristes qui voudraient traverser le Styx sans passer par les services de Charon.

Dans les systèmes d'information, Kerberos est tout autre chose. Il s'agit d'un protocole d'authentification en réseau, dont les principales caractéristiques sont :

- authentification sécurisée : le mot de passe de l'utilisateur ne circule jamais sur le réseau, nous verrons par quel prodige ;

- authentification unique pour plusieurs services : SSO (Single Sign On), l'utilisateur n'a pas à se réauthentifier chaque fois qu'il utilise un nouveau service qui nécessite un droit d'accès ;

- le réseau est « ouvert », autrement dit, c'est un bouge où tout le monde cherche à voler l'information qui circule. Kerberos propose donc une série de mécanismes fondés sur la cryptographie à clé symétrique, pour :

- éviter qu'un indiscret puisse se faire passer pour qui il n'est pas ;

- éviter qu'un indiscret « rejoue » un dialogue qu'il a enregistré sur le réseau ;

- permettre à chaque partenaire dans un dialogue de s'authentifier vis-à-vis de l'autre.

Kerberos ne fait tout de même pas de miracles et ne saurait à lui seul garantir l'intégrité de chaque protagoniste. En particulier :

- il ne sait pas déjouer les attaques par déni de service ;

- il ne sait pas détecter le vol de clés secrètes, il est de la responsabilité des partenaires de protéger eux-mêmes leurs clés d'un vol ;

- il ne sait pas déjouer les attaques par dictionnaire sur les mots de passe ;

- il n'est pas fait pour rendre les échanges confidentiels bien que ceci puisse se faire avec des extensions, il n'est là que pour authentifier des utilisateurs et des services.

Ce protocole a été créé au MIT Massachusetts Institute of Technology, puis normalisé dans sa version 5 dans les RFC 1510 en 1993, rendu obsolète par les RFC 4120 en 2005. Avant, il y a eu la version 4 dont on entend encore parler, mais qui n'est plus maintenue. Les versions précédentes n'étant restées qu'au stade de « proof of concept ». Autant dire que Kerberos n'est plus vraiment une nouveauté.

Pourquoi s'intéresser à Kerberos ? Pour ses avantages bien sûr, et aussi parce que Microsoft a introduit (en le torturant quelque peu, nous savons tous le mal que cette entreprise éprouve à respecter scrupuleusement une norme) ce protocole dans « Active Directory » à partir de Windows 2000 Server. Notons tout de même un effort d'interopérabilité dans la version Windows 2008.

Pour les gens qui comme moi sont astreints à gérer ce genre de serveurs, autant essayer d'en tirer profit quand c'est possible.

Nous allons d'abord essayer de comprendre comment fonctionne cette usine à gaz sulfureux (Kerberos), la mettre en œuvre dans une solution 100 % GPL (plus facile pour comprendre ce que l'on fait).

Nous verrons enfin ce que l'on peut tirer du Cerbère caché dans Active Directory.

L'objectif de ce chapitre n'est pas vraiment d'expliquer dans le détail le fonctionnement de l'usine, mais plutôt de donner quelques informations utiles pour la mettre en œuvre en comprenant un minimum ce que l'on fait, de manière à savoir quoi en attendre et pouvoir réagir avec un peu d'intelligence si jamais ça ne fonctionnait pas comme prévu.

2 - Principe général

Dans le schéma qui suit, nous avons :

- un utilisateur qui a ouvert une session sur une station de travail ;

- un serveur Kerberos, qui abrite deux services distincts :

- un service d'authentification,

- un service d'attribution de tickets d'accès à divers services ;

- un (ou plusieurs) serveur(s) de services qui réclame(nt) l'authentification du client. Par exemple un serveur HTTP, ou tout autre service « kerberisable ».

L'utilisateur dispose au moins d'un mot de passe qui servira de clé de chiffrement pour son authentification initiale, que nous appellerons pour que la suite soit bien lisible et compréhensible. Cette clé est réputée connue des seuls utilisateurs et .

Le serveur dispose d'une clé de chiffrement que nous appellerons et que l'utilisateur ne connait pas.

Le serveur d'application dispose d'une clé de chiffrement . Cette clé n'est connue que par le et le serveur d'application.

2-1 - Authentification

L'utilisateur doit commencer par s'authentifier auprès du service d'authentification de Kerberos. Généralement, cette opération se fait en indiquant le nom de l'utilisateur et son mot de passe dans un formulaire de saisie. Qu'il soit bien clair que ceci ne veut pas dire que le mot de passe va circuler sur le réseau.

- (1) l'utilisateur envoie une requête (AS_REQ). Cette requête contient le nom d'utilisateur, en clair, mais pas le mot de passe, n'ayons pas peur de le répéter.

- (2) grâce à un système de « challenge » que nous étudierons plus tard, le serveur vérifie que l'utilisateur est bien celui qu'il prétend être. Si c'est le cas, le serveur crée :

- une clé de session qui ne sera partagée qu'entre le et l'utilisateur. Il la chiffre avec  ;

- une copie de et l'identité de l'utilisateur que nous nommerons , le tout chiffré avec , ce qui constitue le  ;

- il renvoie le tout (AS_REP) à l'utilisateur.

Ledit utilisateur peut ainsi :

- récupérer sa clé de session en la déchiffrant avec sa . La clé de session ne pourra être utilisée que si le bon mot de passe est connu par les deux protagonistes, sans pour autant qu'il ait été échangé sur le réseau ;

- ranger son dans un coin, car il ne peut le déchiffrer, ne connaissant pas la clé . Rappelons en effet que :

C'est simple, c'est beau. En résumé, l'utilisateur dispose d'une clé de session et d'un . Ce ne peut servir qu'à ceux qui disposent de c'est-à-dire au seul qui a émis ce . Si un utilisateur présente au un falsifié, le ne pourra le déchiffrer et n'en fera rien. Il est donc primordial de s'assurer qu'il n'y a pas de fuites sur , sans quoi, un faux pourrait voir le jour.

Certains préfèrent une présentation plus graphique, la voici :

2-2 - Demande de ticket de service

(3) Si l'utilisateur souhaite accéder à un service nécessitant son authentification (par exemple un serveur HTTP), il devra demander au un ticket de service spécifique. Le client envoie alors au service serveur Kerberos une TGS_REQ contenant :

- le nom du service et de l'hôte que le client souhaite contacter ;

- son (toujours chiffré avec la clé du que le client ne connait donc pas) ;

- un élément d'authentification, probablement un , chiffré avec la clé de session du client (partagée avec le ). Ce chiffrement prouvera au que le client est bien celui qu'il prétend être (protection contre le vol éventuel du du client) ;

(4) Le va alors répondre en envoyant au client un ticket contenant :

- une clé de session pour le service demandé , chiffrée avec la clé de session du client , soit  ;

- une authentification de client, chiffrée avec la clé du service qui n'est connue que du serveur du service et du , si bien qu'un client mal intentionné ne pourra la modifier. Cette authentification contient également une copie de la clé de session du service , ainsi le client et le service la partagent, mais le client ne peut pas modifier cette clé puisque l'exemplaire destiné au service est chiffré avec une clé qu'il ne connait pas, soit qui est donc un « blob » que le client ne peut lire ni modifier.

Ainsi :

- le peut s'assurer de l'authenticité du client qui a chiffré un avec sa clé de session  ;

- le client va pouvoir s'authentifier dans le service demandé, puisque son accréditation est chiffrée avec la clé du service que le client ne connait pas ;

- le client va transmettre au service une clé de session avec ce service . Le , qui a de l'éthique, s'étant empressé d'oublier cette clé, seuls le client et le service la connaissent.

2-3 - Authentification du client auprès du service

(5) Le client peut maintenant utiliser son ticket de service pour s'authentifier auprès du service « kerbérisé ». Il envoie au serveur d'application :

- le « blob » qui contient son identité et la clé de session de service, le tout chiffré avec la clé du service (que l'utilisateur ne connait pas) soit  ;

- son identité et un , le tout chiffré avec la clé de session de service, soit  ;

(6) La réponse AP_REQ est optionnelle et dans notre cas de figure n'existe pas. Inutile donc de se prendre encore un peu plus la tête.

Lorsque le service Kerbérisé reçoit cette requête, comme il dispose de sa clé de service , il peut déchiffrer le « blob » et donc récupérer :

- l'identité de l'utilisateur fournie par le  ;

- la clé de session pour le service , également fournie par le .

Il peut alors déchiffrer l'identité , ainsi que le . La comparaison des deux identités permettra au service de s'assurer qu'il n'est pas grugé par un intrus, le lui permettra de déduire que ce ticket n'est pas un ticket volé, puis rejoué par un intrus (sauf si l'intrus a pu le faire en moins de 5 minutes).

Il pourra alors ouvrir ses portes au client.

2-3-1 - Et GSSAPI ?

Generic Security Services Application Programming Interface est en gros une couche d'abstraction qui va permettre de normaliser les discussions entre les services concernant la sécurité. Ainsi, Kerberos 5 implémentant GSSAPI, toute application qui implémente également GSSAPI doit pouvoir utiliser Kerberos pour gérer les aspects de sécurité. Nous en verrons un exemple avec ssh.

2-4 - Bref…

Lorsque vous saurez que cette présentation est largement simplifiée, dans la mesure où tous les protagonistes appartiennent au même royaume, que nous n'avons pas évoqué le principe des relations interroyaumes ni de choses comme le renouvellement, la transmission, la révocation de tickets et encore moins les éventuelles extensions du protocole, vous apprécierez d'autant mieux la complexité de l'usine.

Ce que nous avons vu devrait permettre de comprendre ce que nous allons observer dans le cas très simple de notre plate-forme de tests.

3 - Démonstration simple

Voyons tout d'abord comment ça se présente du côté de l'utilisateur à peine averti, sur une plate-forme de tests très rudimentaire.

3-1 - Le décor de la scène

Grâce aux avantages de KVM(1) nous pouvons disposer d'autant de machines que nous le souhaitons, profitons-en.

- un serveur Kerberos.maison.mrs se charge de faire fonctionner l'usine à gaz (AS et TGS, le tout appelé KDC comme Key Distribution Center) ;

- un serveur apache-krb.maison.mrs est un serveur HTTP tout bête, sauf qu'il n'autorise l'accès qu'aux personnes dûment authentifiées par Kerberos ;

- un client qui voudrait bien accéder au contenu de apache-krb.maison.mrs depuis un poste de travail qui dispose des outils nécessaires pour dialoguer avec kerberos.maison.mrs.

3-1-1 - Ça ne marche pas

Première prise, l'utilisateur, qui s'appelle chris, n'a pas fait risette au cerbère, et essaye d'accéder à http://apache-kerb.maison.mrs :

Il se fait jeter dehors…

3-1-2 - Risette à Kerberos

Chris lit alors la notice où on lui explique qu'il doit d'abord s'authentifier auprès du cerbère en utilisant la commande kinit :

~$ kinit chris
Password for chris@MAISON.MRS:

Confiant, il réessaye :

Et là, chris peut fermer son navigateur puis le rouvrir, retourner sur la même page. Il sera automatiquement reconnu durant toute la validité de son ticket.

3-2 - Faisons le point

Nous avons pu observer suffisamment de choses pour mieux comprendre le principe de Kerberos et accessoirement pourquoi ce protocole a été appelé ainsi :

- l'utilisateur doit disposer d'un principal dont il fait la demande à une entité nommée AS, au moyen de la commande kinit. Nous verrons plus loin que cette demande peut être automatisée lors de l'ouverture de la session par l'utilisateur. Cette procédure aboutit au fait que l'utilisateur en question a été authentifié par « l'AS » qui lui a transmis une « chose » (le TGT) dont la validité est limitée dans le temps ;

- lorsque l'utilisateur souhaite utiliser un service soumis à une authentification par Kerberos, sous réserve que l'application cliente qui doit communiquer avec ce service sache le faire, le client obtient silencieusement un « ticket de service » en faisant une requête non moins silencieuse au service TGS de Kerberos, et que ce ticket de service lui ouvre les portes du service convoité.

Nous avons donc pu constater de façon subtile l'effet « SSO » de Kerberos. Cet effet serait bien sûr beaucoup plus visible si en plus d'un service HTTP, nous avions mis en place d'autres services, comme un système de fichier en réseau (NFS v4 par exemple), ssh, ftp voire aussi un proxy HTTP comme squid.

4 - Installation de la plate-forme

Kerberos nous l'avons vu, est un protocole complexe, qui fait intervenir pas mal de concepts. Les mettre en œuvre dans une démarche plus pragmatique, n'est pas superflu.

4-1 - L'architecture

Nous disposons d'un réseau IP 192.168.0.0/24, dans lequel un domaine DNS « en bois » a été créé : maison.mrs. Un serveur DNS est donc en mesure d'effectuer toutes les recherches directes dans ce domaine ainsi que les recherches inverses associées. Kerberos aime bien que les DNS soient propres.

Tous les hôtes qui entreront en jeu fonctionneront sous Debian Squeeze (serveurs) ou Ubuntu Karmic (clients).

4-1-1 - Les fils du Temps

L'exactitude est la politesse des rois, aussi ne faut-il pas s'étonner que l'exactitude temporelle revête au royaume de Kerberos une importance particulière. Plus de 300 secondes (5 minutes) de divergence avec le serveur et c'est le bannissement définitif. Il est donc primordial de disposer, au sein du royaume, d'une horloge de référence, et tous les membres du royaume doivent être synchronisés avec. Pour que ça marche, il ne faut pas que les horloges des protagonistes dérivent entre elles de plus de 300 secondes.

Dans notre réseau IP 192.168.0.0/24, nous disposons d'un serveur de temps (NTP) qui fera une horloge de référence tout à fait acceptable.

4-1-2 - Le « KDC »

Dans ce réseau, un hôte kerberos.maison.mrs va héberger le « serveur » Kerberos. Installation du paquet krb5-admin-server :

kerberos:~# aptitude install krb5-admin-server
Lecture des listes de paquets... Fait
Construction de l'arbre des dépendances
Lecture des informations d'état... Fait
Lecture de l'information d'état étendu
Initialisation de l'état des paquets... Fait
Lecture des descriptions de tâches... Fait
Les NOUVEAUX paquets suivants vont être installés :
bind9-host{a} geoip-database{a} krb5-admin-server krb5-config{a}
krb5-kdc{a} krb5-user{a} libbind9-50{a} libcap2{a}
libdns53{a} libgeoip1{a} libgssrpc4{a} libisc50{a}
libisccc50{a} libisccfg50{a} libkadm5clnt-mit7{a}
libkadm5srv-mit7{a} libkdb5-4{a} liblwres50{a}
0 paquets mis à jour, 18 nouvellement installés, 0 à enlever et 0 non mis à jour.
Il est nécessaire de télécharger 2 491ko d'archives. Après dépaquetage, 6 451ko seront utilisés.
Voulez-vous continuer ? [Y/n/?]

L'installation va poser quelques questions d'apparence anodine, mais lourdes de conséquences cependant. Comme nous allons revenir sur les fichiers de configuration, nous verrons cela plus tard.

Pour l'instant, les dépendances du paquet krb5-admin-server offrent un intérêt plus immédiat :

- krb5-config est un paquet que nous retrouverons sur tous les protagonistes de la plate-forme, il va déterminer principalement la topologie du royaume Kerberos ;

- krb5-user contient des outils d'utilisation de Kerberos. A priori à mettre entre les mains de tous les clients qui ont besoin de l'authentification Kerberos ;

- krb5-kdc contient la partie dite kdc (Key Distribution Center), deux des trois têtes du cerbère (AS et TGS). Ce paquet devra être installé sur d'éventuels serveurs Kerberos esclaves, très utiles en production, mais inutiles ici, pour nos tests.

Les autres dépendances sont des bibliothèques associées ou des outils associés à DNS (Kerberos aime NTP et DNS).

En réalité, la paquet le plus important est ici krb5-kdc. krb5-admin-server n'apporte, comme son nom semble l'indiquer, que des outils d'administration locale de l'usine Kerberos.

N'oublions pas de nous assurer, par un moyen quelconque (ntpdate+cron, ntpd), que l'horloge de cet hôte reste raisonnablement synchronisée avec notre référence de temps. Rappelez-vous, pas plus de 300 secondes de dérive…

4-1-3 - Le client

Ici, juste une station de travail sur laquelle nous installons krb5-user. Par dépendance, krb5-config viendra avec.

krb5-user fournira les outils qui permettront :

- aux administrateurs du royaume de faire leur travail d'administration ;

- aux utilisateurs d'obtenir auprès du KDC les accréditations qui leur seront nécessaires.

Dans ce scénario, l'hôte client s'appellera pchris.maison.mrs et fonctionnera sous Ubuntu Karmic Koala.

4-1-4 - Le serveur

Ce serveur est destiné à fournir des services dont l'accès sera autorisé ou non, en fonction des accréditations que ses clients pourront obtenir du KDC. Dans cet exemple, nous installerons un serveur HTTP (Apache) dont l'accès sera soumis à authentification via Kerberos.

Le paquet krb5-user sera installé dessus également par commodité, mais ce n'est pas une obligation.

Dans ce scénario, le serveur s'appellera apache-krb.maison.mrs, il fonctionnera sous Debian Squeeze.

5 - Configuration du KDC

Le Key Distribution Center est la pièce maitresse de l'usine. Il assure deux fonctions :

- l'authentification des utilisateurs (AS comme Authentication Server) ;

- la distribution de tickets d'autorisation pour les services que souhaite obtenir l'utilisateur (TGS comme Ticket Granting Server).

Nous allons détailler son installation pour, dans un premier temps, réaliser l'authentification des utilisateurs.

5-1 - Le krk5.conf

L'installation du paquet krb5-config a créé un fichier /etc/krb5.conf qu'il a adapté en fonction de quelques questions posées par le script d'installation. Le contenu de ce fichier est beaucoup trop compliqué pour nos petits besoins. En voici une version propre à nous satisfaire :

[libdefaults]
default_realm = MAISON.MRS
dns_lookup_realm = false
dns_lookup_kdc = false

[realms]
MAISON.MRS = {
kdc = 192.168.0.133
admin_server = 192.168.0.133
default_domain = MAISON.MRS
}

[domain_realm]
.maison.mrs = MAISON.MRS
maison.mrs = MAISON.MRS

[logging]
default = FILE:/var/log/krb5.log
kdc = FILE:/var/log/krb5kdc.log
admin-server = FILE:/var/log/krb5adm.log

L'analogie entre les noms du domaine DNS et du royaume Kerberos n'est pas un hasard. Lorsque l'on construit un royaume Kerberos, c'est généralement au sein d'un domaine DNS, autant leur donner le même nom. Simplement, un nom de royaume s'écrit toujours en lettres capitales. Ce qui est surtout une commodité avec le Kerberos du MIT devient une obligation avec le Kerberos d'Active Directory.

Si vous voulez en savoir plus sur les divers paramètres de ce fichier de configuration, installez le paquet krb5-doc qui contient entre autres le manuel de krb5.conf.

5-2 - Le kdc.conf

L'installation de krb5-kdc a créé un répertoire /etc/krb5kdc qui contient actuellement un unique fichier kdc.conf :

[kdcdefaults]
kdc_ports = 750,88

[realms]
MAISON.MRS = {
database_name = /var/lib/krb5kdc/principal
admin_keytab = FILE:/etc/krb5kdc/kadm5.keytab
acl_file = /etc/krb5kdc/kadm5.acl
key_stash_file = /etc/krb5kdc/stash
kdc_ports = 750,88
max_life = 10h 0m 0s
max_renewable_life = 7d 0h 0m 0s
master_key_type = des3-hmac-sha1
supported_enctypes = aes256-cts:normal arcfour-hmac:normal des3-hmac-sha1:normal des-cbc-crc:normal des:normal des:v4 des:norealm des:onlyrealm des:afs3
default_principal_flags = +preauth
}

Il n'y a rien à redire à cette configuration, mais il y a à remarquer qu'il est fait référence à trois fichiers qui n'existent pas encore : /var/lib/krb5kdc/principal, /etc/krb5kdc/kadm5.keytab et /etc/krb5kdc/kadm5.acl. Autant dire que l'usine ne va pas démarrer toute seule, il y a encore du travail à faire.

5-3 - Création de la base du royaume

Il faut commencer par créer la base de données pour notre royaume MAISON.MRS. Le moyen le plus standard de le faire est d'employer la commande kdb5_util :

kerberos:~# kdb5_util -r MAISON.MRS create -s
Loading random data
... (un (long) moment plus tard)...
Initializing database '/var/lib/krb5kdc/principal' for realm 'MAISON.MRS',
master key name 'K/M@MAISON.MRS'
You will be prompted for the database Master Password.
It is important that you NOT FORGET this password.
Enter KDC database master key:
Re-enter KDC database master key to verify:

Le mot de passe demandé ici a toutes les chances de ne jamais servir, sauf en cas de maintenance forcée de la base. En cas d'oubli, point de salut…

En attendant, /var/lib/krb5kdc/ a été peuplé :

kerberos:/var/lib/krb5kdc# ls -l
total 16
-rw------- 1 root root 8192 févr. 13 15:57 principal
-rw------- 1 root root 8192 févr. 13 15:57 principal.kadm5
-rw------- 1 root root    0 févr. 13 15:57 principal.kadm5.lock
-rw------- 1 root root    0 févr. 13 15:57 principal.ok

Pour démarrer l'usine, il nous faut encore un fichier /etc/krb5kdc/kadm5.acl, même vide pour l'instant :

kerberos:/etc/krb5kdc# touch /etc/krb5kdc/kadm5.acl

Normalement, nous pouvons maintenant faire démarrer la bête :

kerberos:/etc/krb5kdc# /etc/init.d/krb5-kdc start
kerberos:/etc/krb5kdc# /etc/init.d/krb5-admin-server start
kerberos:/etc/krb5kdc# ps aux | grep krb
root      2363  0.0  0.1   3468   556 ?        Ss   16:06   0:00 /usr/sbin/krb5kdc
kerberos:/etc/krb5kdc# ps aux | grep kadmin
root      2367  0.0  0.1   3424   736 ?        Ss   16:07   0:00 /usr/sbin/kadmind

5-4 - Un administrateur du royaume

Nous allons maintenant créer un « principal » pour un administrateur de ce royaume. À ce stade, nous ne pouvons le faire que localement, car il faut disposer d'un principal d'administrateur du royaume pour pouvoir le faire à distance. C'est le coup de l'œuf et de la poule…

Utilisons pour ceci la commande interactive kadmin.local :

kerberos:/etc/krb5kdc# kadmin.local
Authenticating as principal root/admin@MAISON.MRS with password.

kadmin.local:  add_principal -clearpolicy chris/admin

Enter password for principal "chris/admin@MAISON.MRS":
Re-enter password for principal "chris/admin@MAISON.MRS":
Principal "chris/admin@MAISON.MRS" created.
kadmin.local:  quit

Ici, quelques explications ne sont sans doute pas superflues :

- le principal root/admin@MAISON.MRS est fictif, du moins pour l'instant, et n'est utilisable que par la commande kadmin.local. C'est l'œuf originel (ou la poule), en quelque sorte ;

- une fois l'outil d'administration interactif local démarré, nous créons un « vrai » principal avec la commandeš:

add_principal -clearpolicy chris/admin

- cette commande va créer le principal chris/admin avec un mot de passe associé. Nous ne nous préoccupons pas pour l'instant des « policy » ;

- ce principal est ici constitué de deux parties :

- la partie à gauche du /, obligatoire, et qui devrait correspondre à un utilisateur UNIX, ici chris,

- la partie à droite du /, optionnelle, qui sert à distinguer les différents rôles que peut jouer un même utilisateur, ici admin. Nous n'allons pas tarder à en comprendre le sens.

Nous allons maintenant justement définir les privilèges qu'accorde le rôle admin pour la gestion du royaume :

kerberos:~# echo '*/admin *' >> /etc/krb5kdc/kadm5.acl

Nous ajoutons dans « l' Access Control List » une ligne qui veut dire en français : tous les principaux affublés du rôle « admin » (*/admin) auront tous les droits d'administration ( *).

Nous avons désormais un œuf (ou une pondeuse) et nous pouvons tester si la reproduction va pouvoir se faire dans de bonnes conditions. Mais avant, il faut redémarrer le service d'administration de l'usine (à cause de la modification de l'ACL) :

kerberos:/etc/krb5kdc# /etc/init.d/krb5-admin-server restart

Voyons d'abord s'il est possible de s'authentifier avec ce principal :

kerberos:/etc/krb5kdc# kinit -V chris/admin
Password for chris/admin@MAISON.MRS:
Authenticated to Kerberos v5

Ça marche. Utilisons maintenant la commande kadmin assez proche de kadmin.local à part qu'elle est utilisable depuis tout hôte correctement configuré (krb5.conf) où elle est installée et qui nécessite absolument de s'identifier comme /admin :

kerberos:/etc/krb5kdc# kadmin -p chris/admin
Authenticating as principal chris/admin with password.
Password for chris/admin@MAISON.MRS:
kadmin:  add_principal -clearpolicy chris
Enter password for principal "chris@MAISON.MRS":
Re-enter password for principal "chris@MAISON.MRS":
Principal "chris@MAISON.MRS" created.

Nous avons créé un principal pour chris, mais cette fois-ci sans aucun rôle particulier. Le mot de passe devrait être différent de celui du principal chris/admin, pour des raisons de sécurité bien entendu.

Profitons pour lire la liste des principaux existants :

kadmin:  list_principals
K/M@MAISON.MRS
chris/admin@MAISON.MRS
chris@MAISON.MRS
kadmin/admin@MAISON.MRS
kadmin/changepw@MAISON.MRS
kadmin/history@MAISON.MRS
kadmin/kerberos.maison.mrs@MAISON.MRS
krbtgt/MAISON.MRS@MAISON.MRS

Nous retrouvons bien les deux principaux que nous avons créés, avec en plus quelques autres, créés avec la base de données du royaume.

Si vous avez bien suivi, vous devez pouvoir répondre correctement à la question suivante : Est-ce que la commande kadmin -p chris va permettre de créer de nouveaux principaux ? Essayez pour voir si vous avez « juste ».

Bon. Le royaume existe, dispose d'un administrateur (chris/admin) et d'un sujet (chris). Pour l'instant, c'est une petite usine. Nous allons maintenant ajouter dans le royaume une simple station de travail, avec les outils nécessaires pour obtenir une authentification Kerberos depuis notre « KDC ».

6 - La station de travail

Nous l'avons vu, il suffit d'y installer le paquet krb5-user.

Ensuite, nous copions dessus le fichier /etc/krb5.conf que nous avons créé sur le KDC, sans rien y modifier, du moins pour l'instant.

6-1 - Vérifications diverses

Enfin, il nous suffit de vérifier que tout ça fait le boulot :

root@pchris:~# kinit -V chris
Password for chris@MAISON.MRS:
Authenticated to Kerberos v5

On dirait que oui. Vérification avec klist :

root@pchris:~# klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: chris@MAISON.MRS

Valid starting     Expires            Service principal
02/13/10 17:31:13  02/14/10 03:31:13  krbtgt/MAISON.MRS@MAISON.MRS
renew until 02/14/10 17:31:10

Le « Service principal » krbtgt/MAISON.MRS@MAISON.MRS, nous savons maintenant d'où il vient. Il indique ici que chris dispose d'un ticket d'accès au service « ticket granting tickets ». Encore un peu de patience, il nous faut l'usine complète pour en comprendre tout le sens.

root@pchris:~# kdestroy
root@pchris:~# klist
klist: No credentials cache found (ticket cache FILE:/tmp/krb5cc_0)

Destruction des tickets obtenus.

root@pchris:~# kadmin -p chris/admin
Authenticating as principal chris/admin with password.
Password for chris/admin@MAISON.MRS:
kadmin:  list_principals
K/M@MAISON.MRS
chris/admin@MAISON.MRS
chris@MAISON.MRS
kadmin/admin@MAISON.MRS
kadmin/changepw@MAISON.MRS
kadmin/history@MAISON.MRS
kadmin/kerberos.maison.mrs@MAISON.MRS
krbtgt/MAISON.MRS@MAISON.MRS
kadmin:  quit
root@pchris:~#

La commande kadmin fonctionne correctement depuis le client. Tout va bien.

C'est peut-être le moment de commencer à s'intéresser de près à ce qu'il se passe lorsque l'on invoque la commande kinit ? Voyons ça avec notre wireshark(2) habituel.

6-2 - Le kinit

No.     Time        Source                Destination           Protocol Info
1 0.000000    pchris.maison.mrs     kerberos.maison.mrs   KRB5     AS-REQ
2 0.000590    kerberos.maison.mrs   pchris.maison.mrs     KRB5     KRB Error: KRB5KDC_ERR_PREAUTH_REQUIRED
3 2.763186    pchris.maison.mrs     kerberos.maison.mrs   KRB5     AS-REQ
4 2.766187    kerberos.maison.mrs   pchris.maison.mrs     KRB5     AS-REP

AS-REQ, on comprend. C'est une requête au serveur d'authentification. Mais il n'aime pas, il veut une préauthentification. Il faut regarder plus en détail.

Frame 1 (211 bytes on wire, 211 bytes captured)
...
Kerberos AS-REQ
Pvno: 5
MSG Type: AS-REQ (10)
KDC_REQ_BODY
Padding: 0
KDCOptions: 00000010 (Renewable OK)
.0.. .... .... .... .... .... .... .... = Forwardable: Do NOT use forwardable tickets
..0. .... .... .... .... .... .... .... = Forwarded: This is NOT a forwarded ticket
...0 .... .... .... .... .... .... .... = Proxiable: Do NOT use proxiable tickets
.... 0... .... .... .... .... .... .... = Proxy: This ticket has NOT been proxied
.... .0.. .... .... .... .... .... .... = Allow Postdate: We do NOT allow the ticket to be postdated
.... ..0. .... .... .... .... .... .... = Postdated: This ticket is NOT postdated
.... .... 0... .... .... .... .... .... = Renewable: This ticket is NOT renewable
.... .... ...0 .... .... .... .... .... = Opt HW Auth: False
.... .... .... ..0. .... .... .... .... = Constrained Delegation: This is a normal request (no constrained delegation)
.... .... .... ...0 .... .... .... .... = Canonicalize: This is NOT a canonicalized ticket request
.... .... .... .... .... .... ..0. .... = Disable Transited Check: Transited checking is NOT disabled
.... .... .... .... .... .... ...1 .... = Renewable OK: We accept RENEWED tickets
.... .... .... .... .... .... .... 0... = Enc-Tkt-in-Skey: Do NOT encrypt the tkt inside the skey
.... .... .... .... .... .... .... ..0. = Renew: This is NOT a request to renew a ticket
.... .... .... .... .... .... .... ...0 = Validate: This is NOT a request to validate a postdated ticket
Client Name (Principal): chris
Name-type: Principal (1)
Name: chris
Realm: MAISON.MRS
Server Name (Unknown): krbtgt/MAISON.MRS
Name-type: Unknown (0)
Name: krbtgt
Name: MAISON.MRS
from: 2010-02-13 19:09:50 (UTC)
till: 2010-02-14 19:09:50 (UTC)
Nonce: 140992433
Encryption Types: aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 rc4-hmac des-cbc-crc des-cbc-md5 des-cbc-md4
Encryption type: aes256-cts-hmac-sha1-96 (18)
Encryption type: aes128-cts-hmac-sha1-96 (17)
Encryption type: des3-cbc-sha1 (16)
Encryption type: rc4-hmac (23)
Encryption type: des-cbc-crc (1)
Encryption type: des-cbc-md5 (3)
Encryption type: des-cbc-md4 (2)

L'utilisateur présente le nom de son principal, en indiquant dans quel royaume (MAISON.MRS) et à quel type de ticket (TGT) il demande. Il indique également quels algorithmes de chiffrement il sait manipuler.

Il n'y a aucune partie chiffrée dans cette requête, il n'y a pas non plus de mot de passe. Très simple, donc.

Oui mais voilà, le cerbère ne l'entend pas de cette oreille (ni des 5 autres).

Frame 2 (297 bytes on wire, 297 bytes captured)
...
Kerberos KRB-ERROR
Pvno: 5
MSG Type: KRB-ERROR (30)
ctime: 1974-06-20 20:33:53 (UTC)
stime: 2010-02-13 19:09:50 (UTC)
susec: 737204
error_code: KRB5KDC_ERR_PREAUTH_REQUIRED (25)
Client Realm: MAISON.MRS
Client Name (Principal): chris
Name-type: Principal (1)
Name: chris
Realm: MAISON.MRS
Server Name (Unknown): krbtgt/MAISON.MRS
Name-type: Unknown (0)
Name: krbtgt
Name: MAISON.MRS
e-text: NEEDED_PREAUTH
e-data
padata: PA-ENC-TIMESTAMP Unknown:136 PA-ENCTYPE-INFO2 PA-SAM-RESPONSE Unknown:133
Type: PA-ENC-TIMESTAMP (2)
Value:
Type: Unknown (136)
Value:
Type: PA-ENCTYPE-INFO2 (19)
Value: 30153005A0030201123005A0030201173005A003020110 aes256-cts-hmac-sha1-96 rc4-hmac des3-cbc-sha1
Encryption type: aes256-cts-hmac-sha1-96 (18)
Encryption type: rc4-hmac (23)
Encryption type: des3-cbc-sha1 (16)
Type: PA-SAM-RESPONSE (13)
Value:
Type: Unknown (133)
Value: 4D4954

Ce n'est pas une vraie erreur, c'est juste que l'AS désire recevoir une préauthentification. Voyez les RFC idoines si vous voulez vraiment aller tout au fond des choses :

The ETYPE-INFO2 pre-authentication type is sent by the KDC in a KRB-ERROR indicating a requirement for additional pre-authentication. It is usually used to notify a client of which key to use for the encryption of an encrypted timestamp for the purposes of sending a PA-ENC-TIMESTAMP pre-authentication value.

Bref, le client s'exécute :

Frame 3 (310 bytes on wire, 310 bytes captured)
...
Kerberos AS-REQ
Pvno: 5
MSG Type: AS-REQ (10)
padata: Unknown:133 PA-ENC-TIMESTAMP
Type: Unknown (133)
Value: 4D4954
Type: PA-ENC-TIMESTAMP (2)
Value: 3041A003020112A23A0438EAB48D41FF4F470D1BB255E7D7... aes256-cts-hmac-sha1-96
Encryption type: aes256-cts-hmac-sha1-96 (18)
enc PA_ENC_TIMESTAMP: EAB48D41FF4F470D1BB255E7D7D9A51F0D978D1B41945E89...
KDC_REQ_BODY
Padding: 0
KDCOptions: 00000010 (Renewable OK)
.0.. .... .... .... .... .... .... .... = Forwardable: Do NOT use forwardable tickets
..0. .... .... .... .... .... .... .... = Forwarded: This is NOT a forwarded ticket
...0 .... .... .... .... .... .... .... = Proxiable: Do NOT use proxiable tickets
.... 0... .... .... .... .... .... .... = Proxy: This ticket has NOT been proxied
.... .0.. .... .... .... .... .... .... = Allow Postdate: We do NOT allow the ticket to be postdated
.... ..0. .... .... .... .... .... .... = Postdated: This ticket is NOT postdated
.... .... 0... .... .... .... .... .... = Renewable: This ticket is NOT renewable
.... .... ...0 .... .... .... .... .... = Opt HW Auth: False
.... .... .... ..0. .... .... .... .... = Constrained Delegation: This is a normal request (no constrained delegation)
.... .... .... ...0 .... .... .... .... = Canonicalize: This is NOT a canonicalized ticket request
.... .... .... .... .... .... ..0. .... = Disable Transited Check: Transited checking is NOT disabled
.... .... .... .... .... .... ...1 .... = Renewable OK: We accept RENEWED tickets
.... .... .... .... .... .... .... 0... = Enc-Tkt-in-Skey: Do NOT encrypt the tkt inside the skey
.... .... .... .... .... .... .... ..0. = Renew: This is NOT a request to renew a ticket
.... .... .... .... .... .... .... ...0 = Validate: This is NOT a request to validate a postdated ticket
Client Name (Principal): chris
Name-type: Principal (1)
Name: chris
Realm: MAISON.MRS
Server Name (Unknown): krbtgt/MAISON.MRS
Name-type: Unknown (0)
Name: krbtgt
Name: MAISON.MRS
from: 2010-02-13 19:09:50 (UTC)
till: 2010-02-14 19:09:50 (UTC)
Nonce: 140992433
Encryption Types: aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 rc4-hmac des-cbc-crc des-cbc-md5 des-cbc-md4
Encryption type: aes256-cts-hmac-sha1-96 (18)
Encryption type: aes128-cts-hmac-sha1-96 (17)
Encryption type: des3-cbc-sha1 (16)
Encryption type: rc4-hmac (23)
Encryption type: des-cbc-crc (1)
Encryption type: des-cbc-md5 (3)
Encryption type: des-cbc-md4 (2)

Finalement , le cerbère accorde son TGT au client :

Frame 4 (711 bytes on wire, 711 bytes captured)
...
Kerberos AS-REP
Pvno: 5
MSG Type: AS-REP (11)
padata: PA-ENCTYPE-INFO2
Type: PA-ENCTYPE-INFO2 (19)
Value: 30073005A003020112 aes256-cts-hmac-sha1-96
Encryption type: aes256-cts-hmac-sha1-96 (18)
Client Realm: MAISON.MRS
Client Name (Principal): chris
Name-type: Principal (1)
Name: chris
Ticket
Tkt-vno: 5
Realm: MAISON.MRS
Server Name (Unknown): krbtgt/MAISON.MRS
Name-type: Unknown (0)
Name: krbtgt
Name: MAISON.MRS
enc-part aes256-cts-hmac-sha1-96
Encryption type: aes256-cts-hmac-sha1-96 (18)
Kvno: 1
enc-part: 19E7D3E7337658EFC983B6221B1F4BC80C8E7AB16003E6E9...
enc-part aes256-cts-hmac-sha1-96
Encryption type: aes256-cts-hmac-sha1-96 (18)
enc-part: D41400F6E3A44CD883DA34BEE71F312519890F9FF2A053CC...

The ETYPE-INFO2 MAY also be sent in an AS-REP to provide information to the client about which key salt to use for the string-to-key to be used by the client to obtain the key for decrypting the encrypted part the AS-REP.

Voilà qui ne manque pas de sel.

7 - Configuration d'apache-krb

Le serveur apache-krb.maison.mrs va fournir un service HTTP nécessitant une authentification Kerberos. Nous allons donc installer ce qui est nécessaire :

- un système de synchronisation de l'horloge (NTP) ;

- un serveur Apache classique ;

- les composants Kerberos nécessaires.

7-1 - Installation d'Apache

Nous installons un Apache « prefork » classique :

apache-krb:~# aptitude install apache2-mpm-prefork
---
Enabling site default.
Enabling module alias.
Enabling module autoindex.
Enabling module dir.
Enabling module env.
Enabling module mime.
Enabling module negotiation.
Enabling module setenvif.
Enabling module status.
Enabling module auth_basic.
Enabling module deflate.
Enabling module authz_default.
Enabling module authz_user.
Enabling module authz_groupfile.
Enabling module authn_file.
Enabling module authz_host.
...

Mais il nous manque le module spécifique à l'authentification Kerberos : libapache2-mod-auth-kerb.

apache-krb:~# aptitude install libapache2-mod-auth-kerb
...
Les NOUVEAUX paquets suivants vont être installés :
bind9-host{a} geoip-database{a} krb5-config{a} libapache2-mod-auth-kerb
libbind9-50{a} libdns53{a} libgeoip1{a} libisc50{a} libisccc50{a}
libisccfg50{a} liblwres50{a}
0 paquets mis à jour, 11 nouvellement installés, 0 à enlever et 0 non mis à jour.
Il est nécessaire de télécharger 1 805ko d'archives. Après dépaquetage, 4 579ko seront utilisés.
Voulez-vous continuer ? [Y/n/?]
...
Paramétrage de libapache2-mod-auth-kerb (5.3-5+b1) ...
Enabling module auth_kerb.
Run '/etc/init.d/apache2 restart' to activate new configuration!
...

Notez que krb5-config a été installé par dépendance. Des questions sont posées lors de sa configuration, mais peu importe, nous allons récupérer le krb5.conf déjà utilisé sur kerberos.maison.mrs et sur le poste de travail.

Le module auth_kerb a été activé, mais il faut bien sûr indiquer où et comment Apache doit s'en servir.

7-2 - Configuration de Kerberos

7-2-1 - Sur kerberos.maison.mrs

Nous devons créer un « principal » pour notre service HTTP, de façon presque identique à celle utilisée pour créer des principaux d'utilisateurs. Ici, le mot de passe peut être créé de façon aléatoire, car seuls les composants Kerberos concernés devront le connaitre. Nous pouvons réaliser l'opération depuis n'importe quel hôte sur lequel krb5-user a été installé :

root@pchris:/home/chris# kadmin -p chris/admin
Authenticating as principal chris/admin with password.
Password for chris/admin@MAISON.MRS:

kadmin:  addprinc -clearpolicy -randkey HTTP/apache-krb.maison.mrs

Principal "HTTP/apache-krb.maison.mrs@MAISON.MRS" created.

kadmin:  list_principals
HTTP/apache-krb.maison.mrs@MAISON.MRS
K/M@MAISON.MRS
chris/admin@MAISON.MRS
chris@MAISON.MRS
kadmin/admin@MAISON.MRS
kadmin/changepw@MAISON.MRS
kadmin/history@MAISON.MRS
kadmin/kerberos.maison.mrs@MAISON.MRS
krbtgt/MAISON.MRS@MAISON.MRS

kadmin:  quit

Cette opération a permis la création d'une clé secrète qui doit être partagée avec le service HTTP situé sur apache-krb.maison.mrs. C'est grâce à cette clé que l'utilisateur pourra dialoguer en langage Kerberos avec le service HTTP. Souvenez-vous de ce que l'on a vu dans le Principe général.

kadmin va encore une fois nous servir :

kerberos:~# kadmin -p chris/admin
Authenticating as principal chris/admin with password.
Password for chris/admin@MAISON.MRS:
kadmin:  ktadd -k krb5-http.keytab HTTP/apache-krb.maison.mrs
Entry for principal HTTP/apache-krb.maison.mrs with kvno 3, encryption type AES-256 CTS mode with 96-bit SHA-1 HMAC added to keytab WRFILE:krb5-http.keytab.
Entry for principal HTTP/apache-krb.maison.mrs with kvno 3, encryption type ArcFour with HMAC/md5 added to keytab WRFILE:krb5-http.keytab.
Entry for principal HTTP/apache-krb.maison.mrs with kvno 3, encryption type Triple DES cbc mode with HMAC/sha1 added to keytab WRFILE:krb5-http.keytab.
Entry for principal HTTP/apache-krb.maison.mrs with kvno 3, encryption type DES cbc mode with CRC-32 added to keytab WRFILE:krb5-http.keytab.
kadmin:  quit

Nous avons créé dans le répertoire courant un fichier nommé krb5-http.keytab qui contient quatre entrées, fonction des divers algorithmes de chiffrement possibles. La commande klist permet de le vérifier :

kerberos:~# klist -e -k krb5-http.keytab
Keytab name: WRFILE:krb5-http.keytab
KVNO Principal
---- --------------------------------------------------------------------------
3 HTTP/apache-krb.maison.mrs@MAISON.MRS (AES-256 CTS mode with 96-bit SHA-1 HMAC)
3 HTTP/apache-krb.maison.mrs@MAISON.MRS (ArcFour with HMAC/md5)
3 HTTP/apache-krb.maison.mrs@MAISON.MRS (Triple DES cbc mode with HMAC/sha1)
3 HTTP/apache-krb.maison.mrs@MAISON.MRS (DES cbc mode with CRC-32)

Il nous faut déménager par un moyen sécurisé ce fichier sur apache-krb.maison.mrs, par exemple avec ssh :

kerberos:~# scp krb5-http.keytab  root@apache-krb.maison.mrs:/etc/apache2/
...
root@apache-krb.maison.mrs's password:
krb5-http.keytab                            100%  322     0.3KB/s   00:00
kerberos:~# rm krb5-http.keytab

Nous aurions également pu utiliser la commande kadmin directement sur apache-krb.maison.mrs pour obtenir le keytab sur place.

7-2-2 - Sur apache-krb

Première chose à faire, rendre le « keytab » lisible par www-data :

apache-krb:/etc/apache2# chown www-data:www-data krb5-http.keytab
apache-krb:/etc/apache2# chmod 400 krb5-http.keytab
apache-krb:/etc/apache2# ls -l krb5-http.keytab
-r-------- 1 www-data www-data 322 févr. 15 10:55 krb5-http.keytab

7-3 - Configuration d'Apache

Nous allons maintenant modifier la configuration du serveur par défaut, pour nécessiter l'authentification Kerberos :

apache-krb:/etc/apache2# cat sites-available/default
<VirtualHost *:80>
ServerAdmin webmaster@localhost

DocumentRoot /var/www
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
AuthName "Secure Access"
AuthType Kerberos
Krb5Keytab /etc/apache2/krb5-http.keytab
KrbMethodK5Passwd off
KrbSaveCredentials on
require valid-user

AllowOverride None
Order allow,deny
allow from all
</Directory>
...
</VirtualHost>

Pour les diverses options de configuration disponibles pour le module auth_kerb, je vous invite à visiter la documentation (succincte) disponible sur le site du projet ou de lire le fichier (non moins succinct) /usr/share/doc/libapache2-mod-auth-kerb/README.gz.

L'option KrbMethodK5Passwd off fait que si l'utilisateur ne dispose pas déjà de son TGT, les portes du service lui sont irrémédiablement fermées.

7-3-1 - Configuration du client

Il n'y a pas grand-chose à faire si ce n'est de s'assurer que le navigateur web sait gérer la négociation Kerberos. Au moins Firefox sait le faire, mais sa configuration par défaut ne le permet peut-être pas. Le mieux est de s'en assurer en allant sur l'URI about:config et en s'assurant que l'on a quelque chose de la forme :

network.negotiate-auth.trusted-uris;https://,http://

Ici, toute demande d'URI faisant référence aux protocoles HTTP et HTTPS sera autorisée à effectuer la négociation Kerberos.

7-3-2 - Vérifions

Un petit coup de wireshark ne sera pas du luxe.

7-3-2-1 - Dans une console

Obtention du TGT :

chris@pchris:~$ kinit chris
Password for chris@MAISON.MRS:

7-3-2-2 - Dans Firefox

http://apache-krb.maison.mrs

Ça marche, on le sait, on a déjà fait.

7-3-2-3 - Re dans la console

chris@pchris:~$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: chris@MAISON.MRS

Valid starting     Expires            Service principal
02/15/10 11:45:27  02/15/10 21:45:27  krbtgt/MAISON.MRS@MAISON.MRS
renew until 02/16/10 11:45:22
02/15/10 11:45:42  02/15/10 21:45:27  HTTP/apache-krb.maison.mrs@MAISON.MRS
renew until 02/16/10 11:45:22

Nous constatons que désormais nous avons bien, en plus du TGT, le ticket d'accès au service HTTP sur apache-krb.maison.mrs.

7-3-2-4 - Le snif

Nous avons pu capturer ce qu'il s'est passé et nous voyons ceci (débarrassé des paquets ARP, NTP et autres TCP inutiles à la compréhension) :

No.     Time        Source                Destination           Protocol Info
...
3 0.000209    192.168.0.16          192.168.0.133         KRB5     AS-REQ
4 0.000811    192.168.0.133         192.168.0.16          KRB5     KRB Error: KRB5KDC_ERR_PREAUTH_REQUIRED
...
7 5.152873    192.168.0.16          192.168.0.133         KRB5     AS-REQ
8 5.154053    192.168.0.133         192.168.0.16          KRB5     AS-REP
...
16 19.545409   192.168.0.16          192.168.0.134         HTTP     GET / HTTP/1.1
...
18 19.547868   192.168.0.134         192.168.0.16          HTTP     HTTP/1.1 401 Authorization Required  (text/html)
...
20 19.562570   192.168.0.16          192.168.0.133         KRB5     TGS-REQ
21 19.564939   192.168.0.133         192.168.0.16          KRB5     TGS-REP
22 19.566770   192.168.0.16          192.168.0.134         HTTP     GET / HTTP/1.1

Les trames 3 à 8 correspondent à la demande et à l'obtention du TGT (commande kinit), ce n'est pas nouveau.

La trame 16 correspond à une demande HTTP ingénue de la part du client. La trame 18 correspond à l'erreur 401 Authorization Required. Dans la réponse HTTP, il n'y a rien de plus que le HTML normalement destiné à afficher l'erreur, comme nous l'avons vu au début de ce chapitre :

Frame 18 (719 bytes on wire, 719 bytes captured)
...
Hypertext Transfer Protocol
HTTP/1.1 401 Authorization Required\r\n
[Expert Info (Chat/Sequence): HTTP/1.1 401 Authorization Required\r\n]
[Message: HTTP/1.1 401 Authorization Required\r\n]
[Severity level: Chat]
[Group: Sequence]
Request Version: HTTP/1.1
Response Code: 401
Date: Mon, 15 Feb 2010 10:45:42 GMT\r\n
Server: Apache/2.2.14 (Debian)\r\n
WWW-Authenticate: Negotiate\r\n
Vary: Accept-Encoding\r\n
Content-Encoding: gzip\r\n
Content-Length: 346\r\n
[Content length: 346]
Keep-Alive: timeout=15, max=100\r\n
Connection: Keep-Alive\r\n
Content-Type: text/html; charset=iso-8859-1\r\n
\r\n
Content-encoded entity body (gzip): 346 bytes -> 488 bytes
Line-based text data: text/html
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\n
<html><head>\n
<title>401 Authorization Required</title>\n
</head><body>\n
<h1>Authorization Required</h1>\n
<p>This server could not verify that you\n
are authorized to access the document\n
requested.  Either you supplied the wrong\n
credentials (e.g., bad password), or your\n
browser doesn't understand how to supply\n
the credentials required.</p>\n
<hr>\n
<address>Apache/2.2.14 (Debian) Server at apache-krb.maison.mrs Port 80</address>\n
</body></html>\n

Notre renard de feu, rusé par nature et qui de plus est averti que dans ce cas-là il doit aller regarder du côté de Kerberos, va constater la présence du TGT de chris et lancer une demande de ticket (TGS-REQ) dans la trame 20 :

Frame 20 (761 bytes on wire, 761 bytes captured)
...
Kerberos TGS-REQ
Pvno: 5
MSG Type: TGS-REQ (12)
padata: PA-TGS-REQ
Type: PA-TGS-REQ (1)
Value: 6E82022730820223A003020105A10302010EA20703050000... AP-REQ
Pvno: 5
MSG Type: AP-REQ (14)
Padding: 0
APOptions: 00000000
.0.. .... .... .... .... .... .... .... = Use Session Key: Do NOT use the session key to encrypt the ticket
..0. .... .... .... .... .... .... .... = Mutual required: Mutual authentication is NOT required
Ticket
Tkt-vno: 5
Realm: MAISON.MRS
Server Name (Unknown): krbtgt/MAISON.MRS
Name-type: Unknown (0)
Name: krbtgt
Name: MAISON.MRS
enc-part aes256-cts-hmac-sha1-96
Encryption type: aes256-cts-hmac-sha1-96 (18)
Kvno: 1
enc-part: 50AB88CDE4D868AACA1BDD95C155FCD1BB24764BF57551E1...
Authenticator aes256-cts-hmac-sha1-96
Encryption type: aes256-cts-hmac-sha1-96 (18)
Authenticator data: 2C7936EF34EF6EB1AB7BE53F70E870987591510EB7B0FC90...
KDC_REQ_BODY
Padding: 0
KDCOptions: 00810000 (Renewable, Canonicalize)
.0.. .... .... .... .... .... .... .... = Forwardable: Do NOT use forwardable tickets
..0. .... .... .... .... .... .... .... = Forwarded: This is NOT a forwarded ticket
...0 .... .... .... .... .... .... .... = Proxiable: Do NOT use proxiable tickets
.... 0... .... .... .... .... .... .... = Proxy: This ticket has NOT been proxied
.... .0.. .... .... .... .... .... .... = Allow Postdate: We do NOT allow the ticket to be postdated
.... ..0. .... .... .... .... .... .... = Postdated: This ticket is NOT postdated
.... .... 1... .... .... .... .... .... = Renewable: This ticket is RENEWABLE
.... .... ...0 .... .... .... .... .... = Opt HW Auth: False
.... .... .... ..0. .... .... .... .... = Constrained Delegation: This is a normal request (no constrained delegation)
.... .... .... ...1 .... .... .... .... = Canonicalize: This is a request for a CANONICALIZED ticket
.... .... .... .... .... .... ..0. .... = Disable Transited Check: Transited checking is NOT disabled
.... .... .... .... .... .... ...0 .... = Renewable OK: We do NOT accept renewed tickets
.... .... .... .... .... .... .... 0... = Enc-Tkt-in-Skey: Do NOT encrypt the tkt inside the skey
.... .... .... .... .... .... .... ..0. = Renew: This is NOT a request to renew a ticket
.... .... .... .... .... .... .... ...0 = Validate: This is NOT a request to validate a postdated ticket
Realm: MAISON.MRS
Server Name (Service and Host): HTTP/apache-krb.maison.mrs
Name-type: Service and Host (3)
Name: HTTP
Name: apache-krb.maison.mrs
till: 2010-02-15 20:45:27 (UTC)
Nonce: 1266230742
Encryption Types: aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 rc4-hmac des-cbc-crc des-cbc-md5 des-cbc-md4
Encryption type: aes256-cts-hmac-sha1-96 (18)
Encryption type: aes128-cts-hmac-sha1-96 (17)
Encryption type: des3-cbc-sha1 (16)
Encryption type: rc4-hmac (23)
Encryption type: des-cbc-crc (1)
Encryption type: des-cbc-md5 (3)
Encryption type: des-cbc-md4 (2)
Le serveur kerberos va répondre dans la trame 21 :
Frame 21 (744 bytes on wire, 744 bytes captured)
...
Kerberos TGS-REP
Pvno: 5
MSG Type: TGS-REP (13)
Client Realm: MAISON.MRS
Client Name (Principal): chris
Name-type: Principal (1)
Name: chris
Ticket
Tkt-vno: 5
Realm: MAISON.MRS
Server Name (Service and Host): HTTP/apache-krb.maison.mrs
Name-type: Service and Host (3)
Name: HTTP
Name: apache-krb.maison.mrs
enc-part aes256-cts-hmac-sha1-96
Encryption type: aes256-cts-hmac-sha1-96 (18)
Kvno: 3
enc-part: 726E9E662C728E522451A0E630596656899C08CFF04F6F04...
enc-part aes256-cts-hmac-sha1-96
Encryption type: aes256-cts-hmac-sha1-96 (18)
enc-part: 7FEDC49B008108F3E775207CB937C1DE83828023F9FCFE54...

Et notre renard va reformuler sa requête avec cette fois-ci ce qu'il faut dedans pour satisfaire l'indien dans la trame 22 :

Frame 22 (1504 bytes on wire, 1504 bytes captured)
...
Hypertext Transfer Protocol
GET / HTTP/1.1\r\n
[Expert Info (Chat/Sequence): GET / HTTP/1.1\r\n]
[Message: GET / HTTP/1.1\r\n]
[Severity level: Chat]
[Group: Sequence]
Request Method: GET
Request URI: /
Request Version: HTTP/1.1
Host: apache-krb.maison.mrs\r\n
User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.1.7) Gecko/20100106 Ubuntu/9.10 (karmic) Firefox/3.5.7\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Accept-Language: fr-fr,fr;q=0.8,en;q=0.5,en-us;q=0.3\r\n
Accept-Encoding: gzip,deflate\r\n
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n
Keep-Alive: 300\r\n
Connection: keep-alive\r\n
If-Modified-Since: Mon, 15 Feb 2010 08:38:55 GMT\r\n
If-None-Match: "45d72-b1-47f9f8c2b05c0"\r\n
[truncated] Authorization: Negotiate YIICqAYGKwYBBQUCoIICnDCCApigHzAdBgkqhkiG9xIBAgIGBSsFAQUCBgkqhkiC9xIBAgKiggJzBIICb2CCAmsGCSqGSIb3EgECAgEAboICWjCCAlagAwIBBaEDAgEOogcDBQAAAAAAo4IBb2GCAWswggFnoAMCAQWhDBsKTUFJU09OLk1SU6IoMCagAwIBA6EfMB0bBE
GSS-API Generic Security Service Application Program Interface
OID: 1.3.6.1.5.5.2 (SPNEGO - Simple Protected Negotiation)
SPNEGO
negTokenInit
mechTypes: 3 items
MechType: 1.2.840.113554.1.2.2 (KRB5 - Kerberos 5)
MechType: 1.3.5.1.5.2 (SNMPv2-SMI::org.5.1.5.2)
MechType: 1.2.840.48018.1.2.2 (MS KRB5 - Microsoft Kerberos 5)
mechToken: 6082026B06092A864886F71201020201006E82025A308202...
krb5_blob: 6082026B06092A864886F71201020201006E82025A308202...
KRB5 OID: 1.2.840.113554.1.2.2 (KRB5 - Kerberos 5)
krb5_tok_id: KRB5_AP_REQ (0x0001)
Kerberos AP-REQ
Pvno: 5
MSG Type: AP-REQ (14)
Padding: 0
APOptions: 00000000
.0.. .... .... .... .... .... .... .... = Use Session Key: Do NOT use the session key to encrypt the ticket
..0. .... .... .... .... .... .... .... = Mutual required: Mutual authentication is NOT required
Ticket
Tkt-vno: 5
Realm: MAISON.MRS
Server Name (Service and Host): HTTP/apache-krb.maison.mrs
Name-type: Service and Host (3)
Name: HTTP
Name: apache-krb.maison.mrs
enc-part aes256-cts-hmac-sha1-96
Encryption type: aes256-cts-hmac-sha1-96 (18)
Kvno: 3
enc-part: 726E9E662C728E522451A0E630596656899C08CFF04F6F04...
Authenticator aes256-cts-hmac-sha1-96
Encryption type: aes256-cts-hmac-sha1-96 (18)
Authenticator data: FAC8A2377494B396884927BE3726631FA66EFD41C98DC227...
\r\n

Voilà, c'est aussi compliqué que ça, mais ça fonctionne quand même.

8 - Compléments

Un serveur HTTP kerbérisé, c'est bien pour la démo, mais dans ce cas, Kerberos ne prend en charge que l'authentification, tout le reste du dialogue HTTP sera en clair. Dans la pratique, HTTPS sera sans doute plus indiqué pour un site sensible. Il suffit de transformer son serveur HTTP en HTTPS, ce n'est pas très compliqué et ce n'est pas l'objet de ce chapitre.

Mais on peut utiliser Kerberos pour d'autres applications. Il peut être aussi intéressant de centraliser les comptes d'utilisateurs et d'utiliser pam avec le module libpam-krb5, évitant ainsi à l'utilisateur de manipuler la commande kinit.

8-1 - Pam et Kerberos

Cette autre usine qu'est PAM permet d'employer un module spécifique à Kerberos. Sur Debian (et donc Ubuntu), la simple installation du paquet libpam-krb5 va modifier la configuration de PAM pour prendre en charge l'authentification Kerberos.

Pour les spécialistes de PAM (ce que j'aimerais bien arriver à devenir un jour), voici, pour une Ubuntu « karmic », les modifications apportées, dans common-auth :

auth    [success=2 default=ignore]      pam_krb5.so  minimum_uid=1000
auth    [success=1 default=ignore]      pam_unix.so nullok_secure try_first_pass
auth    requisite                       pam_deny.so
auth    required                        pam_permit.so

Dans common-password :

password        requisite                       pam_krb5.so minimum_uid=1000
password        [success=1 default=ignore]      pam_unix.so obscure use_authtok try_first_pass sha512
password        requisite                       pam_deny.so
password        required                        pam_permit.so
password        optional        pam_gnome_keyring.so

Dans common-session :

session [default=1]                     pam_permit.so
session requisite                       pam_deny.so
session required                        pam_permit.so
session optional                        pam_krb5.so minimum_uid=1000
session required        pam_unix.so
session optional                        pam_ck_connector.so nox11auth    [success=2 default=ignore]      pam_krb5.so  minimum_uid=1000
auth    [success=1 default=ignore]      pam_unix.so nullok_secure try_first_pass
auth    requisite                       pam_deny.so
auth    required                        pam_permit.so

Dans common-password :

password        requisite                       pam_krb5.so minimum_uid=1000
password        [success=1 default=ignore]      pam_unix.so obscure use_authtok try_first_pass sha512
password        requisite                       pam_deny.so
password        required                        pam_permit.so
password        optional        pam_gnome_keyring.so

Dans common-session :

session [default=1]                     pam_permit.so
session requisite                       pam_deny.so
session required                        pam_permit.so
session optional                        pam_krb5.so minimum_uid=1000
session required        pam_unix.so
session optional                        pam_ck_connector.so nox11

Dans common-session-noninterarctive :

session [default=1]                     pam_permit.so
session requisite                       pam_deny.so
session required                        pam_permit.so
session optional                        pam_krb5.so minimum_uid=1000
session required        pam_unix.so

Et dans common-account :

account [success=1 new_authtok_reqd=done default=ignore]        pam_unix.so
account requisite                       pam_deny.so
account required                        pam_permit.so
account required                        pam_krb5.so minimum_uid=1000

Il est nécessaire que l'utilisateur puisse ouvrir une session en s'appuyant sur un mot de passe local, faute de quoi, une indisponibilité du KDC lui empêcherait tout accès à son compte.

Cette configuration présente certaines imperfections qui peuvent amener des comportements désagréables !

- L'utilisateur doit disposer d'un compte local. S'il ne dispose que d'un principal Kerberos, il ne pourra pas ouvrir de session sur la station de travail. Kerberos ne réalise que l'authentification, les autres informations nécessaires nécessiteraient par exemple LDAP en appui.

- Un utilisateur change son mot de passe avec passwd :

- le mot de passe Kerberos sera également changé, mais si l'utilisateur a choisi un mot de passe trivial, la sécurité par défaut de la karmic va faire échouer le changement du pass local ;

- l'utilisateur va se retrouver avec deux pass pour ouvrir une session :

- l'ancien ouvrira une session avec pam_unix,

- le nouveau le fera avec pam_krb5 ;

- dans tous les cas, si l'utilisateur dispose de comptes sur d'autres stations de travail, les mots de passe locaux resteront bien entendu ce qu'ils étaient et l'utilisateur se retrouvera dans le cas précédent, sur ces autres stations ;

- si les pass sont désynchronisés, l'utilisateur ne sait pas par quel moyen il a ouvert une session.

- Un utilisateur change son pass avec kpasswd :

- seul le mot de passe Kerberos est changé, bien entendu, pass Kerberos et pass local ne sont plus synchronisés.

Vérifions tout de même, dans le cas où c'est Kerberos qui a authentifié l'utilisateur. Ce dernier a juste ouvert une session avec l'écran de login de gdm, sans utiliser par la suite la commande kinit. La commande klist donne :

chris@karmicvirt:~$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: chris@MAISON.MRS

Valid starting     Expires            Service principal
02/19/10 18:32:35  02/20/10 04:32:35  krbtgt/MAISON.MRS@MAISON.MRS
renew until 02/20/10 18:32:32

Le TGT a bien été obtenu de façon transparente. Notons que ça ne fonctionne pas avec la connexion automatique à un compte par défaut. Comme cette pratique n'est pas recommandable dans un environnement « peu sûr » (c'est-à-dire dans tous les cas), nous ne pousserons pas plus avant les investigations.

8-2 - Proxy Squid

Le proxy Squid peut utiliser l'authentification Kerberos. Squid est traité au chapitre Squid et SquidGuard, nous ne reviendrons pas sur les détails. Dans ce chapitre, nous avons utilisé NTLM pour permettre d'authentifier les utilisateurs à travers Active Directory. NTLM, c'est pas top. En plus, cette chose nécessite deux « access denied » avant de réagir. Consommation de bande passante et encombrement des logs inutiles.

Squid3, fourni avec Debian Squeeze, propose un « helper » : squid_kerb_auth pour Kerberos qui se manipule un peu de la même manière que ntlm_auth pour NTLM.

Avantages :

- pas besoin d'utiliser Samba ni winbind, Kerberos ne fait pas partie de CIFS ;

- l'authentification est immédiate, économie de bande passante et de volume de logs ;

- meilleure lisibilité des logs de Squid, par le fait.

Inconvénient :

- si l'utilisateur ne dispose pas d'un TGT, le navigateur ne lui proposera pas une fenêtre de connexion son accès sera interdit tout simplement.

Voici rapidement comment faire.

8-2-1 - Le KDC

D'abord, il faut réaliser un principal HTTP/<fqdn>@REALM comme on l'a fait pour Apache, puis exporter la clé dans un fichier qui ne sera lisible que par proxy (nous sommes sur une Debian), également de la manière dont nous l'avons fait pour Apache. Nous obtenons par exemple ceci :

/etc/squid3# ls -l

...
-r-------- 1 proxy proxy    322 févr. 18 12:28 krb5-proxy.keytab
...

8-2-2 - squid.conf

Voici un squid.conf de base :

auth_param negotiate program /usr/lib/squid3/squid_kerb_auth -d
auth_param negotiate children 10
auth_param negotiate keep_alive on
acl auth proxy_auth REQUIRED

acl manager proto cache_object
acl localhost src 127.0.0.1/32
acl localnet src 192.168.0.0/24
acl to_localhost dst 127.0.0.0/8 0.0.0.0/32
acl SSL_ports port 443
acl Safe_ports port 80          # http
acl Safe_ports port 21          # ftp
acl Safe_ports port 443         # https
acl Safe_ports port 70          # gopher
acl Safe_ports port 210         # wais
acl Safe_ports port 1025-65535  # unregistered ports
acl Safe_ports port 280         # http-mgmt
acl Safe_ports port 488         # gss-http
acl Safe_ports port 591         # filemaker
acl Safe_ports port 777         # multiling http
acl CONNECT method CONNECT
http_access allow manager localhost
http_access deny manager
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost
http_access allow localnet auth
http_access deny all
icp_access deny all
htcp_access deny all
http_port 3128
hierarchy_stoplist cgi-bin ?
access_log /var/log/squid3/access.log squid
refresh_pattern ^ftp:           1440    20%     10080
refresh_pattern ^gopher:        1440    0%      1440
refresh_pattern (cgi-bin|\?)    0       0%      0
refresh_pattern .               0       20%     4320
icp_port 3130
coredump_dir /var/spool/squid3

8-2-3 - Environnement de Squid

Squid doit être démarré avec à disposition une variable d'environnement indiquant le chemin d'accès à ce fichier de clés. Débian étant une distribution sérieuse, c'est assez facile à faire en créant un fichier /etc/default/squid3 contenant ceci :

KRB5_KTNAME=/etc/squid3/krb5-proxy.keytab
export KRB5_KTNAME

Ce fichier, s'il existe, est intégré à /etc/init.d/squid3. Il suffit alors de relancer squid.

8-3 - Et encore…

Bien d'autres applications peuvent utiliser Kerberos, grâce à GSSAPI. Postfix, Dovecot, Samba, etc. La fonction SSO étant probablement la plus intéressante, et Microsoft ne s'y est pas trompé, en intégrant Kerberos à son système Active Directory. Ce que l'on peut regretter, c'est la façon opaque dont ça a été fait, mais c'est une marque de fabrique. Encore qu'un effort évident de documentation ait été mené et que l'interopérabilité entre MS Windows et les systèmes libres Unix a quelques chances de progresser grâce à Kerberos (et LDAP, mais ceci est une autre histoire).

9 - Le Kerberos d'Active Directory

Puisque nous avons, bon gré, mal gré, un domaine Microsoft avec des contrôleurs de domaine Active Directory (Windows 2008), voyons un peu comment profiter du cerbère Microsoft. Nous allons voir ceci au travers de deux exemples :

- le proxy squid, nous le savons maintenant, peut utiliser Kerberos. Nous allons ici utiliser notre base de comptes Active Directory ;

- ssh sait utiliser GSSAPI, il est donc possible de permettre à certains utilisateurs d'ouvrir une session ssh sur des hôtes GNU/Linux (ou autres Unix like) en exploitant le SSO de Kerberos. Ceci est juste pour l'exemple. En effet, sans l'aide de LDAP, l'opération ne présente qu'un intérêt didactique, les utilisateurs devant disposer également d'un compte Unix local. Rappelons-nous que Kerberos ne fournit que l'authentification. Les informations concernant l'environnement de l'utilisateur (home, shell, etc.) doivent venir d'une autre source.

C'est le moment maintenant de faire le rapprochement entre ce que nous avons vu avec le KDC du MIT et ce qu'il faut découvrir avec celui de MS Active Directory.

9-1 - L'authentification d'un utilisateur

Cette partie est triviale. Le royaume Kerberos existe et il a le même nom que le domaine Microsoft. Ce domaine privé (sans aucune relation avec l'Internet) s'appelle ici eme.org, si bien que le royaume sera EME.ORG.

Nous prenons un client GNU/Linux au hasard dans notre panoplie d'hôtes virtuels. Il s'agit en l'occurrence d'une Debian Lenny équipée de Gnome. Nous lui installons krb5-client et nous créons un krb5.conf de la forme :

[libdefaults]
default_realm = EME.ORG
dns_lookup_kdc = no
dns_lookup_realm = no
allow_weak_crypto = true
default_keytab_name = /etc/krb5.keytab
; for Windows 2003
;      default_tgs_enctypes = rc4-hmac des-cbc-crc des-cbc-md5
;      default_tkt_enctypes = rc4-hmac des-cbc-crc des-cbc-md5
;      permitted_enctypes = rc4-hmac des-cbc-crc des-cbc-md5

; for Windows 2008 with AES
default_tgs_enctypes = aes256-cts-hmac-sha1-96 rc4-hmac des-cbc-crc des-cbc-md5
default_tkt_enctypes = aes256-cts-hmac-sha1-96 rc4-hmac des-cbc-crc des-cbc-md5
permitted_enctypes = aes256-cts-hmac-sha1-96 rc4-hmac des-cbc-crc des-cbc-md5

[realms]
EME.ORG = {
kdc = urnus.eme.org
kdc = neptune.eme.org
admin_server = uranus.eme.org
admin_server = neptune.eme.org
}

[domain_realm]
.eme.org = EME.ORG
eme.org = EME.ORG

Uranus et Neptune sont les deux contrôleurs de domaine. La directive allow_weak_crypto = true est rendue nécessaire pour permettre aux outils Kerberos du MIT d'utiliser les chiffrements DES considérés actuellement comme trop peu sûrs (mais pas encore par Microsoft, qui n'a pas intégré le triple DES, semble-t-il).

Ceci devrait suffire pour obtenir un TGT. Nous avons sur AD un compte chris, essayons avec :

chris@lennyvirt:~$ kinit -V chris
Password for chris@EME.ORG:
Authenticated to Kerberos v5
chris@lennyvirt:~$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: chris@EME.ORG

Valid starting     Expires            Service principal
03/02/10 11:44:27  03/02/10 21:44:34  krbtgt/EME.ORG@EME.ORG
renew until 03/02/10 21:44:27

Kerberos 4 ticket cache: /tmp/tkt1000
klist: You have no tickets cached

Ça marche.

9-2 - L'enregistrement d'un service

Il nous faut maintenant créer un serveur (GNU/Linux Squeeze) qui va héberger squid3 et aussi ssh, il va s'appeler proxy3.eme.org. Il faudra enregistrer sur le TGS Microsoft cet hôte host/proxy3.eme.org@EME.ORG, ainsi que le service HTTP/proxy3.eme.org@EME.ORG. Sur le TGS du MIT, nous l'avons fait avec la commande kadmin, qui ne donne rien de bon ici. Ce serait en effet trop beau sinon.

Il existe des outils qui devraient arriver au même résultat sur les serveurs Windows 2008, la solution qui me semble la plus simple est d'utiliser les services de Samba qui sait dialoguer avec le Kerberos Microsoft. Soyons clairs, Samba ne sert à rien d'autre ici qu'à intégrer l'hôte proxy3 au domaine Microsoft, créant par là même le principal host/proxy3.eme.org@EME.ORG et de permettre l'emploi du couteau suisse net pour exporter le keytab.

9-2-1 - Samba

Nous installons Samba sur proxy3.eme.org et le configurons comme ceci :

[global]
netbios name = PROXY3
server string = Samba Server
realm = EME.ORG
workgroup = EME2K
security = ADS
password server = 172.16.254.8
kerberos method = dedicated keytab
dedicated keytab file = /etc/krb5.keytab
encrypt passwords = yes
guest account = nobody
log file = /var/log/samba/samba.log
username map = /etc/samba/user.map
socket options = TCP_NODELAY SO_RCVBUF=8192 SO_SNDBUF=8192

Et encore, peut-être pouvons-nous faire encore plus simple…

Le password-server est l'un des contrôleurs AD, il est bien entendu possible de les spécifier tous. La directive Kerberos method est nécessaire pour la récupération du keytab. Notons que cette directive est nouvelle et n'existe sous cette forme que sur Samba version 3.4.5 (nous utilisons Squeeze et non Lenny avec sa version 3.2.5, qui nécessiterait use Kerberos keytab = True).

Côté DNS Microsoft, il vaut mieux enregistrer manuellement notre nouvel hôte avant de l'intégrer au domaine, sans oublier de mettre également à jour la « zone de recherche inversée ». À ce jour, l'intégration d'un hôte via Samba ne gère pas cet enregistrement DNS et produit un message d'erreur, sans gravité, mais désagréable. L'absence de l'enregistrement sur DNS, en revanche n'est pas acceptable, elle empêchera l'exportation du keytab. Il faut donc avoir le DNS Microsoft correctement documenté (Kerberos aime DNS).

Une fois ceci fait, il faut joindre le domaine :

net ads join -U <un administrateur du domaine>

Le message doit clairement indiquer que la procédure a réussi :

proxy3:/etc/samba# net -d=1 ads join -U administrateur
Enter administrateur's password:

Using short domain name -- EME2K
Joined 'PROXY3' to realm 'eme.org'

Le nom de l'hôte doit apparaitre dans le conteneur « Computers » du domaine eme.org dans la mmc « Utilisateurs et ordinateurs Active Directory ».

Il nous faut maintenant récupérer le keytab contenant les principaux de notre hôte :

proxy3:/etc/samba# net ads keytab create -U administrateur
Enter administrateur's password:

Normalement, /etc/krb5.keytab a été généré :

proxy3:/etc/samba# klist -e -k /etc/krb5.keytab
Keytab name: WRFILE:/etc/krb5.keytab
KVNO Principal
---- --------------------------------------------------------------------------
2 host/proxy3.eme.org@EME.ORG (DES cbc mode with CRC-32)
2 host/proxy3.eme.org@EME.ORG (DES cbc mode with RSA-MD5)
2 host/proxy3.eme.org@EME.ORG (ArcFour with HMAC/md5)
2 host/proxy3@EME.ORG (DES cbc mode with CRC-32)
2 host/proxy3@EME.ORG (DES cbc mode with RSA-MD5)
2 host/proxy3@EME.ORG (ArcFour with HMAC/md5)
2 PROXY3$@EME.ORG (DES cbc mode with CRC-32)
2 PROXY3$@EME.ORG (DES cbc mode with RSA-MD5)
2 PROXY3$@EME.ORG (ArcFour with HMAC/md5)

Et voilà le travail. Notez l'absence du triple DES.

Puisque nous y sommes, nous allons générer les principaux pour le service HTTP :

net ads keytab add HTTP -U administrateur

Ceci doit avoir ajouté ce qu'il faut dans le keytab :

proxy3:/etc/samba# klist -e -k /etc/krb5.keytab
Keytab name: WRFILE:/etc/krb5.keytab
KVNO Principal
---- --------------------------------------------------------------------------
2 host/proxy3.eme.org@EME.ORG (DES cbc mode with CRC-32)
2 host/proxy3.eme.org@EME.ORG (DES cbc mode with RSA-MD5)
2 host/proxy3.eme.org@EME.ORG (ArcFour with HMAC/md5)
2 host/proxy3@EME.ORG (DES cbc mode with CRC-32)
2 host/proxy3@EME.ORG (DES cbc mode with RSA-MD5)
2 host/proxy3@EME.ORG (ArcFour with HMAC/md5)
2 PROXY3$@EME.ORG (DES cbc mode with CRC-32)
2 PROXY3$@EME.ORG (DES cbc mode with RSA-MD5)
2 PROXY3$@EME.ORG (ArcFour with HMAC/md5)
2 HTTP/proxy3.eme.org@EME.ORG (DES cbc mode with CRC-32)
2 HTTP/proxy3.eme.org@EME.ORG (DES cbc mode with RSA-MD5)
2 HTTP/proxy3.eme.org@EME.ORG (ArcFour with HMAC/md5)
2 HTTP/proxy3@EME.ORG (DES cbc mode with CRC-32)
2 HTTP/proxy3@EME.ORG (DES cbc mode with RSA-MD5)
2 HTTP/proxy3@EME.ORG (ArcFour with HMAC/md5)

C'est vraiment du niveau « jardin d'enfants ».

Nous avons un bon gros tableau de clés, qu'il convient peut-être de découper en morceaux vu que ces clés vont servir à des services différents, qui vont tourner sous des identités différentes. L'outil ktutil va faire la preuve qu'il est bien nommé. Il s'agit d'un « sous-shell » :

proxy3:/etc/samba# ktutil
ktutil:  ?
Available ktutil requests:

clear_list, clear        Clear the current keylist.
read_kt, rkt             Read a krb5 keytab into the current keylist.
read_st, rst             Read a krb4 srvtab into the current keylist.
write_kt, wkt            Write the current keylist to a krb5 keytab.
write_st, wst            Write the current keylist to a krb4 srvtab.
add_entry, addent        Add an entry to the current keylist.
delete_entry, delent     Delete an entry from the current keylist.
list, l                  List the current keylist.
list_requests, lr, ?     List available requests.
quit, exit, q            Exit program.

Nous lisons notre krb5.keytab :

ktutil:  rkt /etc/krb5.keytab

Nous listons son contenu :

ktutil:  l

slot KVNO Principal
---- ---- ---------------------------------------------------------------------
1    2              host/proxy3.eme.org@EME.ORG
2    2              host/proxy3.eme.org@EME.ORG
3    2              host/proxy3.eme.org@EME.ORG
4    2                      host/proxy3@EME.ORG
5    2                      host/proxy3@EME.ORG
6    2                      host/proxy3@EME.ORG
7    2                          PROXY3$@EME.ORG
8    2                          PROXY3$@EME.ORG
9    2                          PROXY3$@EME.ORG
10    2              HTTP/proxy3.eme.org@EME.ORG
11    2              HTTP/proxy3.eme.org@EME.ORG
12    2              HTTP/proxy3.eme.org@EME.ORG
13    2                      HTTP/proxy3@EME.ORG
14    2                      HTTP/proxy3@EME.ORG
15    2                      HTTP/proxy3@EME.ORG

Puis nous n'y laissons que ce qui sera nécessaire à ssh, à savoir les principaux host :

ktutil:  delent 15
ktutil:  delent 14
ktutil:  delent 13
ktutil:  delent 12
ktutil:  delent 11
ktutil:  delent 10
ktutil:  delent 9
ktutil:  delent 8
ktutil:  delent 7
ktutil:  l
slot KVNO Principal
---- ---- ---------------------------------------------------------------------
1    2              host/proxy3.eme.org@EME.ORG
2    2              host/proxy3.eme.org@EME.ORG
3    2              host/proxy3.eme.org@EME.ORG
4    2                      host/proxy3@EME.ORG
5    2                      host/proxy3@EME.ORG
6    2                      host/proxy3@EME.ORG

Enfin, nous sauvegardons le résultat dans un nouveau tableau :

ktutil:  wkt /etc/ssh-krb5.keytab
ktutil:  q

Et nous pouvons vérifier le résultat :

proxy3:~# klist -e -k /etc/ssh-krb5.keytab
Keytab name: WRFILE:/etc/ssh-krb5.keytab
KVNO Principal
---- --------------------------------------------------------------------------
2 host/proxy3.eme.org@EME.ORG (DES cbc mode with CRC-32)
2 host/proxy3.eme.org@EME.ORG (DES cbc mode with RSA-MD5)
2 host/proxy3.eme.org@EME.ORG (ArcFour with HMAC/md5)
2 host/proxy3@EME.ORG (DES cbc mode with CRC-32)
2 host/proxy3@EME.ORG (DES cbc mode with RSA-MD5)
2 host/proxy3@EME.ORG (ArcFour with HMAC/md5)

Le principe étant compris, nous faisons ceci maintenant pour le service HTTP dans un tableau http-krb5.keytab et nous obtenons finalement :

proxy3:/etc/samba# klist -e -k /etc/http-krb5.keytab
Keytab name: WRFILE:/etc/http-krb5.keytab
KVNO Principal
---- --------------------------------------------------------------------------
2 HTTP/proxy3.eme.org@EME.ORG (DES cbc mode with CRC-32)
2 HTTP/proxy3.eme.org@EME.ORG (DES cbc mode with RSA-MD5)
2 HTTP/proxy3.eme.org@EME.ORG (ArcFour with HMAC/md5)
2 HTTP/proxy3@EME.ORG (DES cbc mode with CRC-32)
2 HTTP/proxy3@EME.ORG (DES cbc mode with RSA-MD5)
2 HTTP/proxy3@EME.ORG (ArcFour with HMAC/md5)

Nous sommes proches de la sortie. Il ne reste plus qu'à kerbériser ssh et squid.

9-3 - Un ssh avec 3 têtes

Juste pour le fun, voici un moyen d'ouvrir une session ssh en utilisant Kerberos au travers de GSSAPI. Soit un utilisateur disposant de :

- la possibilité d'ouvrir une session ssh sur l'hôte distant proxy3.eme.org ;

- un principal (compte dans le domaine Microsoft) qui l'authentifie dans le royaume Kerberos.

À la condition que l'hôte distant fasse partie du royaume, ce qui est le cas ici, et soit lui-même authentifié (clé partagée entre le KDC et l'hôte distant) ce qui est également le cas pour le principal host/proxy3.eme.org@EME.ORG, alors cet utilisateur va pouvoir utiliser le SSO pour ouvrir sa session ssh sur l'hôte distant, sans saisie de mot de passe, et sans échange de clé publique.

Une démonstration rapide ?

Voici la configuration de sshd sur proxy3 :

proxy3:/etc/ssh# cat sshd_config
Port 22
Protocol 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
UsePrivilegeSeparation yes
KeyRegenerationInterval 3600
ServerKeyBits 768
SyslogFacility AUTH
LogLevel INFO
LoginGraceTime 120
PermitRootLogin yes
StrictModes yes
RSAAuthentication yes
PubkeyAuthentication yes
IgnoreRhosts yes
RhostsRSAAuthentication no
HostbasedAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
GSSAPIAuthentication yes
GSSAPICleanupCredentials yes
X11Forwarding yes
X11DisplayOffset 10
PrintMotd no
PrintLastLog yes
TCPKeepAlive yes
AcceptEnv LANG LC_*
Subsystem sftp /usr/lib/openssh/sftp-server
UsePAM yes

Soit un utilisateur chris qui dispose :

- d'un compte local sur squid3.eme.org ;

- d'un compte dans le domaine AD eme.org (pas forcément avec le même mot de passe d'ailleurs, ni même avec les mêmes privilèges d'administration).

Soit une station de travail (Ubuntu Karmic Koala nommée karmicvirt), disposant des outils Kerberos krb5-user. PAM a été configuré de manière à ce que le TGT soit automatiquement chargé à l'ouverture de session. Notre cobaye (chris) a ouvert une session sur ce poste de travail et souhaite accéder à proxy3.eme.org :

chris@karmicvirt:~$ ssh -v proxy3.eme.org
OpenSSH_5.1p1 Debian-6ubuntu2, OpenSSL 0.9.8g 19 Oct 2007
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: Applying options for *
debug1: Connecting to proxy3.eme.org [192.168.0.137] port 22.
debug1: Connection established.
...
debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password
debug1: Next authentication method: gssapi-keyex
debug1: No valid Key exchange context
debug1: Next authentication method: gssapi-with-mic
debug1: Authentication succeeded (gssapi-with-mic).
debug1: channel 0: new [client-session]
debug1: Requesting no-more-sessions@openssh.com
debug1: Entering interactive session.
debug1: Sending environment.
debug1: Sending env LANG = fr_FR.UTF-8
Linux proxy3 2.6.32-trunk-686 #1 SMP Sun Jan 10 06:32:16 UTC 2010 i686

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Mon Mar  1 16:15:32 2010 from 192.168.0.68
chris@proxy3:~$

le SSO a fonctionné.

chris@karmicvirt:~$ klist
Ticket cache: FILE:/tmp/krb5cc_1000_f9ItDE
Default principal: chris@EME.ORG

Valid starting     Expires            Service principal
03/01/10 22:27:43  03/02/10 05:07:43  krbtgt/EME.ORG@EME.ORG
03/01/10 22:29:49  03/02/10 05:07:43  host/proxy3.eme.org@
03/01/10 22:29:49  03/02/10 05:07:43  host/proxy3.eme.org@EME.ORG

Bien sûr, si chris fait ceci :

chris@karmicvirt:~$ kdestroy
chris@karmicvirt:~$ klist
klist: No credentials cache found (ticket cache FILE:/tmp/krb5cc_1000_f9ItDE)

Aucun principal ni aucun ticket ne sont désormais présents dans le cache.

chris@karmicvirt:~$ ssh -v proxy3.eme.org
OpenSSH_5.1p1 Debian-6ubuntu2, OpenSSL 0.9.8g 19 Oct 2007
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: Applying options for *
debug1: Connecting to proxy3.eme.org [192.168.0.137] port 22.
debug1: Connection established.
...
debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password
debug1: Next authentication method: gssapi-keyex
debug1: No valid Key exchange context
debug1: Next authentication method: gssapi-with-mic
debug1: Unspecified GSS failure.  Minor code may provide more information
Credentials cache file '/tmp/krb5cc_1000_f9ItDE' not found

debug1: Unspecified GSS failure.  Minor code may provide more information
Credentials cache file '/tmp/krb5cc_1000_f9ItDE' not found

debug1: Unspecified GSS failure.  Minor code may provide more information

debug1: Next authentication method: publickey
debug1: Trying private key: /home/chris/.ssh/identity
debug1: Trying private key: /home/chris/.ssh/id_rsa
debug1: Trying private key: /home/chris/.ssh/id_dsa
debug1: Next authentication method: password
chris@proxy3.eme.org's password:

Notre client n'a pas de clé publique à présenter, il n'a pas de ticket Kerberos non plus, il va devoir utiliser le mot de passe de son compte chris sur proxy3.

Voici simplement la configuration de sshd sur proxy3 :

proxy3:/etc/ssh# cat sshd_config
Port 22
Protocol 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
UsePrivilegeSeparation yes
KeyRegenerationInterval 3600
ServerKeyBits 768
SyslogFacility AUTH
LogLevel INFO
LoginGraceTime 120
PermitRootLogin yes
StrictModes yes
RSAAuthentication yes
PubkeyAuthentication yes
IgnoreRhosts yes
RhostsRSAAuthentication no
HostbasedAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
GSSAPIAuthentication yes
GSSAPICleanupCredentials yes
X11Forwarding yes
X11DisplayOffset 10
PrintMotd no
PrintLastLog yes
TCPKeepAlive yes
AcceptEnv LANG LC_*
Subsystem sftp /usr/lib/openssh/sftp-server
UsePAM yes

9-4 - Un calmar avec trois têtes

Nous avons déjà vu comment faire avec un cerbère du MIT, ici, ce sera exactement la même chose, la seule différence étant le royaume Kerberos, mais ce n'est pas l'affaire du squid.

9-4-1 - Accès au keytab

/etc/http-krb2.keytab doit être en lecture seule, exclusivement par proxy puisque c'est sous cette identité que fonctionne le poulpe :

chown proxy:proxy /etc/http-krb5.keytab
chmod 400 /etc/http-krb5.keytab

9-4-2 - Environnement de la pieuvre

Nous devons créer une variable d'environnement qui renseigne sur le nom du keytab à utiliser. Nous savons que c'est dans /etc/default/squid3 que ça se passe :

KRB5_KTNAME=/etc/http-krb5.keytab
export KRB5_KTNAME

9-4-3 - Configuration de la seiche

Pour rappel, car il n'y a rien de nouveau ici :

acl manager proto cache_object
acl localhost src 127.0.0.1/32
acl to_localhost dst 127.0.0.0/8 0.0.0.0/32
acl localnet src 192.168.0.0/24

auth_param negotiate program /usr/lib/squid3/squid_kerb_auth -d
auth_param negotiate children 10
auth_param negotiate keep_alive on

acl auth proxy_auth REQUIRED

acl SSL_ports port 443
acl Safe_ports port 80        # http
acl Safe_ports port 21        # ftp
acl Safe_ports port 443        # https
acl Safe_ports port 70        # gopher
acl Safe_ports port 210        # wais
acl Safe_ports port 1025-65535    # unregistered ports
acl Safe_ports port 280        # http-mgmt
acl Safe_ports port 488        # gss-http
acl Safe_ports port 591        # filemaker
acl Safe_ports port 777        # multiling http
acl CONNECT method CONNECT
http_access allow manager localhost
http_access deny manager
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost
http_access allow localnet auth
http_access deny all
icp_access deny all
htcp_access deny all
http_port 3128
hierarchy_stoplist cgi-bin ?
access_log /var/log/squid3/access.log squid
refresh_pattern ^ftp:        1440    20%    10080
refresh_pattern ^gopher:    1440    0%    1440
refresh_pattern (cgi-bin|\?)    0    0%    0
refresh_pattern .        0    20%    4320
icp_port 3130
coredump_dir /var/spool/squid3

Un essai pour vérifier :

chris@lennyvirt:~ $kinit chris
Password for chris@EME.ORG:

Et dans les logs du décapode :

1267547026.580     67 192.168.0.89 TCP_MISS/302 322 GET http://irp.nain-t.net/ chris@EME.ORG DIRECT/213.186.40.149 text/html
1267547026.791    209 192.168.0.89 TCP_MISS/200 12573 GET http://irp.nain-t.net/doku.php chris@EME.ORG DIRECT/213.186.40.149 text/html
...

Bien entendu :

chris@lennyvirt:~$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: chris@EME.ORG

Valid starting     Expires            Service principal
03/02/10 17:20:50  03/03/10 03:20:55  krbtgt/EME.ORG@EME.ORG
renew until 03/03/10 03:20:50
03/02/10 17:21:13  03/03/10 03:20:55  HTTP/proxy3.eme.org@EME.ORG
renew until 03/03/10 03:20:50

C'est devenu de la routine.

Tout l'intérêt de cette solution apparait pour les utilisateurs qui ont ouvert une session Windows (XP, Vista, Seven…) sur le domaine AD eme.org. Ils disposent de leur TGT de façon implicite en ayant ouvert leur session, l'authentification sur le proxy se fait donc de manière parfaitement transparente et plus proprement qu'avec la méthode NTLM, vue dans Active Directory au chapitre Squid et SquidGuard.

La seule restriction supplémentaire est qu'un utilisateur qui a ouvert une session locale ne dispose pas du TGT et donc ne peut avoir accès au proxy.

9-5 - Pour finir

Il n'est donc pas utopique de faire cohabiter des services Unix dans une architecture comportant un domaine Active Directory de façon propre. Windows 2008 intègre les « services pour Unix » qui permettent un peu plus d'interopérabilité, principalement en ajoutant dans l'annuaire LDAP des schémas supplémentaires. Il n'est pas interdit de penser que :

- Samba pourrait se passer de winbind pour autoriser des partages aux utilisateurs Windows ;

- AD pourrait servir à centraliser des comptes d'utilisateurs Unix.

Mais LDAP est une autre affaire. Un jour peut-être…

10 - Remerciements Developpez

Vous pouvez retrouver l'article original ici : L'Internet Rapide et Permanent. Christian Caleca a aimablement autorisé l'équipe « Réseaux » de Developpez.com à reprendre son article. Retrouvez tous les articles de Christian Caleca sur cette page.

Nos remerciements à ClaudeLELOUP pour sa relecture orthographique.

N'hésitez pas à commenter cet article ! Commentez

(1)Kernel-based Virtual Machine

(2)Wireshark est un « Network Protocol Analyzer »