Mise en place de FreeRadius
 23/03/2007 
 Christian CALECA 
Liste des cours

FreeRADIUS

Accueil ] [ FreeRADIUS ] [ Pour les VLANs ] [ Pour WPA2 ] [ Révocations ]


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 :

Etudier 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 :

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éé :

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).


Accueil ] [ Suivante ]