Securité Réseaux Informatiques : Serveur RADIUS - FreeRADIUS

RADIUS

Remote Authentication Dial-In User Service est un système qui fait de « l'AAA » (Authentication, Authorization and Accounting).  Utilisé  depuis   longtemps  déjà  par  bon nombre  de  fournisseurs d'accès à l'internet pour authentifier leurs clients et leur communiquer une configuration IP.

RADIUS est également très utile pour sécuriser un réseau Wi-Fi, ou même un réseau filaire, dans certaines conditions.

Dans ce sous chapitre,  nous verrons comment   installer  et  configurer  un serveur  RADIUS  libre (FreeRADIUS), puis nous le mettrons en oeuvre dans deux situations bien distinctes :
       
  • la sécurisation d'un réseau Wi-Fi (authentification WPA2-TLS),
  • la sécurisation d'un réseau filaire (authentification des stations par adresse MAC).
             
Ce type de solution s'avère fort intéressant si l'on dispose d'un réseau proposant un accès filaire et Wi-Fi,   avec   des   utilisateurs   susceptibles   de   venir   y   connecter   leur   ordinateur   portable.   Par définition,  ce type de machine échappe  totalement  au contrôle de  l'administrateur et  peut  être  la source de bien des soucis.

FreeRADIUS

Avant de commencer...

RADIUS (Remote Authentication Dial-In User Service) est un vaste programme. Pour essayer de faire simple (donc schématique et incomplet), ce service est capable :
              
  • d'authentifier un utilisateur distant, suivant de multiples modes plus ou moins sécurisés, en s'appuyant sur une base connaissances allant du simple fichier texte à l'annuaire LDAP, en passant par une base de données de type SQL,
  • d'enregistrer des informations sur chaque « login »,
  • de   renvoyer   au   demandeur   des   paramètres   variés   pouvant,   suivant   le   cas,   être   une configuration IP, un numéro de LAN virtuel etc.
       
Étudier dans le détail toutes les possibilités de RADIUS est hors de la portée de cet exposé. (c'est, de toutes façons, également hors de ma propre portée). Nous nous contenterons ici de le mettre en oeuvre dans les deux cas qui nous intéressent :
             
  • authentification depuis leur adresse MAC des stations « connues » sur notre réseau filaire, en utilisant  un système de  type « login/password »,  avec  le protocole CHAP  (Challenge-Handshake  Authentication Protocol),   éventuellement   en  assignant  un numéro de  VLAN suivant la machine,
  • authentification avec un certificat x.509 sur le réseau Wi-Fi, en utilisant EAP-TLS.
         
Installer  et   surtout  configurer  un  serveur   radius  pour   la première   fois  a quelque  chose d'assez rebutant,  voire  repoussant.  Nous allons passer  un peu de  temps à détailler  cette opération,  ceci aidera probablement ceux qui n'ont encore jamais tenté l'aventure. Nous utilisons FreeRADIUS sur une Debian Etch.

FreeRadius peut fonctionner en s'appuyant uniquement sur des fichiers texte. Ce n'est pas forcément ce qu'il  y a de plus simple à gérer,  si  l'on doit  manipuler un grand nombre de clients.  Ici,  nous utiliserons MySQL pour stocker les adresses MAC des clients. Outre la souplesse qu'apportent des outils comme phpmyadmin pour gérer la liste des clients, cette solution offre l'avantage de ne pas nécessiter de redémarrage de FreeRADIUS à chaque modification de la base.

Installation de Freeradius

Pour des raisons de compatibilité de licences, FreeRadius est compilé par défaut sur Debian sans le support de TLS (ni de Postgresql). TLS nous servira pour le WPA2. Nous allons donc reconstruire un paquet binaire à partir du paquet source, en tenant compte de cet usage.

Préparatifs

Nous aurons besoin de quelques outils de compilation et de gestion des paquets source :
 
# apt-get install build-essential
...
# apt-get install apt-src



Puis nous devons mettre à jour la liste des paquets source :
   
# apt-src update

Enfin, nous installons le paquet source de FreeRadius dans un répertoire que nous aurons créé dans ce but. La commande apt-src install offre, entre autres, l'avantage d'installer automatiquement les dépendances.
  
# mkdir ~/build_freeradius
# cd ~/build_freeradius
# apt-src install freeradius
   
Nous devons retrouver dans notre répertoire :
   
# ls -l
total 2552
drwxr-xr-x 15 root root 4096 2007-01-11 16:35 freeradius-1.1.3
-rw-r--r-- 1 root root 15130 2006-09-01 20:07 freeradius_1.1.3-3.diff.gz
-rw-r--r-- 1 root root 975 2006-09-01 20:07 freeradius_1.1.3-3.dsc
-rw-r--r-- 1 root root 2587376 2006-09-01 20:07 freeradius_1.1.3.orig.tar.gz


Configuration de la compilation

Dans   le   répertoire   ~/build_freeradius/freeradius-1.1.3/debian   nous   avons   un   fichier   nommé « rules »,   qui   contient   les   directives   de   compilation.  Dans   ce   fichier,   nous   trouvons   dans   les premières lignes, quelques informations qui nous intéressent :
          
# If you want to use SSL and/or the postgres module, comment
# out these two lines and uncomment the two after
# You will also need to add a Build-Depends on libssl-dev and libpq-dev
# and remove the Build-Conflicts on libssl-dev
# Finally you need to cat debian/control.postgresql >> debian/control


Tant que nous y sommes, ajoutons le support de Postgresql, ce qui simplifiera la manipulation et permettra à ceux qui préfèrent, d'utiliser Postgresql plutôt que MySQL.

La première modification est facile à réaliser :
                       
#buildssl=--without-rlm_eap_peap --without-rlm_eap_tls --without-rlm_eap_ttls --without-rlm_otp
          --without-rlm_sql_postgresql --without-snmp
#modulelist=krb5 ldap sql_mysql sql_iodbc
buildssl=--with-rlm_sql_postgresql_lib_dir=`pg_config --libdir`
         --with-rlm_sql_postgresql_include_dir=`pg_config --includedir`
modulelist=krb5 ldap sql_mysql sql_iodbc sql_postgresql
             

La seconde est  peut-être moins évidente si   l'on ne sait  pas qu'il   faut  réaliser   l'opération dans  le fichier ~/build_freeradius/freeradius-1.1.3/debian/control :
                  
Source: freeradius
Build-Depends: debhelper (>= 5), libltdl3-dev, libpam0g-dev, libmysqlclient15-dev | libmysqlclient-
dev, libgdbm-dev,
               libldap2-dev, libsasl2-dev, libiodbc2-dev, libkrb5-dev, snmp, autotools-dev, dpatch
(>= 2),
               libperl-dev, libtool, dpkg-dev (>= 1.13.19), libssl-dev, libpq-dev
Build-Conflicts:         
                
Enfin, il faut ajouter à ce fichier le contenu de control.postgresql :
         
# cd ~/build_freeradius/freeradius-1.1.3/debian
# cat control.postgresql >> control


Comme nous avons un peu bousculé les dépendances et les conflits, nous devons réparer ça :
        
# apt-get install libssl-dev libpq-dev
            

Se protéger des mises à jour de apt-get upgrade

Si nous compilons maintenant le paquet binaire, nous obtiendrons des paquets ayant le même nom (version comprise), que les binaires de la distribution, et les mises à jour futures ne manqueront pas de nous remplacer notre construction à la première occasion.

Une solution élégante consiste à obtenir des paquets binaires, avec un nom différent. Pour ce faire, nous pouvons agir dans le fichier ~/build_freeradius/freeradius-1.1.3/debian/changelog en ajoutant quelques lignes en tête du fichier. Par exemple :
             
freeradius (1.1.3-3tls) unstable; urgency=low
* Add TLS support for compilation
-- Christian Caleca <for.spam.only@eme-enseignement.fr> Fri, 11 Jan 2007 15:46:11 +0100
freeradius (1.1.3-3) unstable; urgency=medium
* Fix POSIX compliance problem in init script. Closes: #403384.
-- Mark Hymers <mark@hymers.orgue.uk> Sat, 16 Dec 2006 20:45:11 +0000
...                      
                Il nous reste à construire les binaires :
                 
# cd ~/build_freeradius
# apt-src build freeradius
...
...
...
I: Successfully built in /root/build_freeradius/freeradius-1.1.3
# ls -l | grep deb$
-rw-r--r-- 1 root root 761896 2007-01-11 18:10 freeradius_1.1.3-3tls_i386.deb
-rw-r--r-- 1 root root 114398 2007-01-11 18:09 freeradius-dialupadmin_1.1.3-3tls_all.deb
-rw-r--r-- 1 root root 31892 2007-01-11 18:10 freeradius-iodbc_1.1.3-3tls_i386.deb
-rw-r--r-- 1 root root 32586 2007-01-11 18:10 freeradius-krb5_1.1.3-3tls_i386.deb
-rw-r--r-- 1 root root 46894 2007-01-11 18:10 freeradius-ldap_1.1.3-3tls_i386.deb
-rw-r--r-- 1 root root 31836 2007-01-11 18:10 freeradius-mysql_1.1.3-3tls_i386.deb
-rw-r--r-- 1 root root 32280 2007-01-11 18:10 freeradius-postgresql_1.1.3-3tls_i386.deb 
  

Nous n'avons ici besoin que de deux de ces paquets :

# dpkg -i freeradius_1.1.3-3tls_i386.deb freeradius-mysql_1.1.3-3tls_i386.deb



Vérifions que Freeradius est bien lancé :
  
# ps aux | grep radius
freerad 4118 0.0 0.8 44608 2224 ? Ssl 18:16 0:00 /usr/sbin/freeradius


Nous devons maintenant préparer une base Mysql et configurer FreeRadius pour qu'il s'en serve.

Configuration de Mysql

Nous supposons que Mysql est correctement installé. Nous utilisons ici :
      
# mysql -V
mysql Ver 14.12 Distrib 5.0.30, for pc-linux-gnu (i486) using readline 5.2
     

Création de la Base « radius » et de l'utilisateur du même nom :
             
# mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 5.0.30-Debian_3-log Debian etch distribution
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> create database radius;
Query OK, 1 row affected (0.04 sec)
mysql> grant all on radius.* to radius@'localhost' identified by 'epikoi';
Query OK, 0 rows affected (0.06 sec)
mysql> exit
Bye
       

La base est créée mais elle reste vide. Pour créer les tables, le paquet freeradius-mysql nous donne le schéma dans /usr/share/doc/freeradius/examples/mysql.sql.gz :
       
# zcat /usr/share/doc/freeradius/examples/mysql.sql.gz | mysql -u root -p radius
Enter password:
#

Tout semble s'être correctement passé. Vérification :
            
# mysql -u radius -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 48
Server version: 5.0.30-Debian_3-log Debian etch distribution
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> connect radius
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Connection id: 49
Current database: radius
mysql> show tables;
+------------------+
| Tables_in_radius |
+------------------+
| nas                       |
| radacct                 |
| radcheck              |
| radgroupcheck     |
| radgroupreply      |
| radpostauth         |
| radreply              |
| usergroup           |
+------------------+
8 rows in set (0.00 sec)
mysql>
       

Configuration de FreeRadius

/etc/freeradius/radiusd.conf

Assurez-vous que seules les options définies dans ce qui suit sont activées (certaines options dans radiusd.conf sont à dé commenter, d'autres sont à commenter).


Dans la section « authorize »
  
authorize {
        preprocess
        eap
        sql
}
 
Dans la section « authenticate »
 
authenticate {
        Auth-Type CHAP {
          chap
        }
        eap
}
 
Dans la section « accounting »
  
accounting {
        detail
        radutmp
        sql
}
 
(Notez   que   cette   section   ne   concerne   pas   l'authentification   et   qu'il   est   donc   possible   de   la supprimer).

Dans la section « session »
    
session {
        sql
}
       
Voici un exemple complet de configuration, qui correspond à notre besoin du moment :

                     
prefix = /usr
exec_prefix = /usr
sysconfdir = /etc
localstatedir = /var
sbindir = ${exec_prefix}/sbin
logdir = /var/log/freeradius
raddbdir = /etc/freeradius
radacctdir = ${logdir}/radacct
confdir = ${raddbdir}
run_dir = ${localstatedir}/run/freeradius
log_file = ${logdir}/radius.log
libdir = /usr/lib/freeradius
pidfile = ${run_dir}/freeradius.pid
user = freerad
group = freerad
max_request_time = 30
delete_blocked_requests = no
cleanup_delay = 5
max_requests = 1024
bind_address = *
port = 0
hostname_lookups = no
allow_core_dumps = no
regular_expressions     = yes
extended_expressions    = yes
log_stripped_names = no
log_auth = no
log_auth_badpass = no
log_auth_goodpass = no
usercollide = no
lower_user = no
lower_pass = no
nospace_user = no
nospace_pass = no
checkrad = ${sbindir}/checkrad
security {
        max_attributes = 200
        reject_delay = 1
        status_server = no
}
$INCLUDE  ${confdir}/clients.conf
snmp    = no
thread pool {
        start_servers = 5
        max_servers = 32
        min_spare_servers = 3
        max_spare_servers = 10
        max_requests_per_server = 0
}
modules {
        chap {
                authtype = CHAP
        }
$INCLUDE ${confdir}/eap.conf
        checkval {
                item-name = Calling-Station-Id
                check-name = Calling-Station-Id
                data-type = string
        }
        preprocess {
                huntgroups = ${confdir}/huntgroups
                hints = ${confdir}/hints
                with_ascend_hack = no
                ascend_channels_per_line = 23
                with_ntdomain_hack = no
                with_specialix_jetstream_hack = no
                with_cisco_vsa_hack = no
        }
        files {
                usersfile = ${confdir}/users
                compat = no
        }
        detail {
                detailfile = ${radacctdir}/%{Client-IP-Address}/detail-%Y%m%d
                detailperm = 0600
        }
        acct_unique {
                key = "User-Name, Acct-Session-Id, NAS-IP-Address, Client-IP-Address, NAS-Port"
        }
        $INCLUDE  ${confdir}/sql.conf
        radutmp {
                filename = ${logdir}/radutmp
                username = %{User-Name}
                case_sensitive = yes
                check_with_nas = yes
                perm = 0600
                callerid = "yes"
        }
        radutmp sradutmp {
                filename = ${logdir}/sradutmp
                perm = 0644
                callerid = "no"
        }
        attr_filter {
                attrsfile = ${confdir}/attrs
        }
        counter daily {
                filename = ${raddbdir}/db.daily
                key = User-Name
                count-attribute = Acct-Session-Time
                reset = daily
                counter-name = Daily-Session-Time
                check-name = Max-Daily-Session
                allowed-servicetype = Framed-User
                cache-size = 5000
        }
        sqlcounter dailycounter {
                counter-name = Daily-Session-Time
                check-name = Max-Daily-Session
                sqlmod-inst = sql
                key = User-Name
                reset = daily
                query = "SELECT SUM(AcctSessionTime - \
                 GREATEST((%b - UNIX_TIMESTAMP(AcctStartTime)), 0)) \
                 FROM radacct WHERE UserName='%{%k}' AND \
                 UNIX_TIMESTAMP(AcctStartTime) + AcctSessionTime > '%b'"
        }
        sqlcounter monthlycounter {
                counter-name = Monthly-Session-Time
                check-name = Max-Monthly-Session
                sqlmod-inst = sql
                key = User-Name
                reset = monthly
                query = "SELECT SUM(AcctSessionTime - \
                 GREATEST((%b - UNIX_TIMESTAMP(AcctStartTime)), 0)) \
                 FROM radacct WHERE UserName='%{%k}' AND \
                 UNIX_TIMESTAMP(AcctStartTime) + AcctSessionTime > '%b'"
        }
        always fail {
                rcode = fail
        }
        always reject {
                rcode = reject
        }
        always ok {
                rcode = ok
                simulcount = 0
                mpp = no
        }
        expr {
        }
        digest {
        }
        exec {
                wait = yes
                input_pairs = request
        }
        exec echo {
                wait = yes
                program = "/bin/echo %{User-Name}"
                input_pairs = request
                output_pairs = reply
        }
}
authorize {
        preprocess
        sql
        eap
}
authenticate {
        Auth-Type CHAP {
                chap
        }
        eap
}

preacct {
        preprocess
        acct_unique
        files
}
session {
        sql
}

/etc/freeradius/sql.conf

Les fichiers de configuration de FreeRadius se trouvent dans /etc/freeradius.  Commençons par le plus « simple » (en réalité, il n'est pas simple du tout, mais il y a peu de choses à y faire). Aménagez en fonction de vos choix lors de la création de la base. Voici un exemple complet, qui est dépouilléde la partie « accounting » :

sql {
        driver = "rlm_sql_mysql"
        server = "localhost"
        login = "radius"
        password = "epikoi"
        radius_db = "radius"
        acct_table1 = "radacct"
        acct_table2 = "radacct"
        postauth_table = "radpostauth"
        authcheck_table = "radcheck"
        authreply_table = "radreply"
        groupcheck_table = "radgroupcheck"
        groupreply_table = "radgroupreply"
        usergroup_table = "usergroup"
        nas_table = "nas"
        deletestalesessions = yes
        sqltrace = no
        sqltracefile = ${logdir}/sqltrace.sql
        num_sql_socks = 5
        connect_failure_retry_delay = 60
        sql_user_name = "%{User-Name}"
        authorize_check_query = "SELECT id, UserName, Attribute, Value, op \
          FROM ${authcheck_table} \
          WHERE Username = '%{SQL-User-Name}' \
          ORDER BY id"
        authorize_reply_query = "SELECT id, UserName, Attribute, Value, op \
          FROM ${authreply_table} \
          WHERE Username = '%{SQL-User-Name}' \
          ORDER BY id"
        authorize_group_check_query = "SELECT ${groupcheck_table}.id, \
          ${groupcheck_table}.GroupName,${groupcheck_table}.Attribute, \
          ${groupcheck_table}.Value,${groupcheck_table}.op \
          FROM ${groupcheck_table},${usergroup_table} \
          WHERE ${usergroup_table}.Username = '%{SQL-User-Name}' \
          AND ${usergroup_table}.GroupName = ${groupcheck_table}.GroupName \
          ORDER BY ${groupcheck_table}.id"
        authorize_group_reply_query = "SELECT ${groupreply_table}.id, \
          ${groupreply_table}.GroupName,${groupreply_table}.Attribute, \
          ${groupreply_table}.Value,${groupreply_table}.op  \
          FROM ${groupreply_table},${usergroup_table} \
          WHERE ${usergroup_table}.Username = '%{SQL-User-Name}' \
          AND ${usergroup_table}.GroupName = ${groupreply_table}.GroupName \
          ORDER BY ${groupreply_table}.id"
        simul_verify_query = "SELECT RadAcctId, AcctSessionId, UserName, \
          NASIPAddress, NASPortId, FramedIPAddress, CallingStationId, FramedProtocol \
          FROM ${acct_table1} \
          WHERE UserName='%{SQL-User-Name}' \
          AND AcctStopTime = 0"
        group_membership_query = "SELECT GroupName FROM ${usergroup_table} \
          WHERE UserName='%{SQL-User-Name}'"
        postauth_query = "INSERT into ${postauth_table} (id, user, pass, reply, date) \
          values ('', '%{User-Name}', '%{User-Password:-Chap-Password}', \
          '%{reply:Packet-Type}', NOW())"
        readclients = yes
}

/etc/freeradius/eap.conf

Nous avons décidé, en préparant notre système Wi-Fi,  d'utiliser EAP-TLS pour l'authentification des utilisateurs. Lors de la création des certificats pour WPA2, nous avons créé :
                  
  • root_maison_CA-cacert.pem qui est le certificat de notre racine de confiance,
  • sysop@maison.mrs-cert.pem qui est le certificat du serveur FreeRADIUS. Nous l'avons créé de manière à ce qu'il contienne la clé privée du serveur.
         
Nous allons utiliser ici ces deux certificats, qu'il faut placer dans le répertoire /etc/freeradius/certs.

Ce répertoire devrait contenir :

/etc/freeradius/certs# ls -l
total 12
-rw-r----- 1 root freerad    0 2007-03-12 11:11 dh
-rw-r----- 1 root freerad 3242 2007-03-12 15:38 maison.mrs-cert.pem
-rw-r----- 1 root freerad 1024 2007-03-12 11:11 random
-rw-r----- 1 root freerad 2610 2007-03-12 15:25 root_maison_CA-cacert.pem


Faites attention aux droits d'accès des  fichiers de ce  répertoire.   Il  suffit  maintenant  de modifier eap.conf de la sorte :

        eap {
                default_eap_type = tls
                timer_expire     = 60
                ignore_unknown_eap_types = no
                cisco_accounting_username_bug = no
                tls {
                        private_key_password = epikoi
                        private_key_file = ${raddbdir}/certs/sysop@maison.mrs-cert.pem
                        certificate_file = ${raddbdir}/certs/sysop@maison.mrs-cert.pem
                        CA_file = ${raddbdir}/certs/root_maison_CA-cacert.pem
                        CA_path = ${raddbdir}/certs/
                        dh_file = ${raddbdir}/certs/dh
                        random_file = ${raddbdir}/certs/random
                        fragment_size = 1024
                        include_length = yes
                        check_crl = no
                }
        }

N'oublions pas de relancer FreeRadius pour qu'il prenne en compte la nouvelle configuration.

Premier test

Nous créons un « authenticator » de test dans la table « nas » :
       
# echo "INSERT INTO nas(nasname,shortname,secret) VALUES ('127.0.0.1','localhost','naspassword');" \
 | mysql -u root -p radius


Nous créons un utilisateur de test dans « radcheck » :
   
# echo "INSERT INTO radcheck(UserName,Attribute,op,Value)
 VALUES ('test0','User-Password','==','userpassword');" \
 | mysql -u root -p radius

Notez que l'on utilise un mot de passe en clair dans la base,  ce qui correspondra à un protocole « chap » pour l'authentification.

Enfin,  depuis  le serveur radius  lui-même,  qui  va pour  l'occasion cumuler  le rôle d'authenticator, nous utilisons l'outil radtest :
        
# radtest test0 userpassword 127.0.0.1 0 naspassword
Sending Access-Request of id 146 to 127.0.0.1 port 1812
User-Name = "test0"
User-Password = "userpassword"
NAS-IP-Address = 255.255.255.255
NAS-Port = 0
rad_recv: Access-Accept packet from host 127.0.0.1:1812, id=146, length=20

Notre solution fonctionne. Il ne nous reste qu'à ajouter dans la table « nas » nos switchs , nos points d'accès Wi-Fi, et dans la table « radcheck » toutes nos adresses MAC en guise d'utilisateurs pour le réseau filaire (« UserName » et « User-Password » identiques).

Pour les VLANs

La gestion des adresses MAC des clients

Il  existe plusieurs méthodes pour  collecter   les adresses MAC de vos postes clients,  comme par exemple les logiciels d'inventaire de parc (OCS Inventory NG, en est un).

La table « radcheck »

La   table   qui   doit   accueillir   les   « utilisateurs »  s'appelle   « radcheck ».  Voyons   de   plus   près   la structure de cette table :
       
                
Entrer  dans  le détail  des nombreuses possibilités de FreeRADIUS nous mènerait  beaucoup  trop loin. Dans le cadre de notre projet, nous devons créer une ligne par client de la manière suivante :
               
  • Username   contiendra   l'adresse  MAC  du   client,   au   format   xx:yy:zz:aa:bb:cc,   en   lettres minuscules,
  • Attribute contiendra le texte : « User-Password »,
  • op contiendra « == »,
  • Value contiendra également l'adresse MAC du client, au format xx:yy:zz:aa:bb:cc, en lettres minuscules.
                   
Comme  pour  gérer  dans   la  suite  une  telle  collection de valeurs,   il   faudra   savoir   à quel   client correspond une adresse MAC,  je vous conseille vivement d'ajouter dans cette table une rubrique supplémentaire destinée à contenir, par exemple, le nom du client associé :
     
       

Il   « suffit »   ensuite   de   peupler   cette   base   avec   vos   adresses  MAC  par   la  méthode   qui   vous conviendra le mieux. Ceci devrait aboutir à quelque chose de la forme :
 
mysql> select *  from radcheck;
      

                   

Il n'est pas nécessaire de relancer FreeRADIUS après modification de cette table.

La table « nas »

Il  ne  faut  pas  oublier  non plus  d'ajouter  dans   la   table « nas »  les   informations  concernant  vos SWITCHs. La table « nas » est de structure suivante :
            
              

Nous devons avoir dans cette table, quelque chose qui ressemble à ceci :
          
          

Notez   qu'une  modification   de   la   table   « nas »   nécessite   (pour   l'instant)   un   redémarrage   de FreeRADIUS.

Normalement,   tout  devrait   fonctionner  correctement.  Le client  d'adresse MAC 00:05:5d:df:f4:5b devrait se retrouver sur le VLAN d'IP 2 (PARADIS_VLAN) alors qu'un client d'adresse MAC ne figurant pas dans la table « radcheck » se retrouvera dans le VLAN d'ID 3 (ENFER_VLAN).

Si vous rencontrez des problèmes, la première chose à faire est d'arrêter FreeRADIUS, puis de le relancer en « foreground » (avant-plan) en mode « debug », par la commande :
   
freeradius -x
 

ou, pour encore plus de détails :

freeradius -xx
    

FreeRADIUS est assez verbeux dans ce mode. Si, lors de la connexion d'un client, vous n'observez rien, c'est tout simplement que le switch ne dialogue pas avec le radius. Il faut alors en trouver la raison.

Si   le   dialogue   démarre,   il   vous   faudra   déchiffrer   le   discours   pour   trouver   la   raison   du disfonctionnement. Les raisons les plus probables étant :
                
  • le radius ne reconnaît pas le switch (problème de secret partagé, de configuration du radius sur le switch, de configuration du switch dans la table « nas » du radius),
  • le radius ne trouve pas le client dans sa base (erreur dans la table « radcheck »).
    
Pour WPA2

Configuration de eap

La table « nas »

Il nous faut ajouter notre borne Wi-Fi dans la table « nas ». Ici, nous utilisons une borne de type Netgear GW302, qui aura dans notre réseau l'adresse IP 192.168.10.3, comme nom « netgear » et qui  utilisera « re-chutt » comme secret  partagé avec  le serveur radius.  Nous devrions avoir dans notre table, quelque chose de ce genre :
        

             

Gestion des certificats des clients

Ici, la configuration va être extrêmement simple. Comme nous avons choisi d'utiliser WPA2-TLS, qui  nécessite un certificat  chez le client,  il n'y aura à priori pas de base de noms d'utilisateurs à construire. Le serveur RADIUS va se contenter de vérifier l'authenticité du certificat présenté par le client. Si un client dispose d'un certificat valide, c'est bien qu'il est autorisé à se connecter.

Installation des certificats sur une machine Windows

Certificat de l'autorité
           

                

Dans   le cas  de Windows  XP,  nous avons  besoin du  certificat  de  notre racine   de   confiance (root_maison_CA)   ainsi   que   du certificat   client   (user1).   Ces certificats   sont   à   installer   par l'utilisateur et serviront à authentifier l'utilisateur.   Entendez   par   là   deux
points importants :
                
  • l'attachement  à  la borne Wi-Fi ne pourra se faire qu'après ouverture d'une session,
  • seule   la   session   de l'utilisateur   qui   aura   installé les   certificats   pourra permettre un attachement.

Commençons  par   le certificat  de  la racine   de   confiance.   Double   clic dessus, ce qui nous amène à :
                       
                
Cette Autorité de certification étant inconnue   de   Microsoft,   il   nous appartient  de décider si  oui  ou non, nous   l'acceptons.  Nous   allons   bien sûr   installer   ce   certificat,   non   sans être   certain   que   c'est   bien   le   bon. Autrement,  dit,  il nous appartient de vérifier   que   ce   certificat   provient bien de chez nous.

« Installer   ce   certificat... »   va   nous conduire à un assistant :
           


dont   le  texte explicatif  ne nécessite aucun commentaire.
Suivant :
         

        
Nous   allons   choisir   nous   même l'emplacement   où   ce   certificat   sera stocké.
« Parcourir... »
                    


Nous   choisissons   logiquement   les Autorités  de certification  racines de confiance.
              


L'assistant a fini son travail.
          


Mais, tout de même, nous avons encore droit à un bon gros avertissement de sécurité.
                 


Encore une fois, si nous sommes certains de l'origine de ce certificat, nous pouvons y aller.



Certificat du client
Double clic sur le certificat client.

Ceci démarre un nouvel
assistant.
            


La   seconde   permet   de vérifier   le   chemin d'accès   au   certificat   que nous   voulons   enregistrer.
Suivant >
Suivant >
        
         

Le   mot   de   passe demandé ici est celui qui a   été   spécifié   lors   de l'exportation   au   format PKCS#12 avec TinyCA (« Export password »).
          


Ici,  nous pouvons choisir de sélectionner automatiquement   le   magasin.
Suivant >

Le reste ne présente pas d'intérêt particulier, l'installation doit se terminer sans encombres.

WPA2 et Windows XP

Windows XP, même avec le SP2, ne gère pas WPA2. Il faut lui ajouter un correctif à télécharger chez  Microsoft   (validation  d'intégrité   « Genuine  Advantage »   requise).  Ce   correctif   s'appelle :

WindowsXP-KB893357-v2-x86-FRA.exe. A l'heure où ces lignes sont écrites, ce lien est valide.
Sans ce correctif, vous devrez vous contenter de WPA.

Une fois tout ceci réalisé, avec un peu de chance, la connexion devrait s'établir automatiquement pour l'utilisateur qui a installé les certificats.
En cas de problèmesVoici quelques éléments qui devraient vous aider à trouver la solution.

Voir les certificats installés


Une console « mmc » va nous venir en aide. Créons une console de gestion des certificats.


exécuter mmc...
           

Ajouter   un   composant logiciel enfichable...
       
            

Choisir   le   composant
« certificats »...
« Fermer »
          
              

Si vous avez un compte d'administrateur (ce qui, rappelons   le,   n'est   pas une bonne idée, même si c'est   le   comportement par  défaut  de WIndows XP),  vous aurez droit  à choisir  s'il  s'agit  de vos certificats   (utilisateur) ou   d'autres   certificats, comme   ceux   qui
concernent l'ordinateur.

Si votre compte est celui d'un   simple   utilisateur, vous   n'aurez   pas   ce choix   et   ne   pourrez gérer   que   vos   propres certificats,   ce   qui   est suffisant dans notre cas.
« Terminer »
          


Si nous double cliquons
sur le certificat :
           


Nous  voyons   apparaître la   racine   de   confiance associée.
    
            

La suite sous entend que les connexions wi-fi sont gérées par les outils Windows. Même si l'on peut admettre   qu'ils   sont   plutôt   rébarbatifs,   peu   ergonomiques   à   première   vue,   mon   expérience personnelle ne m'en a pas fait découvrir de meilleurs parmi les nombreux « gadgets » fournis par les constructeurs de matériel wi-fi.

Faites apparaître les propriétés des connexions réseau sans fils :
Sélectionnez   votre   réseau et affichez ses propriétés,
           
      

Sur   l'onglet   « Association »,  assurez-vous que vous   êtes   bien   sur   le mode  WPA2,   avec   un chiffrement AES,



Sur   l'onglet   « Authentification »,   le   type  EAP doit  être « Carte à puce ou autre certificat ». Affichez ses propriétés,
             
               

Utilisez   la   sélection   de certificat   recommandée (simple),   indiquez   le nom complet du serveur RADIUS,   tel  qu'il  a été défini   dans   son   certificat, sélectionnez enfin le certificat   de   la   racine d'autorité.

Ceci   devrait   enlever toute   ambiguïté,   en   cas de troubles.
       

        
Installation du certificat sur une machine Linux

Contrairement à Windows, le moyen le plus simple d'obtenir l'attachement à un réseau Wi-Fi est de le faire au niveau du système.  Entendez par  là que  l'authentification se fera lors du montage du réseau, au démarrage du système, ou lors de l'activation de l'interface Wi-Fi. Autrement dit, ce ne sera pas l'utilisateur qui sera authentifié, mais l'administrateur de la machine.

La manipulation est   faite sur  une distribution Ubuntu 6.10,  où wpa-supplicant  est  normalement installé par défaut. Il y a sur cette distribution, deux interventions à faire. La première est typique aux   distributions   basées   sur  Debian   et   il   vous   faudra   trouver   comment   adapter   à   une   autre distribution.

L'interface  Wi-Fi   est  une   carte  PCMCIA  (cardbus)  D-Link DWL-G650,  qui  utilise  un  chipset Atheros (Driver Madwifi).

Copie des certificats

L'installation du paquet  wpasupplicant  a créé un  répertoire  /etc/wpa_supplicant/.  Nous  pouvons créer dedans un répertoire « certs » et y mettre dedans nos deux certificats :
         
  • root_maison_CA-cacert.pem pour l'autorité de certification,
  • user1@maison.mrs-cert.pem pour le client.

Comme le contenu de ce répertoire est utilisé lors du démarrage,  il n'y a aucune raison qu'il soit accessible par quiconque d'autre que root.

Configuration de l'interface wi-fi


Sur les Distributions Debian et dérivées, il faut agir sur le fichier /etc/network/interfaces :
           
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
# The loopback network interface
auto lo
iface lo inet loopback
auto ath0
iface ath0 inet dhcp
        wpa-driver madwifi
        wpa-conf /etc/wpa_supplicant.conf
...
               

Configuration de wpa-supplicant

wpa-supplicant mériterait à lui seul tout un chapitre. Son fonctionnement n'est pas d'une évidence extrême, et sa configuration non plus.

Il faut créer un fichier /etc/wpa_supplicant.conf,  comme indiqué dans /etc/network/interfaces, qui indiquera, réseau par réseau (ici, un seul suffira), les paramètres nécessaires à l'attachement.

Voici un exemple de configuration dans notre contexte :
         
ctrl_interface=/var/run/wpa_supplicant
ap_scan=1
network={
        ssid="maison"
        scan_ssid=0
        key_mgmt=WPA-EAP
        eap=TLS
        proto=WPA2
        pairwise=CCMP TKIP
        group=CCMP TKIP
        identity="user1"
        ca_cert="/etc/wpa_supplicant/certs/root_maison_CA-cacert.pem"
        client_cert="/etc/wpa_supplicant/certs/user1@maison.mrs-cert.pem"
        private_key="/etc/wpa_supplicant/certs/user1@maison.mrs-cert.pem"
        private_key_passwd="epikoi"
}
        

Il n'y a rien d'incompréhensible dans ce fichier, la difficulté réside surtout dans le fait d'utiliser les bons   paramètres.   Pour   vous   aider,   vous   avez   dans   le   répertoire /usr/share/doc/wpasupplicant/examples/   quelques   exemples   de   configuration   ainsi   qu'un   fichier « wpa_supplicant.conf.gz »   très   largement   documenté,   qui   passe   en   revue   tous   les   paramètres possibles.


C'est bien mais...

Cette  solution ne nous  permet  pas  de gérer   les  « coups  durs ».  Comment   faire en effet   si   l'on apprend qu'un certificat encore valide a été compromis ? Car dans cette configuration, une fois un certificat installé, il sera accepté par FreeRADIUS tant qu'il n'aura pas expiré.

Révocations

Comment gérer les impondérables ?

Dans   notre   solution   globale,   nous   gérons   le   réseau   filaire   par   une   authentification   de   type « login/password » où l'adresse MAC joue le rôle des deux composants. Nous disposons d'une base de données MySQL (ou Postgresql) qui recense toutes les adresses MAC connues. Nous avons pu tester son bon fonctionnement en utilisant un « login/password » introduit dans la base de données.

En ce qui concerne le réseau Wi-Fi, nous avons confié à EAP l'authentification,  via le protocole TLS. Nous avons vu que, dans ce cas, tout se passe par l'intermédiaire des certificats, et qu'il n'y a aucune information dans la base de données.

Comment   faire alors   si  pour  une  raison ou une autre,  nous  devions  être amenés  à bloquer  un utilisateur  disposant  d'un  certificat   en  cours  de  validité ?   Il  peut  y  avoir  plusieurs   raisons  qui pourraient amener à cette résolution, comme :
                   
  • un vol déclaré de la machine sur laquelle le certificat a été installé,
  • un utilisateur autorisé pour une certaine durée, mais qui pour une raison ou une autre, n'a plus rien à faire sur notre réseau, temporairement ou définitivement (non respect de la charte des utilisateurs, démission, etc.).
                
Dans notre configuration actuelle,  ce type de situation ne peut être géré, il nous faut trouver une solution, si possible pas trop complexe à maintenir.Deux voies sont à explorer :
         
  • l'utilisation de certificats de révocation,
  • trouver un moyen pour qu'en plus de  l'authentification par certificats,  le nom d'utilisateur doive être présent dans la base de données pour autoriser l'attachement.

Certificat de révocation

TinyCA sait générer simplement des certificats de révocation, et FreeRADIUS peut être configuré assez simplement pour en tenir compte.  Cette solution offre cependant deux gros défauts pour la maintenance :
           
  • à chaque nouvelle  suspension de compte,   il   faut   révoquer   le certificat  correspondant  au compte suspendu, recréer un nouveau certificat de révocations, l'exporter, l'installer sur le serveur puis redémarrer le serveur,
  • en cas de suspension provisoire, un certificat révoqué doit être recréé puis réinstallé sur la machine cliente à la fin de la suspension du compte.
       
Usage de la base de données

Etat des lieux

Si  nous   trouvons  un moyen pour  que  le certificat  client  ne  soit  accepté que  si   l'utilisateur  est référencé dans la base, les choses deviendraient beaucoup plus simples, il suffirait d'ajouter ou de supprimer une ligne dans la base pour suspendre, temporairement ou non, un compte d'utilisateur, même si le certificat est encore en cours de validité.

Cette solution nous obligera à créer une ligne par utilisateur Wi-Fi dans notre base de données, en plus du certificat, mais nous permettra de gérer nettement plus facilement les impondérables.

Pour arriver à ce résultat, il va nous falloir entrer plus en profondeur dans le fonctionnement  de RADIUS. Dans notre configuration très simple, nous ne définissons pas de type d'authentification approprié à chaque requête. Le résultat est que pour chaque requête, tous les types d'authentification possibles sont testés, si l'un d'entre eux renvoie « Accept », l'autorisation est donnée.

Actuellement, nous avons :
Cette solution n'est clairement pas facile à gérer.
  
authorize {
        preprocess
        eap
        sql
}
 

C'est à dire que nous autoriserons les connexions soit par EAP, soit par la base de données sql, et nous  n'avons  défini  nulle part  de  façon explicite   le mode d'authentification pour  un utilisateur donné. Le résultat est que :
           
  • tout client présentant un certificat valide sera autorisé via « eap »,
  • tout  client  présentant  un « login/password » présent  dans   la base  sera autorisé en mode « local » via « sql ».
          
Que se passerait-il, si nous trouvions le moyen de fixer le mode d'authentification par défaut ?

Fixer un mode d'authentification par défaut

Ceci peut se réaliser par l'intermédiaire du fichier « users », à la condition d'ajouter « files » dans la section « authorize » de radiusd.conf. Si nous arrivons à forcer le serveur à rejeter par défaut toute authentification,  puis à  l'autoriser  à utiliser  un mode particulier  pour  un utilisateur  donné,  nous serons proches de la solution.

Fichier « users »

Nous  mettons  dans  « users »  la   simple   ligne  « DEFAULT Auth-Type   := Reject ».  Par  défaut, l'autorisation devrait ainsi être refusée à tout le monde :
              
# cat users
DEFAULT Auth-Type := Reject


Le problème étant maintenant  d'attribuer un type d'authentification correct aux utilisateurs Wi-Fi connus. Il faut le faire avant de définir le mode par défaut. Fort heureusement, les autorisations sont lues séquentiellement dans la section « authorize ».
      
Fichier « radiusd.conf »

authorize {
        preprocess
        sql
        files
        eap
}
          
A part que, avec cette écriture, lorsque l'on sort d'un module on entre dans le suivant, quelle que soit la réponse du module précédent, pourvu qu'il n'y ait pas eu de « Reject ». Ici, même si SQL accorde un « Auth-Type := EAP » à un utilisateur connu,  cette valeur sera écrasée par le Auth-Type par défaut du fichier « users »; et donc personne ne sera autorisé, ce n'est pas ce que nous voulons.

Si nous plaçons files avant sql, ça ne fonctionnera pas non plus, puisque le type d'authentification « Reject » sera rencontré en premier, mettant fin à la procédure.

Fort heureusement, le mode « fail-over » va venir à notre secours sous cette forme :
     
authorize {
        preprocess
        redundant {
                sql
                files
                eap
        }
}
       

Ainsi, lorsqu'un utilisateur est trouvé dans la base sql, soit il sera authentifié en mode Local, si son mot  de  passe   est   indiqué   (pour   les   autorisations  depuis   les   adresses  MAC),   soit,   si  un mode d'authentification de type TLS lui est attribué, EAP fonctionnera.

Si en revanche aucun mode d'authentification n'est attribué, c'est le mode par défaut qui passera et, même avec un certificat valide, l'utilisateur devrait être rejeté.
        
Essayons un « login/password » :

Sending Access-Request of id 25 to 127.0.0.1 port 1812
        User-Name = "test0"
        User-Password = "userpassword"
        NAS-IP-Address = 255.255.255.255
        NAS-Port = 0
rad_recv: Access-Accept packet from host 127.0.0.1:1812, id=25, length=20
          

C'est bon. Essayons maintenant  le Wi-Fi avec un certificat valide, présentant le nom d'utilisateur userwifi1,  non  répertorié dans  la base des utilisateurs,   l'authentification échouera.  Si  maintenant nous ajoutons ceci dans la base :
        
# echo "INSERT INTO radcheck(UserName,Attribute,op,Value)
 VALUES ('userwifi1','Auth-Type',':=','EAP');" \
 | mysql -u root -p radius
     


L'authentification eap devrait réussir. Voyons un « best-of » des logs de freeradius lancé en mode « debug » : 
           
rad_recv: Access-Request packet from host 192.168.10.3:1046, id=19, length=163
        User-Name = "userwifi1"
        NAS-IP-Address = 192.168.10.3
        Connect-Info = "CONNECT 802.11"
        Called-Station-Id = "000f3dab9ac9"
        Calling-Station-Id = "000fb572abe0"
        NAS-Identifier = "00-0f-b5-72-ab-e0"
        NAS-Port-Type = Wireless-802.11
        NAS-Port = 1
        NAS-Port-Id = "1"
        Framed-MTU = 1400
        EAP-Message = 0x0201001201636c6e74302e6274732e656d65
        Message-Authenticator = 0x417f19394ab35a5eda42ffb628855e41
rlm_sql (sql): Reserving sql socket id: 3
rlm_sql (sql): Released sql socket id: 3
Sending Access-Challenge of id 19 to 192.168.10.3 port 1046
        EAP-Message = 0x010200060d20
        Message-Authenticator = 0x00000000000000000000000000000000
        State = 0xee660260336863b8917d0c319918e83e
...
...
...
error=0
--> User-Name = userwifi1
--> BUF-Name = root_maison_CA
--> subject =
/C=FR/ST=France/L=Marseille/O=Maison/OU=Reseau_maison/CN=root_maison_CA/emailAddress=user1@maison.mr
s
--> issuer  =
/C=FR/ST=France/L=Marseille/O=Maison/OU=Reseau_maison/CN=root_maison_CA/emailAddress=user1@maison.mr
s
--> verify return:1
chain-depth=0,
error=0
--> User-Name = userwifi1
--> BUF-Name = userwifi1
--> subject =
/C=FR/ST=France/L=Marseille/O=Maison/OU=Reseau_maison/CN=userwifi1/emailAddress=userwifi1
--> issuer  =
/C=FR/ST=France/L=Marseille/O=Maison/OU=Reseau_maison/CN=root_maison_CA/emailAddress=user1@maison.mr
s
--> verify return:1
rlm_eap: SSL error error:00000000:lib(0):func(0):reason(0)
Sending Access-Challenge of id 26 to 192.168.10.3 port 1046
        EAP-Message = 0x010900350d800000002b1403010001011603010020bea471f3b5...
        Message-Authenticator = 0x00000000000000000000000000000000
        State = 0xf22d9e05e6fc415b67c3b36def7a809d
rad_recv: Access-Request packet from host 192.168.10.3:1046, id=27, length=169
        User-Name = "userwifi1"
        NAS-IP-Address = 192.168.10.3
        Connect-Info = "CONNECT 802.11"
        Called-Station-Id = "000f3dab9ac9"
        Calling-Station-Id = "000fb572abe0"
        NAS-Identifier = "00-0f-b5-72-ab-e0"
        NAS-Port-Type = Wireless-802.11
        NAS-Port = 1
        NAS-Port-Id = "1"
        Framed-MTU = 1400
        State = 0xf22d9e05e6fc415b67c3b36def7a809d
        EAP-Message = 0x020900060d00
        Message-Authenticator = 0x1a08bc62c3852a0b6436c016ba81dfd6
rlm_sql (sql): Reserving sql socket id: 0
rlm_sql (sql): Released sql socket id: 0
Sending Access-Accept of id 27 to 192.168.10.3 port 1046
        MS-MPPE-Recv-Key = 0xd6a70248ae96a9e5621d6053bad74de70db8b358ad3e79bcda1351ec50375873
        MS-MPPE-Send-Key = 0xf418a1cf92136b7e5b948dd0fbf51a0b7a95b06456abc4e15c4df200e57cc29c
        EAP-Message = 0x03090004
        Message-Authenticator = 0x00000000000000000000000000000000
        User-Name = "userwifi1"
           

Vérifions tout de même que si l'utilisateur est supprimé de la table « radcheck », l'authentification va échouer :
image

Le log de freeradius deviendra beaucoup plus court :
          
rad_recv: Access-Request packet from host 192.168.10.3:1046, id=28, length=163
        User-Name = "userwifi1"
        NAS-IP-Address = 192.168.10.3
        Connect-Info = "CONNECT 802.11"
        Called-Station-Id = "000f3dab9ac9"
        Calling-Station-Id = "000fb572abe0"
        NAS-Identifier = "00-0f-b5-72-ab-e0"
        NAS-Port-Type = Wireless-802.11
        NAS-Port = 1
        NAS-Port-Id = "1"
        Framed-MTU = 1400
        EAP-Message = 0x0201001201636c6e74302e6274732e656d65
        Message-Authenticator = 0x0c3f20db2c9341cac65bff1c4bf17e37
rlm_sql (sql): Reserving sql socket id: 3
rlm_sql (sql): User userwifi1 not found in radcheck
rlm_sql (sql): User userwifi1 not found in radgroupcheck
rlm_sql (sql): Released sql socket id: 3
rlm_sql (sql): User not found
Sending Access-Reject of id 28 to 192.168.10.3 port 1046

Nous n'avons  plus trop besoin de gérer de certificats de révocation.

Une méthode encore plus propre

Ce que nous venons de voir va nous contraindre à mélanger dans la même table les utilisateurs qui disposent d'une authentification de type CHAP (réseau filaire) et EAP (réseau Wi-Fi). Les tables « radgroupcheck » et « usergroup » vont nous aider à séparer les deux types d'utilisateurs, dans des tables différentes.

L'idée est la suivante. Nous allons créer un groupe d'utilisateur nommé « wifiGroup » (dans la table « radgroupcheck »), auquel nous allons appliquer l'attribut « Auth-Type := EAP ». Il suffira ensuite d'intégrer nos utilisateurs Wi-Fi dans ce groupe (dans la table « usergroup »), pour qu'ils héritent de l'attribut.
    
mysql> INSERT INTO radgroupcheck(GroupName,Attribute,op,Value) \
  VALUES ('wifiGroup','Auth-Type',':=','EAP');
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO usergroup(UserName,GroupName) VALUES ('userwifi1','wifiGroup');
Query OK, 1 row affected (0.00 sec)
 

Freeradius considèrera que tous les membres du groupe « wifiGroup » doivent être identifiés via EAP, et les membres de ce groupe sont tous définis dans la table « usergroup ». De cette manière, il sera plus facile de gérer les deux types d'utilisateurs.
                  
--------------------------------------------------------------------------------------------
< Précédent : WPA2
          

                 Début : > Introduction

                           

Article plus récent Article plus ancien

Leave a Reply