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.

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.

Configuration de kerberos

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.

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

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.

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.

Vérifions

Un petit coup de wireshark ne sera pas du luxe.

Dans une console

Obtention du TGT :

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

Dans Firefox

http://apache-krb.maison.mrs

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

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.

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 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 re-formuler 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.