Authentification Kerberos avec Apache dans un environnement Active Directory multi-domaine
Le contexte
Parfois les gens sont fous ! Ils installent des architectures informatiques avec du Microsoft partout : des postes de travail sous Windows, pleins de contrôleurs de domaine et pleins d’annuaires Active Directory. Et puis régulièrement MS vient les voir en leurs présentant la facture et ils perdent un bras. Comme ils veulent quand même continuer à manger du chocolat, ils décident (ce qui est déjà pas mal) d’ouvrir leur SI à d’autres technologies. Comme ça la prochaine fois qu’un soldat de Bill viendra les voir, ils pourront le menacer (un peu) de ne plus utiliser ses produits et ainsi faire baisser la douloureuse.
La prochaine application sera donc totalement faite avec des briques logicielles libres, et de plus au format Web tant qu’à faire. L’utilisateur devra se connecter à l’application via son navigateur en utilisant l’authentification intégrée de Windows.
Nous, on arrive et on est content car on propose d’utiliser les logiciels suivants :
- Linux CentOS 5.6
- Samba 3.5.4
- Apache 2.2.3
- Mod_auth_kerb : 5.1
- Kerberos 1.6.1
- Php 5.1
Et tout ça dans l’environnement qui suit :
- OS des postes de travail : Windows XP et 7
- Navigateur web : IE9 et Firefox 4
- KDC : Windows 2003/2008 R2
Un petit schéma pour illustrer l’architecture Active Directory à considérer :
Et un petit 2ème :
Règles de nommage utilisées dans ce tutoriel
- Serveur Linux Apache : serveurA
- Serveur KDC 2003 dom1.maboite.fr : serveurKDC1
- Serveur KDC 2008 dom1.maboite.fr : serveurKDC2
- Serveur KDC 2003 dom2.maboite.fr : serveurKDC3
Installation
Une fois la distribution CentOS préparée dans une version de base, il faut installer les packages nécessaires :
# yum update # yum install krb5-workstation # yum install samba3x samba3x-client # yum install httpd # yum install mod_auth_kerb # yum install ntp # yum install php php-ldap
Configuration
Date et heure
Notre serveur ne doit pas être trop décalé (< 10 mn) avec le DC du domaine DOM1.
Modifiez le fichier /etc/ntp.conf pour que le serveur se synchronise avec un serveur de temps, puis taper les commandes suivantes :
# service ntpd stop # ntpdate @SERVEUR_de_TEMPS # service ntpd start
Fichier HOSTS
Le fichier /etc/hosts doit être correctement renseigner pour pouvoir joindre un domaine Active Directory :
127.0.0.1 serveurA.maboite.fr serveurA
KERBEROS
Éditez le fichier /etc/krb5.conf et adaptez ce qui suit :
[libdefaults] default_realm = DOM1.MABOITE.FR default_keytab_name = FILE:/etc/krb5.keytab kdc_timesync = 1 ccache_type = 4 forwardable = true proxiable = true fcc-mit-ticketflags = true [realms] DOM1.MABOITE.FR = { kdc = @IP serveurKDC1 kdc = @IP serveur KDC2 admin_server = @IP serveurKDC1 admin_server = @IP serveurKDC2 } [domain_realm] MONWORKGROUP = DOM1.MABOITE.FR .dom1.maboite.fr = DOM1.MABOITE.FR [appdefaults] kinit = { renewable = true fowardable = true }
Créer ensuite un fichier /etc/krb5.keytab :
# touch /etc/krb5.keytab # chmod 640 /etc/krb5.keytab # chgrp apache /etc/krb5.keytab
SAMBA
Voici un extrait du fichier de configuration /etc/samba/smb.conf présentant les paramètres nécessaires :
[global] dedicated keytab file = /etc/krb5.keytab kerberos method = secrets and keytab security = ADS server string = SERVEURA realm = DOM1.MABOITE.FR workgroup = MONWORKGROUP password server = * winbind offline logon = true winbind refresh tickets = true winbind cache time = 3600 winbind use default domain = no preferred master = no domain master = No
Puis nous allons joindre le serveur Linux au domaine 1 :
# /etc/init.d/smb stop # /etc/init.d/nmb stop # /etc/init.d/winbind stop # net cache flush # kinit compte_admin_dom1 # net ads join -U compte_admin_dom1 Enter compte_admin_dom1's password:
La réponse de la dernière commande indiquant que l’opération s’est bien déroulée doit être :
Using short domain name -- MONWORKGROUP Joined 'SERVEURA' to realm 'dom1.maboite.fr'
Pour vérifier :
# net ads testjoin Join is OK
Puis on redémarre SAMBA :
# /etc/init.d/smb start # /etc/init.d/nmb start # /etc/init.d/winbind start
KEYTAB
Le serveur Web a correctement joint le domaine AD, nous allons maintenant renseigner le fichier /etc/krb5.keytab pour qu’il soit utilisé par Apache.
# net ads keytab add HTTP -U compte_admin_dom1 Processing principals to add... Enter compte_admin_dom1's password:
Vérifions que le fichier /etc/krb5.keytab est bien renseigné :
# ktutil ktutil: rkt /etc/krb5.keytab ktutil: l slot KVNO Principal ---- ---- --------------------------------------------------------------------- 1 28 HTTP/serveurA.maboite.fr@DOM1.MABOITE.FR 2 28 HTTP/serveurA.maboite.fr@DOM1.MABOITE.FR 3 28 HTTP/serveurA.maboite.fr@DOM1.MABOITE.FR 4 28 HTTP/serveurA@DOM1.MABOITE.FR 5 28 HTTP/serveurA@DOM1.MABOITE.FR 6 28 HTTP/serveurA@DOM1.MABOITE.FR
APACHE
Éditons maintenant le fichier /etc/httpd/conf.d/auth_kerb.conf :
# # Sample configuration: Kerberos authentication must only be # used over SSL to prevent replay attacks. The keytab file # configured must be readable only by the "apache" user, and # must contain service keys for "HTTP/www.example.com", where # "www.example.com" is the FQDN of this server. # <Location /logon> AuthType Kerberos AuthName "Kerberos Login" KrbMethodNegotiate On KrbMethodK5Passwd On KrbAuthRealms DOM1.MABOITE.FR DOM2.MABOITE.FR Krb5KeyTab /etc/krb5.keytab KrbSaveCredentials On KrbServiceName HTTP require valid-user </Location>
Et on redémarre Apache :
# /etc/init.d/httpd restart
PHP et LDAP
Le fichier PHP suivant est donné à titre d’exemple. Nous afficherons les variables PHP pour vérifier que l’authentification se passe bien, puis nous tenterons de récupérer sur l’utilisateur quelques informations contenues dans l’AD (prénom, nom, mail, …).
Créons maintenant un fichier php /var/www/html/logon/index.php dans lequel nous allons mettre le code suivant :
SUPER ça marche !!<br> <?php foreach($_SERVER as $key_name => $key_value){ print $key_name . " = " . $key_value . "<br>"; } echo "<br />"; // Info AD DOM1 $ldaphostDOM1 = "serveurKDC1"; // notre serveur AD du DOM1 $ldapportDOM1 = 389; // port // Info AD DOM2 $ldaphostDOM1 = "serveurKDC3"; // notre serveur AD du DOM2 $ldapportDOM1 = 389; // port // [...] pareil pour les autres domaines // User DN DOM1 $ldaprdnDOM1 = 'CN=LDAP, OU=Comptes techniques, OU=Dom1, DC=dom1, DC=maboite, DC=fr'; $ldappassDOM1 = 'motdepasse'; // Mot de passe associé // User DN DOM2 $ldaprdnDOM2 = 'CN=LDAP, OU=Comptes techniques, OU=Dom2, DC=dom2, DC=maboite, DC=fr'; $ldappassDOM2 = 'motdepasse'; // Mot de passe associé // [...] pareil pour les autres domaines // DN utilisateurs DOM1 $dnDOM1 = "OU=Comptes Utilisateurs,OU=dom1, DC=Dom1, DC=maboite, DC=fr" // DN utilisateurs DOM2 $dnDOM2 = "OU=Comptes Utilisateurs,OU=dom2, DC=Dom2, DC=maboite, DC=fr" // [...] pareil pour les autres domaines // $person est le nom de l'utilisateur et $domain est le REALM $tab = explode('@',$_SERVER['REMOTE_USER']); $person = $tab[0]; $domain = $tab[1]; switch ($domain){ case "DOM1.MABOITE.FR": $ldaphost = $ldaphostDOM1; $ldapport = $ldapportDOM1; $ldaprdn = $ldaprdnDOM1; $ldappass = $ldappassDOM1; $dn = $dnDOM1; break; case "DOM2.MABOITE.FR": $ldaphost = $ldaphostDOM2; $ldapport = $ldapportDOM2; $ldaprdn = $ldaprdnDOM2; $ldappass = $ldappassDOM2; $dn = $dnDOM2, break; // case [...] pareil pour les autres domaines default: die( "Domaine utilisateur inconnu : $domain" ); } // Connecting to LDAP $ldapconn = ldap_connect( $ldaphost, $ldapport ) or die( "Impossible de se connecter au serveur LDAP " ); if ($ldapconn) { //Connexion au serveur LDAP $ldapbind = ldap_bind($ldapconn, $ldaprdn, $ldappass); // Identification if ($ldapbind) { echo "Connexion LDAP reussie<br>"; $filter = "(sAMAccountName=$person)"; $justthese = array( "cn", "ou", "sn", "givenname", "mail", "memberOf"); $sr=ldap_search($ldapconn, $dn, $filter, $justthese); $info = ldap_get_entries($ldapconn, $sr); echo $info["count"]." entrees trouvees.\\n"; for ($i=0; $i<$info["count"]; $i++) { echo 'dn est : ' . $info[$i]["dn"] . '<br />'; echo 'premiere entree cn : ' . $info[$i]["cn"][0] . '<br />'; echo 'prenom : ' . $info[$i]["givenname"][0] . '<br />'; echo 'nom : ' . $info[$i]["sn"][0] . '<br />'; echo 'premier email : ' . $info[$i]["mail"][0] . '<br />'; $taillememberof = sizeof($info[$i]["memberof"]) -1; for ($j=0; $j<$taillememberof; $j++){ echo 'memberOf ['.$j.'] : ' . $info[$i]["memberof"][$j] . '<br />'; } } }else{ echo "Connexion LDAP échouée"; } } ?>
INTERNET EXPLORER
Si le navigateur utilisé est IE, vérifiez la configuration dans les options Internet :
FIREFOX
Dans la barre d’adresse tapez about:config puis filtrez avec le mot « auth.trusted » :
RESULTAT
SUPER ça marche !! HTTP_ACCEPT = image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash; [..] */* HTTP_ACCEPT_LANGUAGE = fr-FR HTTP_USER_AGENT = Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0; .NET CLR 1.1.4322; .NET4.0C; InfoPath.3) HTTP_ACCEPT_ENCODING = gzip, deflate HTTP_HOST = serveurA HTTP_CONNECTION = Keep-Alive PATH = /sbin:/usr/sbin:/bin:/usr/bin SERVER_SIGNATURE = Apache/2.2.3 (CentOS) Server at serveurA.maboite.fr Port 80 SERVER_SOFTWARE = Apache/2.2.3 (CentOS) SERVER_NAME = serveurA.maboite.fr SERVER_ADDR = 10.80.133.196 SERVER_PORT = 80 REMOTE_ADDR = 10.80.128.36 DOCUMENT_ROOT = /var/www/html SERVER_ADMIN = root@localhost SCRIPT_FILENAME = /var/www/html/logon/index.php REMOTE_PORT = 52398 REMOTE_USER = MUSER@DOM2.MABOITE.FR AUTH_TYPE = Negotiate GATEWAY_INTERFACE = CGI/1.1 SERVER_PROTOCOL = HTTP/1.1 REQUEST_METHOD = GET QUERY_STRING = REQUEST_URI = /logon/index.php SCRIPT_NAME = /logon/index.php PHP_SELF = /logon/index.php REQUEST_TIME = 1308664098 Connexion LDAP reussie 1 entrees trouvees. dn est : CN=USER\\, MON,OU=Comptes Collaborateurs,OU=Dom2,DC=dom2,DC=maboite,DC=fr premiere entree cn : USER, Mon prenom : Mon nom : USER premier email : MUSER@maboite.fr memberOf [0] : CN=LinuxEXPLOIT,OU=Groupes d'equipes,OU=Dom2,DC=dom2,DC=maboite,DC=fr memberOf [1] : CN=Utilisateurs Internet,OU=Groupes Applicatifs,OU=Dom2,DC=dom2,DC=maboite,DC=fr memberOf [2] : CN=Equipe Systeme,OU=Groupes d'equipes,OU=Dom2,DC=dom2,DC=maboite,DC=fr
Conclusion
Et voilà c’est terminé pour aujourd’hui. Comme nous l’avons vu, il est possible de permettre à des utilisateurs Windows de s’authentifier en toute transparence à un site Web qui fonctionne sous Linux.
Si vous avez des commentaires n’hésitez pas !