Adosser des services Kerbérisés Linux à Active Directory

Spécialité(s)


Résumé

Historiquement, l’authentification système des utilisateurs en environnement hétérogène a toujours obligé les administrateurs à maintenir plusieurs bases de credentials (par exemple, un hash NTLMv2 sous Windows et SHA sous Linux). L’adoption native de Kerberos par tous les systèmes favorise l'unification de l'authentification.


Body

 

L’objet de cet article est de présenter une façon d’intégrer des services Linux nécessitant une authentification dans un domaine Active Directory (AD). Nous allons traiter deux services : un proxy web Squid et un serveur OpenSSH. Ces deux services ne sont pas choisis au hasard. En effet, lorsque l’on parle d’authentification système, il est courant d’englober deux notions : la vérification des credentials et l’autorisation. Le service Squid se contente de vérifier des credentials pour accoler une identité aux requêtes HTTP qui le traversent. Il n'a donc absolument pas besoin que l'utilisateur existe au niveau système (c'est-à-dire dispose d'un UID valide) sur le proxy. C'est le cas le plus simple de mise en œuvre du protocole Kerberos. Le second cas pratique basé sur OpenSSH nécessite l'ouverture d'une session système sur la machine ciblée. En conséquence, il faut compléter les utilisateurs locaux de la machine avec ceux de l'AD.

1. Introduction à Kerberos

Kerberos propose une authentification SSO (Single Sign On) des utilisateurs sur des services. Les identités (utilisateurs ou services) sont organisées en «  realms ». Chaque realm est une base de données contenant des « principals ». Ces principals sont soit des utilisateurs, soit des services. Chaque fois que l'on ajoute un utilisateur ou un service dans le realm, il faut lui créer un principal. Pour les utilisateurs, un principal est un identifiant associé à un jeu de clés dérivé du mot de passe, selon plusieurs méthodes de chiffrement (« enctypes »). On retrouve une clé pour chaque enctype. Le fonctionnement est similaire pour les services : au niveau de l'identifiant, on parle de SPN (Service Principal Name) et le mot de passe, à partir duquel sont dérivées les clés, est souvent généré aléatoirement (pour éviter les mots de passe faibles). Les principals sont stockés dans une base de données servie par un KDC (Key Distribution Center).

Lorsqu'un utilisateur se connecte à sa station de travail, il utilise un module d'authentification qui interagit avec le service d'authentification. Pour Kerberos, cela se passe en deux temps. L'utilisateur obtient son TGT (Ticket Granting Ticket) auprès de l'AS (Authentication Server). Cette phase sert le plus souvent à ouvrir une session sur le poste de travail, tout en acquérant « un ticket permettant d'obtenir d'autres tickets » (en l’occurrence, des tickets de service). La deuxième phase concerne l'accès aux services kerberisés. Elle se passe entre le client et le TGS (Ticket Granting Service) et permet à l'utilisateur muni d'un TGT valide (ou connaissant le mot de passe ayant servi à dériver les clés) d'obtenir un ticket de service, à présenter à l'application auprès de laquelle il souhaite s'authentifier.

1.1 Obtention du TGT

Le TGT est un ticket permettant d'obtenir des tickets associés aux services déclarés dans le realm. Pour obtenir le TGT, le client envoie une requête AS_REQ auprès de l'AS contenant l'identifiant du destinataire du TGT (c'est-à-dire, le principal associé à l'utilisateur pour lequel le TGT va être émis par l'AS), le service sollicité par l'utilisateur et une durée de validité pour le TGT. Dans un schéma classique, le service sollicité est krbtgt/realm@REALM, c'est-à-dire le service d’émission de tickets pour le realm courant, configuré sur la machine cliente. En effet, il est tout à fait possible de demander à l'AS un ticket pour un service donné (option -S de kinit) pour le tester, sans nécessairement acquérir un TGT.

L'AS contrôle alors si les principals utilisateur et service (le plus souvent krbtgt/realm@REALM) sont bien contenus dans le KDC. Ensuite, l'AS génère une clé de session à utiliser entre l'utilisateur et le TGS SKtgs. Un TGT est créé contenant le nom du principal associé au demandeur, le nom du principal associé au service (par défaut krbtgt/realm@REALM), la liste des adresses IP autorisées à utiliser le TGT, une estampille temporelle, une durée de vie et surtout la clé SKtgs générée.

Ensuite, l'AS envoie une réponse AS-REP au client, contenant le TGT chiffré avec la clé privée du TGS et le principal du service et une estampille de temps, ainsi qu'une durée de vie et la clé de session Sktgs. Ces deux derniers attributs sont chiffrés avec Kc (clé privée du client, c'est-à-dire pour un utilisateur, la clé dont est dérivé le mot de passe).

Un problème à ce niveau est que n'importe qui peut obtenir un TGT, il suffit d'en demander un pour essayer de le casser hors ligne. Dans la version 5 du protocole, Kerberos intègre un mécanisme de préauthentification pour pallier au problème. Lorsque le client envoie une requête AS_REQ, le service AS lui renvoie une erreur lui indiquant qu'une préauthentification est nécessaire. Du côté du client, le mot de passe dont sont dérivées les clés associées au principal du client est demandé pour chiffrer une estampille temporelle, qui sera utilisée dans le champ optionnel PADATA pour une nouvelle requête AS_REQ. Le client soumet cette nouvelle requête, le service AS vérifie l’estampille chiffrée et si ça correspond, il envoie une réponse AS_REP valide en retour.

1.2 Obtention d'un ticket de service

Une fois muni du TGT, le client peut demander des tickets de service à présenter aux applications kerberisées. C'est cette obtention de tickets de service sans authentification interactive (donc transparente pour l'utilisateur) qui fait de Kerberos un SSO. À ce niveau, l'utilisateur possède un TGT valide chiffré avec la clé privée du TGS Ktgs et une clé de session entre lui et le TGS : SKtgs.

Le client envoie une requête TGS_REQ au TGS. Cette requête contient un authenticator chiffré avec SKtgs contenant le nom du principal et une estampille temporelle, le TGT chiffré, la clé privée du TGS Ktgs et le SPN demandé avec une durée de validité en clair.

À réception de la requête, le TGS vérifie que le TGT est valide, que le nom de principal contenu dans la demande (chiffrée avec SKtgs) est bien le même que celui contenu dans le TGT (chiffré avec Ktgs), que l'authenticator est absent du cache de rejeu (une entrée est ajoutée à ce cache pour chaque requête traitée) et que l'adresse IP source du paquet est bien dans la liste des adresses IP autorisées (liste contenue dans le TGT chiffré avec Ktgs).

Une fois ces tests effectués, le service TGS génère une clé de session entre le client et le service demandé SKs. Un ticket de service est également généré contenant le principal du demandeur, le principal du service demandé, une liste des adresses IP autorisées, une estampille temporelle, une durée de validité du ticket de service et la clé de session SKs, générée par le service TGS.

Ce ticket de service est ensuite chiffré avec Ks (clé privée associée au principal représentant le service demandé) et envoyé au client au sein d'une réponse TGS_REP. Cette réponse contient également les champs suivants chiffrés avec SKtgs : principal du service demandé, une estampille temporelle, une durée de validité et surtout, la clé de session entre le service et le client SKs.

Une fois muni d'un ticket de service chiffré avec Ks et de la clé Sks, le client génère un authenticator contenant le principal du service demandé et une estampille temporelle. Cet authenticator chiffré avec la clé de session de service Sks est concaténé au ticket de service chiffré avec la clé privée du principal associé au service Ks. Le tout compose une AP_REQ (Application Request) qui est envoyée au service kerberisé. (voir schéma en figure 1).

Kerberos

Fig. 1 : Le protocole Kerberos.

2. Préparation de la machine

La configuration d'une machine Linux dans un domaine AD se fait en deux étapes. Premièrement, il faut que la machine Linux soit capable de localiser le KDC de l'AD. Deuxièmement, il faut ajouter le principal host/FQDN@REALM ; c'est le ticket de service associé à la machine elle-même. Les tickets de service sont stockés dans le KDC côté serveur Kerberos et dans un fichier nommé keytab sur la machine hébergeant un service kerberisé. Kerberiser une application revient à générer un fichier keytab côté client et l'injecter dans le KDC. Dans le cas d'une intégration à AD, mon conseil est de configurer l'AD comme serveur DNS de la machine. Les enregistrements DNS dans l'AD proxy.local.lin2ad.org et serveur.local.lin2ad.org pointent tous les deux sur la même IP. Il s'agit en effet de la même machine physique hébergeant les deux services utilisés pour l'illustration pratique de l'article.

2.1 Localisation du KDC

Nous allons commencer par configurer notre serveur Linux adossé à l'AD, afin qu'il puisse obtenir des tickets du KDC Microsoft. Il faut installer les outils client Kerberos :

root@serveur:~# apt-get install krb5-user

Et configurer le fichier /etc/krb5.conf pour que la machine soit capable de localiser le KDC du serveur AD :

root@serveur:~# cat /etc/krb5.conf

[libdefaults]

    default_realm = LOCAL.LIN2AD.ORG

[realms]

    LOCAL.LIN2AD.ORG = {

    kdc = kdc.local.lin2ad.org

    }

Le fichier est divisé en deux sections. La première concerne les paramètres par défaut des librairies Kerberos (libdefaults). Ici, il s'agit uniquement de positionner le realm par défaut (default_realm). La seconde donne les paramètres de connexion aux KDC de chaque realm configuré (realms). Ici, nous avons juste le realm LOCAL.LIN2AD.ORG de configuré. Le seul attribut est le FQDN du KDC AD (kdc). Testons l'obtention d'un ticket Administrator du domaine :

root@serveur:~# kinit Administrator

Password for Administrator@LOCAL.LIN2AD.ORG:

Vérifions la présence du ticket :

root@serveur:~# klist

Ticket cache: FILE:/tmp/krb5cc_0

Default principal: Administrator@LOCAL.LIN2AD.ORG

Valid starting    Expires           Service principal

14/01/2019 10:40:16  14/01/2019 20:40:16  krbtgt/LOCAL.LIN2AD.ORG@LOCAL.LIN2AD.ORG

    renew until 15/01/2019 10:40:10

2.2 Création du ticket de service « host »

Pour créer le ticket de service host/FQDN@REALM, l'écrire dans le keytab de la machine et l'enregistrer dans le KDC AD, nous allons utiliser l'outil msktutil, qui fait tout ça en une seule fois. Munis du ticket de l'administrateur du domaine AD, nous allons exécuter ces commandes :

root@serveur:~# apt-get install msktutil

root@serveur:~# msktutil create --computer-name serveur --user-creds-only --server win-e8fsd9al2ar.local.lin2ad.org --no-reverse-lookups

Dans cette commande, nous ajoutons un ticket de service pour la machine serveur (--computer-name) en utilisant uniquement le ticket obtenu via kinit (--user-creds-only) en contactant la machine win-e8fsd9al2ar.local.lin2ad.org (--server) sans faire le test de résolution inverse (--no-reverse-lookups), car le contrôleur de domaine dispose de plusieurs interfaces réseau et ça met la grouille au niveau des résolutions PTR. À l'issue de cette commande, nous avons un fichier keytab avec le ticket du serveur dedans :

root@serveur:~# klist -ke

Keytab name: FILE:/etc/krb5.keytab

KVNO Principal

---- --------------------------------------------------------------------------

   1 serveur$@LOCAL.LIN2AD.ORG (arcfour-hmac)

   ...

   1 SERVEUR$@LOCAL.LIN2AD.ORG (arcfour-hmac)

   ...

   1 host/serveur@LOCAL.LIN2AD.ORG (arcfour-hmac)

   ...

   1 host/serveur.local.lin2ad.org@LOCAL.LIN2AD.ORG (arcfour-hmac)

   ...

Nous retrouvons bien nos principals associés à la machine (serveur$, SERVEUR$, host/serveur et host/serveur.local.lin2ad.org). Ces quatre principals sont dérivés avec trois enctypes : arcfour-hmac, aes128-cts-hmac-sha1-96 et aes256-cts-hmac-sha1-96.

La petite particularité des clés associées aux principals de services sous AD est qu'elles sont renouvelées tous les trente jours. Il faut donc placer une tâche cron pour gérer le renouvellement des clés associées à la machine serveur :

root@serveur:~# cat /etc/crontab

0 23 * * * root /usr/sbin/msktutil --auto-update --verbose --computer-name serveur -k /etc/krb5.keytab | /usr/bin/logger -t msktutil

3. Configuration de Squid

Squid est un proxy web, c'est-à-dire que les utilisateurs du réseau local doivent le traverser pour accéder à des URL. Le FQDN du proxy doit être configuré dans le navigateur du client. L'objectif de la manipulation est que l'utilisateur sur le poste client s'authentifie en SSO auprès du proxy.

3.1 Préparation du proxy

Nous allons configurer Squid sur la machine serveur. Pour l'installer :

root@serveur:~# apt-get install squid

La première étape est d'ajouter la clé au keytab local et de l'injecter dans le KDC de l'AD. Nous allons à nouveau utiliser l'outil msktutil :

root@serveur:~# msktutil -c -s HTTP/proxy.local.lin2ad.org -h proxy.local.lin2ad.org -k /etc/squid/HTTP.keytab --computer-name proxy --upn HTTP/proxy.local.lin2ad.org --server win-e8fsd9al2ar.local.lin2ad.org --no-reverse-lookups

Par rapport à la commande précédente, plusieurs paramètres ont été ajoutés. L'agument -s signifie qu'on génère un ticket de service, en l'occurrence le service HTTP (proxy web) sur la machine proxy.local.lin2ad.org. L'argument -h explicite le nom d'hôte à utiliser, ici proxy. Enfin, le --upn spécifie le nom du principal à créer (le realm sera automatiquement concaténé). Vérifions la bonne création du keytab :

root@serveur:~# klist -ke FILE:/etc/squid/HTTP.keytab

Keytab name: FILE:/etc/squid/HTTP.keytab

KVNO Principal

---- --------------------------------------------------------------------------

   1 proxy$@LOCAL.LIN2AD.ORG (arcfour-hmac)

   ...

   1 PROXY$@LOCAL.LIN2AD.ORG (arcfour-hmac)

   ...

   1 HTTP/proxy.local.lin2ad.org@LOCAL.LIN2AD.ORG (arcfour-hmac)

   ...

   1 host/proxy@LOCAL.LIN2AD.ORG (arcfour-hmac)

   ...

   1 host/proxy.local.lin2ad.org@LOCAL.LIN2AD.ORG (arcfour-hmac)

   ...

Enfin, il est nécessaire que le processus squid puisse accéder à ce keytab :

root@serveur:~# chgrp proxy /etc/squid/HTTP.keytab

root@serveur:~# chmod g+r /etc/squid/HTTP.keytab

Pour indiquer l'emplacement du keytab au processus squid, il faut créer le fichier /etc/default/squid qui va positionner la variable KRB5_KTNAME pointant sur le fichier /etc/squid/HTTP.keytab :

root@serveur:~# cat /etc/default/squid

KRB5_KTNAME=/etc/squid/HTTP.keytab

export KRB5_KTNAME

3.2 Configuration du proxy

La configuration de Squid s'effectue dans le fichier /etc/squid/squid.conf. Ce fichier est monstrueux, j'ai donc isolé les sections pertinentes :

root@serveur:~# cat /etc/squid/squid.conf

...

auth_param negotiate program /usr/lib/squid3/negotiate_kerberos_auth -r -s HTTP/proxy.local.lin2ad.org@LOCAL.LIN2AD.ORG

auth_param negotiate children 50

auth_param negotiate keep_alive on

...

acl localnet src 192.168.0.0/24      # RFC 4291 link-local (directly plugged) machines

...

acl AUTHENTICATED proxy_auth REQUIRED

...

http_access allow AUTHENTICATED localnet

http_access allow localhost

http_access deny all

Le helper qui va utiliser le ticket de l'utilisateur pour réaliser l’authentification est negociate_kerberos_auth. On lui passe le principal du keytab à utiliser (HTTP/proxy.local.lin2ad.org@LOCAL.LIN2AD.ORG). Il faut ensuite définir le réseau autorisé à utiliser le proxy (192.168.0.0/24) et la condition d'utilisation (AUTHENTICATED). Avec cette configuration, seuls les utilisateurs authentifiés et localisés sur des machines de ce réseau peuvent utiliser le proxy. Il faut bien penser à redémarrer Squid. Côté client, il suffit d'appliquer l'étape 2.1 et de demander un ticket d'utilisateur crée sur l'AD :

user@client:~# kinit nico

Ensuite on déclare le proxy dans les préférences du navigateur et on revient sur le serveur pour regarder les logs d'accès :

root@serveur:~# cat /var/log/squid/access.log

...

1547482965.423   3493 192.168.0.100 TCP_TUNNEL/200 1213033 CONNECT lipn.univ-paris13.fr:443 nico HIER_DIRECT/194.254.163.36 -

On voit bien que le huitième champ contient l'identifiant de l'utilisateur (nico). Comme précédemment, il faut prévoir un cron pour régénérer les tickets.

4. Configuration du serveur OpenSSH

L'objet est de permettre à l'utilisateur, depuis la machine cliente, de se connecter en SSH sur la machine serveur en présentant son ticket. Par défaut, OpenSSH utilise le principal host/FQDN@REALM qui est déjà présent dans le fichier /etc/krb5.keytab, le keytab par défaut. Côté Kerberos, tout est donc bon. Il faut juste le configurer. Nous allons commencer par un cas très simple, avec un compte local sur la machine serveur.

4.1 Connexion à un compte local

Commençons par créer le compte local nico sur la machine serveur :

root@serveur:~# useradd -m nico

Ensuite, il faut configurer le serveur SSH en allant dans le fichier /etc/ssh/sshd_config pour permettre l'utilisation du ticket Kerberos de l'utilisateur :

root@serveur:~# cat /etc/ssh/sshd_config

GSSAPIAuthentication yes

On redémarre le serveur SSH et on bascule sur le client:

root@client:~# kinit nico

root@client:~# ssh nico@serveur.local.lin2ad.org

Linux openssh-server 4.9.0-7-amd64 #1 SMP Debian 4.9.110-3+deb9u2 (2018-08-13) x86_64

...

nico@serveur:~$

Ça fonctionne. La dernière étape est d'importer les utilisateurs depuis l'AD. Nous allons détruire l'utilisateur nico sur le serveur avant de passer à la suite :

root@serveur:~# userdel nico

4.2 Connexion à un compte AD

Pour compléter les utilisateurs locaux avec ceux de l'AD il faut configurer un système de mapping entre les SID Windows et les UID Linux. Le service qui s'occuper de ça, c'est winbind. Il fonctionne avec des machines intégrées au domaine AD. Il va donc falloir également installer un petit bout de Samba pour joindre la machine au domaine avec la commande net. Commençons par installer winbind :

root@serveur:~$ apt-get install winbind

Le service winbind est extrêmement adhérent à Samba. Le service est donc configuré dans le fichier /etc/samba/smb.conf. Voici une configuration minimale :

root@openssh-server:~# cat /etc/samba/smb.conf

[Global]

netbios name = serveur

workgroup = LOCAL

realm = LOCAL.LIN2AD.ORG

security = ads

encrypt passwords = yes

password server = kdc.local.lin2ad.org

kerberos method = system keytab

idmap config * : backend = tdb

idmap config * : range = 3000001-4000000

winbind use default domain = yes

winbind enum users = yes

winbind enum groups = yes

winbind refresh tickets = yes

template shell = /bin/bash

template homedir = /home/%U

Le paramètre critique qui conditionne la valeur de tous les autres, c'est security = ads. Ce paramètre indique que la machine va devoir s'intégrer dans un domaine AD pour avoir accès à la base d'utilisateurs. Un cas typique d'utilisation est un serveur de fichiers ou le positionnement des ACL nécessite de connaître les utilisateurs. Pour lier la machine, nous retrouvons les attributs realm, workgroup et netbios name qui parlent d'eux-mêmes. Nous avons ensuite le paramètre password server qui indique le FQDN du KDC. L'attribut kerberos method fixé a system keytab indique que Samba doit utiliser le keytab par défaut (/etc/krb5.keytab). Ensuite, ce sont les paramètres winbind. On commence par définir une étendue d'UID réservée sur le système pour mapper les SID. Les attributs enum permettent (ou non) au système d'énumérer les utilisateurs. On pourrait souhaiter désactiver ça dans un souci de performance. Enfin, les deux paramètres template fixent le shell à /bin/bash et le répertoire home à /home/<identifiant> au moment du mapping. On redémarre winbind et on joint la machine au domaine :

root@serveur:~# kinit Administrator

root@serveur:~# net ads join -k

On peut maintenant regarder si on retrouve nos utilisateurs :

root@serveur:~# wbinfo -u | grep nico

nico

Le service winbind fonctionne. Voyons si l'utilisateur est visible sur le système :

root@serveur:~# getent passwd | grep nico

Personne… En fait, c'est que le service nsswitch n'est pas configuré. C'est lui qui compose la base utilisateur finale en combinant plusieurs sources. Ici, il nous faut les utilisateurs locaux complétés avec les utilisateurs AD. Commençons par installer la librairie winbind pour le service NSS :

root@serveur:~# apt-get install libnss-winbind

Ensuite, il faut ajuster le fichier /etc/nsswitch.conf pour configurer les sources de comptes utilisateur :

root@serveur:~# cat /etc/nsswitch.conf

passwd:         compat winbind

group:          compat winbind

shadow:         compat winbind

Nous avons ajouté la source winbind aux utilisateurs locaux. Testons à nouveau :

root@serveur:/etc/samba# getent passwd | grep nico

nico:*:3000005:3000001:Nico:/home/nico:/bin/bash

Arrivé ici, pensez à fixer le propriétaire du répertoire /home/nico pour qu'il appartienne à l'utilisateur nico mappé depuis AD. L'UID a changé par rapport au compte local créé en 4.1. Retournons maintenant sur le client :

root@client:~# kinit nico

root@client:~# ssh nico@serveur.local.lin2ad.org

Linux openssh-server 4.9.0-7-amd64 #1 SMP Debian 4.9.110-3+deb9u2 (2018-08-13) x86_64

...

nico@serveur:~$

Conclusion

Cet article vous a donné les étapes de base pour intégrer des services Linux dans un environnement AD. La volonté était de décomposer chaque étape, pour vous permettre de bien comprendre ce qui se passe au niveau système. En effet, il existe des systèmes tout intégrés comme sssd qui font ça très bien et de façon bien plus concise, en termes de configuration. Cependant, quand on fait de l'interopérabilité Kerberos Windows / Linux, il faut vraiment avoir les idées claires sur comment ça fonctionne sous le capot !

 



Article rédigé par

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous