Ceci est une ancienne révision du document !


radius freeradius eap vlan

FreeRADIUS

(2.0.4 sur Debian Lenny).

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 de 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 œuvre 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 Lenny.

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 (Lenny) sans le support de TLS. TLS nous servira pour le WPA2. Nous allons donc reconstruire un paquet binaire à partir du paquet source, en tenant compte de cet usage.

Notons que ceci n'a plus lieu d'être sur Squeeze, ce qui simplifiera notre tâche, l'installation du binaire standard suffira à nos besoins.

Préparatifs

Nous aurons besoin de quelques outils de compilation et de gestion des paquets source :

# aptitude install build-essential
...
# aptitude 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 2908
drwxr-xr-x 15 root root    4096 mar  4 15:14 freeradius-2.0.4+dfsg
-rw-r--r--  1 root root    4860 sep  7 20:02 freeradius_2.0.4+dfsg-6.diff.gz
-rw-r--r--  1 root root    1476 sep  7 20:02 freeradius_2.0.4+dfsg-6.dsc
-rw-r--r--  1 root root 2953674 mai 19  2008 freeradius_2.0.4+dfsg.orig.tar.gz

Configuration de la compilation

Dans le répertoire ~/build_freeradius/freeradius-2.0.4+dfsg/debian nous avons un fichier nommé « rules », qui contient les directives de compilation. Nous allons devoir modifier ce fichier.

Voici la première partie qui nous intéresse :

	./configure $(confflags) \
		--prefix=/usr \
		--exec-prefix=/usr \
		--mandir=$(mandir) \
		--sysconfdir=/etc \
		--libdir=$(libdir) \
		--datadir=/usr/share \
		--localstatedir=/var \
		--with-raddbdir=$(raddbdir) \
		--with-logdir=/var/log/$(package) \
		--enable-ltdl-install=no --enable-strict-dependencies \
		--with-large-files --with-udpfromto --with-edir \
		--enable-developer \
		--config-cache \
		--without-rlm_eap_tls \
		--without-rlm_eap_ttls \
		--without-rlm_eap_peap \
		--without-rlm_eap_tnc \
		--without-rlm_otp \
		--with-rlm_sql_postgresql_lib_dir=`pg_config --libdir` \
		--with-rlm_sql_postgresql_include_dir=`pg_config --includedir` \
		--without-openssl \
		--without-rlm_eap_ikev2 \
		--without-rlm_sql_oracle \
		--without-rlm_sql_unixodbc \
		--with-system-libtool
Les lignes surlignées sont celles qu'il faut supprimer pour obtenir le support de TLS. Nous devons donc aboutir à ceci :

	./configure $(confflags) \
		--prefix=/usr \
		--exec-prefix=/usr \
		--mandir=$(mandir) \
		--sysconfdir=/etc \
		--libdir=$(libdir) \
		--datadir=/usr/share \
		--localstatedir=/var \
		--with-raddbdir=$(raddbdir) \
		--with-logdir=/var/log/$(package) \
		--enable-ltdl-install=no --enable-strict-dependencies \
		--with-large-files --with-udpfromto --with-edir \
		--enable-developer \
		--config-cache \
		--without-rlm_eap_tnc \
		--with-rlm_sql_postgresql_lib_dir=`pg_config --libdir` \
		--with-rlm_sql_postgresql_include_dir=`pg_config --includedir` \
		--without-rlm_eap_ikev2 \
		--without-rlm_sql_oracle \
		--without-rlm_sql_unixodbc \
		--with-system-libtool

Ce serait tout si le concepteur du paquet n'avait pas mis un petit test pour bloquer la compilation s'il traine un lien vers OpenSSL lors de l'édition des liens. Il faut repérer ce test et l'inhiber. Le voici :

	for pkg in ${pkgs} ; do \
	  if dh_shlibdeps -p $$pkg -- -O 2>/dev/null | grep -q libssl; then \
	    echo "$$pkg links to openssl" ;\
	    exit 1 ;\
	  fi ;\
	done

Il suffit de commenter la ligne exit 1;\ comme ceci :

	for pkg in ${pkgs} ; do \
	  if dh_shlibdeps -p $$pkg -- -O 2>/dev/null | grep -q libssl; then \
	    echo "$$pkg links to openssl" ;\
#	    exit 1 ;\
	  fi ;\
	done

Du côté de la compilation, nous sommes parés.

Voici donc le fichier « rules » tel qu'il doit finalement se présenter :

#!/usr/bin/make -f
# -*- makefile -*-
# Sample debian/rules that uses debhelper.
#
# This file was originally written by Joey Hess and Craig Small.
# As a special exception, when this file is copied by dh-make into a
# dh-make output file, you may use that output file without restriction.
# This special exception was added by Craig Small in version 0.37 of dh-make.
#
# Modified to make a template file for a multi-binary package with separated
# build-arch and build-indep targets  by Bill Allombert 2001

# Uncomment this to turn on verbose mode.
export DH_VERBOSE=1

.NOTPARALLEL:

SHELL           =/bin/bash

package         = freeradius
freeradius_dir  = $(CURDIR)/debian/tmp/

mandir          = /usr/share/man
libdir          = /usr/lib/$(package)
logdir          = /var/log/$(package)
pkgdocdir       = /usr/share/doc/$(package)
raddbdir        = /etc/$(package)

modulelist=krb5 ldap sql_mysql sql_iodbc sql_postgresql
pkgs=$(shell dh_listpackages)

# This has to be exported to make some magic below work.
export DH_OPTIONS

# These are used for cross-compiling and for saving the configure script
# from having to guess our platform (since we know it already)
export DEB_HOST_GNU_TYPE  ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
export DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)

ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
	CFLAGS += -O0
else
	CFLAGS += -O2
endif

ifeq ($(DEB_BUILD_GNU_TYPE), $(DEB_HOST_GNU_TYPE))
	confflags += --build $(DEB_HOST_GNU_TYPE)
else
	confflags += --build $(DEB_BUILD_GNU_TYPE) --host $(DEB_HOST_GNU_TYPE)
endif

config.status: configure
	dh_testdir

ifeq (config.sub.dist,$(wildcard config.sub.dist))
	rm config.sub
else
	mv config.sub config.sub.dist
endif
ifeq (config.guess.dist,$(wildcard config.guess.dist))
	rm config.guess
else
	mv config.guess config.guess.dist
endif
	ln -s /usr/share/misc/config.sub config.sub
	ln -s /usr/share/misc/config.guess config.guess
	

	./configure $(confflags) \
		--prefix=/usr \
		--exec-prefix=/usr \
		--mandir=$(mandir) \
		--sysconfdir=/etc \
		--libdir=$(libdir) \
		--datadir=/usr/share \
		--localstatedir=/var \
		--with-raddbdir=$(raddbdir) \
		--with-logdir=/var/log/$(package) \
		--enable-ltdl-install=no --enable-strict-dependencies \
		--with-large-files --with-udpfromto --with-edir \
		--enable-developer \
		--config-cache \
		--without-rlm_eap_tnc \
		--with-rlm_sql_postgresql_lib_dir=`pg_config --libdir` \
		--with-rlm_sql_postgresql_include_dir=`pg_config --includedir` \
		--without-rlm_eap_ikev2 \
		--without-rlm_sql_oracle \
		--without-rlm_sql_unixodbc \
		--with-system-libtool

#Architecture 
build: build-arch build-indep

build-arch: build-arch-stamp
build-arch-stamp: config.status
	$(MAKE) 
	touch $@

build-indep: build-indep-stamp
build-indep-stamp: config.status
	touch $@

clean:
	dh_testdir
	dh_testroot
	rm -f build-arch-stamp build-indep-stamp 
	rm -f config.cache config.log
	
	[ ! -d src/modules/lib ] || rm -fr src/modules/lib || true
	[ ! -d src/binary ] || rm -fr src/binary || true

	# Add here commands to clean up after the build process.
ifeq (Make.inc,$(wildcard Make.inc))
	$(MAKE) distclean
endif
ifeq (config.sub.dist,$(wildcard config.sub.dist))
	rm -f config.sub
	mv config.sub.dist config.sub
endif
ifeq (config.guess.dist,$(wildcard config.guess.dist))
	rm -f config.guess
	mv config.guess.dist config.guess
endif
	dh_clean 

install: install-indep install-arch
install-indep: build-indep-stamp
	dh_testdir
	dh_testroot
	dh_installdirs -i

	$(MAKE) -C dialup_admin DIALUP_PREFIX=/usr/share/freeradius-dialupadmin \
	                        DIALUP_DOCDIR=/usr/share/doc/freeradius-dialupadmin \
	                        DIALUP_CONFDIR=/etc/freeradius-dialupadmin \
	                        R=$(freeradius_dir) install

	mv $(freeradius_dir)/usr/share/freeradius-dialupadmin/bin/dialup_admin.cron \
	       $(freeradius_dir)/usr/share/freeradius-dialupadmin/bin/freeradius-dialupadmin.cron
	mv $(freeradius_dir)/usr/share/doc/freeradius-dialupadmin/Changelog \
	       $(freeradius_dir)/usr/share/doc/freeradius-dialupadmin/changelog

	install -m0644 debian/apache2.conf $(freeradius_dir)/etc/freeradius-dialupadmin/

	dh_install -i --sourcedir=$(freeradius_dir)
	dh_installdocs -p freeradius-dialupadmin dialup_admin/README

install-arch: build-arch-stamp
	dh_testdir
	dh_testroot
	dh_installdirs -s
	test -d $(freeradius_dir)/usr/lib/freeradius || mkdir -p $(freeradius_dir)/usr/lib/freeradius
	ln -s rlm_sql.so $(freeradius_dir)/usr/lib/freeradius/librlm_sql.so
	$(MAKE) install R=$(freeradius_dir)
	
	# rename radius binary to play nicely with others
	mv $(freeradius_dir)/usr/sbin/radiusd $(freeradius_dir)/usr/sbin/$(package)
	mv $(freeradius_dir)/$(mandir)/man8/radiusd.8 $(freeradius_dir)/$(mandir)/man8/$(package).8
	
	dh_install --sourcedir=$(freeradius_dir) -p libfreeradius2
	dh_install --sourcedir=$(freeradius_dir) -p libfreeradius-dev

	for mod in ${modulelist}; do \
	  pkg=$${mod##sql_} ; \
	  dh_install --sourcedir=$(freeradius_dir) -p freeradius-$$pkg ; \
	  rm -f $(freeradius_dir)/usr/lib/freeradius/rlm_$$mod*.so ; \
	done

	dh_install --sourcedir=$(freeradius_dir) -p freeradius-utils
	dh_install --sourcedir=$(freeradius_dir) -p freeradius
	
	dh_strip -a --dbg-package=freeradius-dbg

	dh_makeshlibs -a -n
	for pkg in ${pkgs} ; do \
	  if dh_shlibdeps -p $$pkg -- -O 2>/dev/null | grep -q libssl; then \
	    echo "$$pkg links to openssl" ;\
#	    exit 1 ;\
	  fi ;\
	done
	dh_shlibdeps

binary-common:
	dh_testdir
	dh_testroot
	dh_installchangelogs 
	dh_installdocs
	dh_installexamples
	dh_installlogrotate	
	dh_installpam --name=radiusd 
	dh_installinit --noscripts
	dh_installman
	dh_lintian
	dh_link
	dh_compress -Xexamples
	dh_fixperms
	dh_installdeb
	dh_gencontrol
	dh_md5sums
	dh_builddeb

# Build architecture independant packages using the common target.
binary-indep: build-indep install-indep
	$(MAKE) -f debian/rules DH_OPTIONS=-i binary-common

# Build architecture dependant packages using the common target.
binary-arch: build-arch install-arch
	$(MAKE) -f debian/rules DH_OPTIONS=-s binary-common

binary: binary-arch binary-indep
.PHONY: build clean binary-indep binary-arch binary install install-indep install-arch 

Le fichier « control »

Nous devons ici ajouter la dépendance à la librairie libssl-dev :

Source: freeradius
Build-Depends: autotools-dev, debhelper (>= 6.0.7), libgdbm-dev, libiodbc2-dev, libkrb5-dev, libldap2-dev, libltdl3-dev, libmysqlclient15-dev | libmysqlclient-dev, libpam0g-dev, libpcap-dev, libperl-dev, libpq-dev, libsasl2-dev, libsnmp-dev, libtool, python-dev, libssl-dev
Section: net
Priority: optional
Maintainer: Stephen Gran 
Uploaders: Mark Hymers 
Standards-Version: 3.7.3
...
Et nous assurer qu'elle est bien présente sur notre système :

aptitude install libssl-dev

construction des binaires

Il nous reste à construire les paquets binaires :

cd ~/build_freeradius
apt-src build freeradius

Une fois la compilation terminée, normalement sans message d'erreur, nous obtenons la liste des paquets qui suit. Nous n'avons pas besoin de les installer tous ici, puisque nous n'utiliserons que MySQL :

ls -l *.deb
-rw-r--r-- 1 root root 513228 fév 22 16:20 freeradius_2.0.4+dfsg-6_i386.deb
-rw-r--r-- 1 root root 205030 fév 22 16:21 freeradius-common_2.0.4+dfsg-6_all.deb
-rw-r--r-- 1 root root 949458 fév 22 16:20 freeradius-dbg_2.0.4+dfsg-6_i386.deb
-rw-r--r-- 1 root root 132748 fév 22 16:21 freeradius-dialupadmin_2.0.4+dfsg-6_all.deb
-rw-r--r-- 1 root root  17184 fév 22 16:20 freeradius-iodbc_2.0.4+dfsg-6_i386.deb
-rw-r--r-- 1 root root  18082 fév 22 16:20 freeradius-krb5_2.0.4+dfsg-6_i386.deb
-rw-r--r-- 1 root root  34426 fév 22 16:20 freeradius-ldap_2.0.4+dfsg-6_i386.deb
-rw-r--r-- 1 root root  24874 fév 22 16:20 freeradius-mysql_2.0.4+dfsg-6_i386.deb
-rw-r--r-- 1 root root  35364 fév 22 16:20 freeradius-postgresql_2.0.4+dfsg-6_i386.deb
-rw-r--r-- 1 root root  71282 fév 22 16:20 freeradius-utils_2.0.4+dfsg-6_i386.deb
-rw-r--r-- 1 root root  85212 fév 22 16:20 libfreeradius2_2.0.4+dfsg-6_i386.deb
-rw-r--r-- 1 root root 103672 fév 22 16:20 libfreeradius-dev_2.0.4+dfsg-6_i386.deb

Installation des paquets utiles

La commande dpkg :

dpkg -i libfreeradius2_2.0.4+dfsg-6_i386.deb freeradius-common_2.0.4+dfsg-6_all.deb freeradius_2.0.4+dfsg-6_i386.deb freeradius-mysql_2.0.4+dfsg-6_i386.deb freeradius-utils_2.0.4+dfsg-6_i386.deb

Se protéger des mises à jour de « aptitude »

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 consiste à utiliser l'outil dpkg pour gérer la sélection des paquets installés. Faisons d'abord un :

:~# dpkg --get-selections > packages

De manière à obtenir le fichier packages qu'il nous faudra éditer. Si nous recherchons les paquets relatifs à Freeradius qui sont installés (au cas où nous aurions des trous dans la mémoire) :

aptitude search freeradius | grep ^i
i   freeradius                      - a high-performance and highly configurable
i   freeradius-common               - FreeRadius common files                   
i   freeradius-mysql                - MySQL module for FreeRADIUS server        
i   freeradius-utils                - FreeRadius client utilities               
i   libfreeradius2                  - FreeRADIUS shared library

Nous retrouvons dans le fichier packages que nous avons créé, les mêmes paquets :

cat packages | grep radius
freeradius					install
freeradius-common				install
freeradius-mysql				install
freeradius-utils				install
libfreeradius2					install

Nous devons éditer ce fichier en remplaçant l'information install par hold, qui indiquera à apt que ces paquets ne doivent pas être touchés lors des mises à jour. Après édition, nous devons avoir :

cat packages | grep radius
freeradius					hold
freeradius-common				hold
freeradius-mysql				hold
freeradius-utils				hold
libfreeradius2					hold

Il nous reste à entrer ces nouvelles informations dans la base de donnée des paquets installés :

:~# dpkg --set-selections < packages

Si tout s'est bien passé, la vérification suivante doit donner :

 aptitude search freeradius | grep ^i
ih  freeradius                      - a high-performance and highly configurable
ih  freeradius-common               - FreeRadius common files                   
ih  freeradius-mysql                - MySQL module for FreeRADIUS server        
ih  freeradius-utils                - FreeRadius client utilities               
ih  libfreeradius2                  - FreeRADIUS shared library

Le h qui suit le i indique bien que ces paquets sont désormais protégés des mises à jour.

Configuration

Création de la base MySQL

Nous voulons ici faire quelque chose de « simple ». Il sera toujours temps de compliquer les choses une fois que la solution minimale aura été validée. Notre base MySQL contiendra la liste des « utilisateurs » (chez nous des adresses MAC), la liste des « authenticators » (nos switchs et notre borne WI-FI) et éventuellement une liste d'utilisateurs autorisés à utiliser le WI-FI, nous verrons plus loin pourquoi.

FreeRADIUS n'aura donc à manipuler cette base qu'en lecture, il n'aura rien à écrire dedans. Nous allons donc créer une base radius et un utilisateur radius qui ne pourra que faire des SELECT dans cette base. De cette manière, si notre serveur FreeRADIUS venait à être compromis, il ne pourrait pas facilement transformer la base en foutoir.

Nous utilisons ici :

# mysql -V
mysql  Ver 14.12 Distrib 5.0.51a, for debian-linux-gnu (i486) using readline 5.2

Création de la base :

# mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 42
Server version: 5.0.51a-24 (Debian)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> CREATE DATABASE radius;
Query OK, 1 row affected (0.01 sec)

Création de l'utilisateur :

CREATE USER 'radius'@'localhost' IDENTIFIED BY 'epikoi';
GRANT SELECT ON radius . * TO 'radius'@'localhost';

Pour la création des tables, FreeRADIUS propose des fichiers SQL qui vont nous aider ici. Ils se trouvent dans /etc/freeradius/sql/mysql/. Le principal fichier s'appelle schema.sql et crée les tables les plus importantes :

  • radacct (inutilisée dans notre cas) ;
  • radcheck ;
  • radgroupcheck ;
  • radgroupreply (inutilisée dans notre cas) ;
  • radreply (inutilisée dans notre cas) ;
  • radusergroup ;
  • radpostauth (inutilisée dans notre cas).

Le second s'appelle nas.sql et n'ajoute qu'une seule table nas. Cette table est destinée à contenir les authenticators (nas). Nous pourrions nous en passer et utiliser un simple fichier texte à la place, d'autant que cette table n'est lue qu'une seule fois au déparrage de FreeRADIUS (contrairement à toutes les autres) et n'apporte donc pas grand chose de plus qu'un fichier texte.

Finalement, pour vérification :

# mysql -uroot -pepikoi radius
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 83
Server version: 5.0.51a-24-log (Debian)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> show tables;
+------------------+
| Tables_in_radius |
+------------------+
| nas              | 
| radacct          | 
| radcheck         | 
| radgroupcheck    | 
| radgroupreply    | 
| radpostauth      | 
| radreply         | 
| radusergroup     | 
+------------------+
8 rows in set (0.00 sec)

Configuration de FreeRADIUS

C'est maintenant que nous allons vraiment commencer à nous amuser. En effet il y a pas mal de fichiers qui ont été placés dans /etc/freeradius et beaucoup vont devoir être modifiés.

Dans /etc/freeradius/, il y a radiusd.conf qui est le fichier principal. Il est copieusement documenté et fait par défaut appel à quelques autres dont nous n'aurons pas besoin. Voyons ceci :

# cat radiusd.conf | egrep -v -e '[[:blank:]]*#|^$' | grep \$INCLUDE
$INCLUDE proxy.conf
$INCLUDE clients.conf
$INCLUDE snmp.conf
$INCLUDE eap.conf
$INCLUDE policy.conf
$INCLUDE sites-enabled/

Dans notre cas très simple :

  • proxy.conf ne nous servira pas ;
  • clients.conf de même. En effet, ce fichier contient les « nas » et nous allons les mettre dans la base MySQL ;
  • snmp.conf ne nous sera d'aucune utilité ;
  • policy.conf probablement non plus, mais nous le garderons « d'usine ».

En revanche :

  • eap.conf nous servira à configurer le mode « eap » ;
  • sites-enabled/ va contenir des liens symboliques vers des fichiers placés dans sites-availables/ à la mode d'Apache2. Dans notre cas (très simple, rappelons-le), un seul site sera « available » et s'appellera poétiquement default.

Il reste enfin le fichier sql.conf qui sera nécessaire pour l'usage de MySQL et qui fait lui-même appel à /etc/freeradius/sql/mysql/dialup.conf que nous nous garderons de toucher, bien que dans notre cas (très simple), il serait possible de le dégraisser quelque peu.

Au final, nous avons à voir et à modifier :

  • /etc/freeradius/radiusd.conf ;
  • /etc/freeradius/eap.conf ;
  • /etc/freeradius/sql.conf ;
  • /etc/freeradius/sites-available/default ;

Et nous supprimerons dans /etc/freeradius/sites-enabled/ tout lien qui ne pointera pas sur /etc/freeradius/sites-available/default.

Lorsque je vous disais qu'il y a de quoi s'amuser…

radiusd.conf

Première chose à faire :

cd /etc/freeradius
mv radiusd.conf radiusd.conf.dist

Deuxième chose à faire : Lire le contenu de radiusd.conf.dist, histoire de comprendre un peu ce que l'on va faire par la suite….

Troisième chose :

cat radiusd.conf.dist | egrep -v -e '^[[:blank:]]*#|^$' > radiusd.conf

De manière à ne pas se bousiller les yeux à chercher les lignes « utiles » dans la forêt de commentaires.

Nous obtenons quelque chose qui ressemble à :

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
db_dir = $(raddbdir)
libdir = /usr/lib/freeradius
pidfile = ${run_dir}/freeradius.pid
user = freerad
group = freerad
max_request_time = 30
cleanup_delay = 5
max_requests = 1024
listen {
	type = auth
	ipaddr = *
	port = 0
}
listen {
	ipaddr = *
	port = 0
	type = acct
}
hostname_lookups = no
allow_core_dumps = no
regular_expressions	= yes
extended_expressions	= yes
log {
	destination = files
	file = ${logdir}/radius.log
	syslog_facility = daemon
	stripped_names = no
	auth = no
	auth_badpass = no
	auth_goodpass = no
}
checkrad = ${sbindir}/checkrad
security {
	max_attributes = 200
	reject_delay = 1
	status_server = yes
}
#proxy_requests  = yes
#$INCLUDE proxy.conf
#$INCLUDE clients.conf
snmp	= no
#$INCLUDE snmp.conf
thread pool {
	start_servers = 5
	max_servers = 32
	min_spare_servers = 3
	max_spare_servers = 10
	max_requests_per_server = 0
}
modules {
#	pap {
#		auto_header = no
#	}
	chap {
		authtype = CHAP
	}
#	pam {
#		pam_auth = radiusd
#	}
#	unix {
#		radwtmp = ${logdir}/radwtmp
#	}
$INCLUDE eap.conf
	mschap {
	}
	ldap {
		server = "ldap.your.domain"
		basedn = "o=My Org,c=UA"
		filter = "(uid=%{Stripped-User-Name:-%{User-Name}})"
		ldap_connections_number = 5
		timeout = 4
		timelimit = 3
		net_timeout = 1
		tls {
			start_tls = no
		}
		dictionary_mapping = ${confdir}/ldap.attrmap
		edir_account_policy_check = no
	}
	realm IPASS {
		format = prefix
		delimiter = "/"
	}
	realm suffix {
		format = suffix
		delimiter = "@"
	}
	realm realmpercent {
		format = suffix
		delimiter = "%"
	}
	realm ntdomain {
		format = prefix
		delimiter = "\\"
	}	
	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
		acctusersfile = ${confdir}/acct_users
		preproxy_usersfile = ${confdir}/preproxy_users
		compat = no
	}
	detail {
		detailfile = ${radacctdir}/%{Client-IP-Address}/detail-%Y%m%d
		detailperm = 0600
		header = "%t"
	}
	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 attr_filter.post-proxy {
		attrsfile = ${confdir}/attrs
	}
	attr_filter attr_filter.pre-proxy {
		attrsfile = ${confdir}/attrs.pre-proxy
	}
	attr_filter attr_filter.access_reject {
		key = %{User-Name}
		attrsfile = ${confdir}/attrs.access_reject
	}
	attr_filter attr_filter.accounting_response {
		key = %{User-Name}
		attrsfile = ${confdir}/attrs.accounting_response
	}
	counter daily {
		filename = ${db_dir}/db.daily
		key = User-Name
		count-attribute = Acct-Session-Time
		reset = daily
		counter-name = Daily-Session-Time
		check-name = Max-Daily-Session
		reply-name = Session-Timeout
		allowed-servicetype = Framed-User
		cache-size = 5000
	}
	always fail {
		rcode = fail
	}
	always reject {
		rcode = reject
	}
	always noop {
		rcode = noop
	}
	always handled {
		rcode = handled
	}
	always updated {
		rcode = updated
	}
	always notfound {
		rcode = notfound
	}
	always ok {
		rcode = ok
		simulcount = 0
		mpp = no
	}
	expr {
	}
	digest {
	}
	expiration {
		reply-message = "Password Has Expired\r\n" 
	}
	logintime {
		reply-message = "You are calling outside your allowed timespan\r\n"
		minimum-timeout = 60
	}
	exec {
		wait = yes
		input_pairs = request
		shell_escape = yes
		output = none
	}
	exec echo {
		wait = yes
		program = "/bin/echo %{User-Name}"
		input_pairs = request
		output_pairs = reply
		shell_escape = yes
	}
	ippool main_pool {
		range-start = 192.168.1.1
		range-stop = 192.168.3.254
		netmask = 255.255.255.0
		cache-size = 800
		session-db = ${db_dir}/db.ippool
		ip-index = ${db_dir}/db.ipindex
		override = no
		maximum-timeout = 0
	}
	policy {
	       filename = ${confdir}/policy.txt
	}
}
instantiate {
	exec
	expr
	expiration
	logintime
}
$INCLUDE policy.conf
$INCLUDE sites-enabled/
Il y a dans ce fichier plein de choses que nous pourrions enlever car elles ne nous servent à rien (dans notre cas…). Les lignes surlignées montrent ce qu'il est nécessaire de modifier pour nos besoins.

sites-avalable/default

Assez peu de choses dans ce fichier, compte tenu de la simplicité de nos besoins :

authorize {
	preprocess
	eap {
		ok = return
	}
	sql
}
authenticate {
	Auth-Type CHAP {
		chap
	}
	eap
}
session {
	sql
}

eap.conf

Il s'agit d'indiquer que nous utiliserons tls et donner le chemin d'accès aux certificats de la racine et du serveur, ainsi que la clé privée du serveur, qui peut ici être protégée par un mot de passe.

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.

De même, nous pouvons y créer le fichier dh (Pour l'échange « Diffie-Hellman ») :

openssl dhparam -check -text -5 512 -out dh

Ainsi qu'un fichier random :

dd if=/dev/urandom of=random count=2

Ce répertoire devrait finalement 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/maison.mrs-cert.pem
                        certificate_file = ${raddbdir}/certs/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
                }

                mschapv2 {
                }
        }

sql.conf

Nous avons une base MySQL radius et un utilisateur du même nom, capable de lire dans cette base :

sql {
	database = "mysql"
	driver = "rlm_sql_${database}"
	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 = "radusergroup"
	deletestalesessions = yes
	sqltrace = no
	sqltracefile = ${logdir}/sqltrace.sql
	num_sql_socks = 5
	connect_failure_retry_delay = 60
	nas_table = "nas"
	$INCLUDE sql/${database}/dialup.conf
	readclients = yes
}

Le fichier inclus dialup.conf pourrait être copieusement dégraissé dans notre cas, mais nous n'y toucherons pas.

Vérifions...

Essai chap

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','Cleartext-Password',':=','userpassword');" | mysql -u root -p radius

Enfin nous démarrons freeradius en mode « debug », dans une console avec :

freeradius -X

Le mode « debug » s'avère très volubile, mais instructif :

FreeRADIUS Version 2.0.4, for host i486-pc-linux-gnu, built on Feb 22 2009 at 16:19:09
Copyright (C) 1999-2008 The FreeRADIUS server project and contributors. 
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A 
PARTICULAR PURPOSE. 
You may redistribute copies of FreeRADIUS under the terms of the 
GNU General Public License. 
Starting - reading configuration files ...
including configuration file /etc/freeradius/radiusd.conf
including configuration file /etc/freeradius/eap.conf
including configuration file /etc/freeradius/sql.conf
including configuration file /etc/freeradius/sql/mysql/dialup.conf
including configuration file /etc/freeradius/policy.conf
including files in directory /etc/freeradius/sites-enabled/
including configuration file /etc/freeradius/sites-enabled/default
including dictionary file /etc/freeradius/dictionary
main {
	prefix = "/usr"
	localstatedir = "/var"
	logdir = "/var/log/freeradius"
	libdir = "/usr/lib/freeradius"
	radacctdir = "/var/log/freeradius/radacct"
	hostname_lookups = no
	max_request_time = 30
	cleanup_delay = 5
	max_requests = 1024
	allow_core_dumps = no
	pidfile = "/var/run/freeradius/freeradius.pid"
	user = "freerad"
	group = "freerad"
	checkrad = "/usr/sbin/checkrad"
	debug_level = 0
	proxy_requests = yes
 security {
	max_attributes = 200
	reject_delay = 1
	status_server = yes
 }
}
radiusd: #### Loading Realms and Home Servers ####
radiusd: #### Instantiating modules ####
 instantiate {
 Module: Linked to module rlm_exec
 Module: Instantiating exec
  exec {
	wait = yes
	input_pairs = "request"
	shell_escape = yes
  }
 Module: Linked to module rlm_expr
 Module: Instantiating expr
 Module: Linked to module rlm_expiration
 Module: Instantiating expiration
  expiration {
	reply-message = "Password Has Expired  "
  }
 Module: Linked to module rlm_logintime
 Module: Instantiating logintime
  logintime {
	reply-message = "You are calling outside your allowed timespan  "
	minimum-timeout = 60
  }
 }
radiusd: #### Loading Virtual Servers ####
server {
 modules {
 Module: Checking authenticate {...} for more modules to load
 Module: Linked to module rlm_eap
 Module: Instantiating eap
  eap {
	default_eap_type = "tls"
	timer_expire = 60
	ignore_unknown_eap_types = no
	cisco_accounting_username_bug = no
  }
 Module: Linked to sub-module rlm_eap_tls
 Module: Instantiating eap-tls
   tls {
	rsa_key_exchange = no
	dh_key_exchange = yes
	rsa_key_length = 512
	dh_key_length = 512
	verify_depth = 0
	CA_path = "/etc/freeradius/certs/"
	pem_file_type = yes
	private_key_file = "/etc/freeradius/certs/radius.eme.org-cert.pem"
	certificate_file = "/etc/freeradius/certs/radius.eme.org-cert.pem"
	CA_file = "/etc/freeradius/certs/Root_EME_CA-cacert.pem"
	private_key_password = "ph34rl3r4dius"
	dh_file = "/etc/freeradius/certs/dh"
	random_file = "/etc/freeradius/certs/random"
	fragment_size = 1024
	include_length = yes
	check_crl = no
   }
 Module: Linked to sub-module rlm_eap_mschapv2
 Module: Instantiating eap-mschapv2
   mschapv2 {
	with_ntdomain_hack = no
   }
 Module: Linked to module rlm_chap
 Module: Instantiating chap
 Module: Checking authorize {...} for more modules to load
 Module: Linked to module rlm_preprocess
 Module: Instantiating preprocess
  preprocess {
	huntgroups = "/etc/freeradius/huntgroups"
	hints = "/etc/freeradius/hints"
	with_ascend_hack = no
	ascend_channels_per_line = 23
	with_ntdomain_hack = no
	with_specialix_jetstream_hack = no
	with_cisco_vsa_hack = no
	with_alvarion_vsa_hack = no
  }
 Module: Linked to module rlm_sql
 Module: Instantiating sql
  sql {
	driver = "rlm_sql_mysql"
	server = "localhost"
	port = ""
	login = "radius"
	password = "epikoi"
	radius_db = "radius"
	read_groups = yes
	sqltrace = yes
	sqltracefile = "/var/log/freeradius/sqltrace.sql"
	readclients = yes
	deletestalesessions = yes
	num_sql_socks = 5
	sql_user_name = "%{User-Name}"
	default_user_profile = ""
	nas_query = "SELECT id, nasname, shortname, type, secret FROM nas"
	authorize_check_query = "SELECT id, username, attribute, value, op FROM radcheck WHERE username = '%{SQL-User-Name}' ORDER BY id"
	authorize_reply_query = "SELECT id, username, attribute, value, op FROM radreply WHERE username = '%{SQL-User-Name}' ORDER BY id"
	authorize_group_check_query = "SELECT id, groupname, attribute, value, op FROM radgroupcheck WHERE groupname = '%{Sql-Group}' ORDER BY id"
	authorize_group_reply_query = "SELECT id, groupname, attribute, value, op FROM radgroupreply WHERE groupname = '%{Sql-Group}' ORDER BY id"
	accounting_onoff_query = ""
	accounting_update_query = ""
	accounting_update_query_alt = ""
	accounting_start_query = ""
	accounting_start_query_alt = ""
	accounting_stop_query = ""
	accounting_stop_query_alt = ""
	group_membership_query = "SELECT groupname FROM radusergroup WHERE username = '%{SQL-User-Name}' ORDER BY priority"
	connect_failure_retry_delay = 60
	simul_count_query = ""
	simul_verify_query = ""
	postauth_query = "INSERT INTO radpostauth (username, pass, reply, authdate) VALUES ('%{User-Name}','%{%{User-Password}:-%{Chap-Password}}','%{reply:Packet-Type}', '%S')"
	safe-characters = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"
  }
rlm_sql (sql): Driver rlm_sql_mysql (module rlm_sql_mysql) loaded and linked
rlm_sql (sql): Attempting to connect to radius@localhost:/radius
rlm_sql (sql): starting 0
rlm_sql (sql): Attempting to connect rlm_sql_mysql #0
rlm_sql_mysql: Starting connect to MySQL server for #0
rlm_sql (sql): Connected new DB handle, #0
rlm_sql (sql): starting 1
rlm_sql (sql): Attempting to connect rlm_sql_mysql #1
rlm_sql_mysql: Starting connect to MySQL server for #1
rlm_sql (sql): Connected new DB handle, #1
rlm_sql (sql): starting 2
rlm_sql (sql): Attempting to connect rlm_sql_mysql #2
rlm_sql_mysql: Starting connect to MySQL server for #2
rlm_sql (sql): Connected new DB handle, #2
rlm_sql (sql): starting 3
rlm_sql (sql): Attempting to connect rlm_sql_mysql #3
rlm_sql_mysql: Starting connect to MySQL server for #3
rlm_sql (sql): Connected new DB handle, #3
rlm_sql (sql): starting 4
rlm_sql (sql): Attempting to connect rlm_sql_mysql #4
rlm_sql_mysql: Starting connect to MySQL server for #4
rlm_sql (sql): Connected new DB handle, #4
rlm_sql (sql): Processing generate_sql_clients
rlm_sql (sql) in generate_sql_clients: query is SELECT id, nasname, shortname, type, secret FROM nas
rlm_sql (sql): Reserving sql socket id: 4
rlm_sql_mysql: query:  SELECT id, nasname, shortname, type, secret FROM nas
rlm_sql (sql): Read entry nasname=127.0.0.1,shortname=localhost,secret=naspassword
rlm_sql (sql): Adding client 127.0.0.1 (localhost, server=) to clients list
rlm_sql (sql): Released sql socket id: 4
 Module: Checking session {...} for more modules to load
 }
}
radiusd: #### Opening IP addresses and Ports ####
listen {
	type = "auth"
	ipaddr = *
	port = 0
}
listen {
	type = "acct"
	ipaddr = *
	port = 0
}
main {
	snmp = no
	smux_password = ""
	snmp_write_access = no
}
Listening on authentication address * port 1812
Listening on accounting address * port 1813
Listening on proxy address * port 1814
Ready to process requests.

Notez la liste de tous les fichiers inclus, en cas de problème. Notez également que le serveur, si rien n'a coincé dans la configuration, est prêt à recevoir des requêtes. Notez enfin que tout ceci n'est pas très propre, puisque nous n'utilisons pas ici ni l' accounting ni le proxy, mais que FreeRADIUS va tout de même ouvrir ces ports. Il y aurait pas mal de « tuning » à faire.

Dans une autre console, nous allons essayer une authentification avec l'utilitaire radtest :

radtest test0 userpassword 127.0.0.1 0 naspassword

Nous devrions obtenir la réponse :

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

Dans la console où s'exécute FreeRADIUS en mode « debug » :

rad_recv: Access-Request packet from host 127.0.0.1 port 50494, id=197, length=57
	User-Name = "test0"
	User-Password = "userpassword"
	NAS-IP-Address = 127.0.1.1
	NAS-Port = 0
+- entering group authorize
++[preprocess] returns ok
  rlm_eap: No EAP-Message, not doing EAP
++[eap] returns noop
	expand: %{User-Name} -> test0
rlm_sql (sql): sql_set_user escaped user --> 'test0'
rlm_sql (sql): Reserving sql socket id: 3
	expand: SELECT id, username, attribute, value, op FROM radcheck WHERE username = '%{SQL-User-Name}'  ORDER BY id -> SELECT id, username, attribute, value, op FROM radcheck WHERE username = 'test0' ORDER BY id
rlm_sql_mysql: query:  SELECT id, username, attribute, value, op FROM radcheck WHERE username = 'test0' ORDER BY id
rlm_sql (sql): User found in radcheck table
	expand: SELECT id, username, attribute, value, op FROM radreply WHERE username = '%{SQL-User-Name}' ORDER BY id -> SELECT id, username, attribute, value, op FROM radreply  WHERE username = 'test0' ORDER BY id
rlm_sql_mysql: query:  SELECT id, username, attribute, value, op FROM radreply WHERE username = 'test0' ORDER BY id
	expand: SELECT groupname FROM radusergroup WHERE username = '%{SQL-User-Name}' ORDER BY priority -> SELECT groupname FROM radusergroup WHERE username = 'test0' ORDER BY priority
rlm_sql_mysql: query:  SELECT groupname FROM radusergroup WHERE username = 'test0' ORDER BY priority
rlm_sql (sql): Released sql socket id: 3
++[sql] returns ok
auth: type Local
auth: user supplied User-Password matches local User-Password
Login OK: [test0/userpassword] (from client localhost port 0)
Sending Access-Accept of id 197 to 127.0.0.1 port 50494
Finished request 0.
Going to the next request
Waking up in 4.9 seconds.
Cleaning up request 0 ID 197 with timestamp +6
Ready to process requests.

Notre authentification par nom d'utilisateur et mot de passe (chap) fonctionne correctement. 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 valeur de l'attribut « Cleartext-Password » identiques).