Utiliser FreeRADIUS avec LinOTP 2 pour l'authentification à deux facteurs (OTP)
Objectif principal
Fournir une intégration simple entre FreeRADIUS et LinOTP 2 (édition communautaire) pour réaliser une authentification à deux facteurs basée sur OTP.
Variantes de recherche utiles
- FreeRADIUS LinOTP
- authentification RADIUS OTP
- rlm_perl LinOTP
- configuration FreeRADIUS OTP
- LinOTP API
Aperçu de l’API LinOTP
LinOTP Community Edition fournit une API HTTP simple pour valider des OTP. L’appel d’authentification typique ressemble à :
https://yourServer/validate/check?user=....&pass=....
ou
https://yourServer/validate/simplecheck?user=...&pass=...
Ces endpoints retournent une réponse concise indiquant si l’OTP est valide. Adaptez l’URL selon votre serveur LinOTP et votre configuration TLS.
Important: vérifiez toujours la connectivité TLS et l’authenticité du serveur LinOTP (cf. section Sécurité et limites).
Intégration avec FreeRADIUS
La version Enterprise de LinOTP propose un module C pour FreeRADIUS. La Community Edition, sous AGPLv3, n’en dispose pas ; toutefois l’API web permet d’intégrer LinOTP via un module externe. Deux approches courantes :
- Utiliser rlm_exec pour appeler un script externe.
- Utiliser rlm_perl pour écrire un petit module Perl exécuté par FreeRADIUS (exemple ci‑dessous).
Cet article fournit un exemple pour rlm_perl basé sur l’exemple officiel, modifié pour consulter l’API LinOTP depuis la fonction authenticate
.
Exemple de module Perl (rlm_perl)
Le module Perl suivant est un exemple pré‑bêta montrant comment interroger LinOTP depuis FreeRADIUS. Adaptez la variable $URL
et améliorez la gestion d’erreurs et TLS avant production.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#
# Copyright 2002 The FreeRADIUS server project
# Copyright 2002 Boian Jordanov <[email protected]>
# Copyright 2011 linotp project <[email protected]>
#
# Based on the Example code for use with rlm_perl
#
#
=head1 NAME
freeradius_perl - Perl module for use with FreeRADIUS rlm_perl, to authenticate against
LinOTP http://www.linotp.org
=head1 SYNOPSIS
use with freeradius:
Configure rlm_perl to work with LinOTP:
in /etc/freeradius/users
set:
DEFAULT Auth-type := perl
in /etc/freeradius/modules/perl
point
perl {
module =
to this file
in /etc/freeradius/sites-enabled/
set
authenticate{
perl
[....]
=head1 DESCRIPTION
This module enables freeradius to authenticate using LinOTP.
TODO:
* checking of server certificate
=head2 Methods
* authenticate
=head1 AUTHOR
Cornelius Koelbel ([email protected])
=head1 COPYRIGHT
Copyright 2011
This library is free software; you can redistribute it
under the GPLv2.
=head1 SEE ALSO
perl(1).
=cut
use strict;
use LWP 5.64;
# use ...
# This is very important ! Without this script will not get the filled hashesh from main.
use vars qw(%RAD_REQUEST %RAD_REPLY %RAD_CHECK $URL);
use Data::Dumper;
$URL = "https://localhost/validate/simplecheck";
# This is hash wich hold original request from radius
#my %RAD_REQUEST;
# In this hash you add values that will be returned to NAS.
#my %RAD_REPLY;
#This is for check items
#my %RAD_CHECK;
#
# This the remapping of return values
#
use constant RLM_MODULE_REJECT=> 0;# /* immediately reject the request */
use constant RLM_MODULE_FAIL=> 1;# /* module failed, don't reply */
use constant RLM_MODULE_OK=> 2;# /* the module is OK, continue */
use constant RLM_MODULE_HANDLED=> 3;# /* the module handled the request, so stop. */
use constant RLM_MODULE_INVALID=> 4;# /* the module considers the request invalid. */
use constant RLM_MODULE_USERLOCK=> 5;# /* reject the request (user is locked out) */
use constant RLM_MODULE_NOTFOUND=> 6;# /* user not found */
use constant RLM_MODULE_NOOP=> 7;# /* module succeeded without doing anything */
use constant RLM_MODULE_UPDATED=> 8;# /* OK (pairs modified) */
use constant RLM_MODULE_NUMCODES=> 9;# /* How many return codes there are */
# Function to handle authorize
sub authorize {
# For debugging purposes only
# &log_request_attributes;
# Here's where your authorization code comes
# You can call another function from here:
&test_call;
return RLM_MODULE_OK;
}
# Function to handle authenticate
sub authenticate {
# For debugging purposes only
# &log_request_attributes;
my $ua = LWP::UserAgent->new();
my $req = HTTP::Request->new(GET => $URL . "?user=" .
$RAD_REQUEST{'User-Name'} . "&pass=" .
$RAD_REQUEST{'User-Password'} );
my $response = $ua->request( $req );
die "Error at $URL\n ", $response->status_line, "\n Aborting"
unless $response->is_success;
if($response->content =~ m/:\-\)/i) {
return RLM_MODULE_OK;
} else {
$RAD_REPLY{'Reply-Message'} = "LinOTP server denied access!";
return RLM_MODULE_REJECT;
}
}
# Function to handle preacct
sub preacct {
# For debugging purposes only
# &log_request_attributes;
return RLM_MODULE_OK;
}
# Function to handle accounting
sub accounting {
# For debugging purposes only
# &log_request_attributes;
# You can call another subroutine from here
&test_call;
return RLM_MODULE_OK;
}
# Function to handle checksimul
sub checksimul {
# For debugging purposes only
# &log_request_attributes;
return RLM_MODULE_OK;
}
# Function to handle pre_proxy
sub pre_proxy {
# For debugging purposes only
# &log_request_attributes;
return RLM_MODULE_OK;
}
# Function to handle post_proxy
sub post_proxy {
# For debugging purposes only
# &log_request_attributes;
return RLM_MODULE_OK;
}
# Function to handle post_auth
sub post_auth {
# For debugging purposes only
# &log_request_attributes;
return RLM_MODULE_OK;
}
# Function to handle xlat
sub xlat {
# For debugging purposes only
# &log_request_attributes;
# Loads some external perl and evaluate it
my ($filename,$a,$b,$c,$d) = @_;
&radiusd::radlog(1, "From xlat $filename ");
&radiusd::radlog(1,"From xlat $a $b $c $d ");
local *FH;
open FH, $filename or die "open '$filename' $!";
local($/) = undef;
my $sub = ;
close FH;
my $eval = qq{ sub handler{ $sub;} };
eval $eval;
eval {main->handler;};
}
# Function to handle detach
sub detach {
# For debugging purposes only
# &log_request_attributes;
# Do some logging.
&radiusd::radlog(0,"rlm_perl::Detaching. Reloading. Done.");
}
#
# Some functions that can be called from other functions
#
sub test_call {
# Some code goes here
}
sub log_request_attributes {
# This shouldn't be done in production environments!
# This is only meant for debugging!
for (keys %RAD_REQUEST) {
&radiusd::radlog(1, "RAD_REQUEST: $_ = $RAD_REQUEST{$_}");
}
}
1;
Remarque rapide : modifiez la variable $URL
pour pointer vers votre serveur LinOTP et choisissez check
ou simplecheck
selon vos préférences d’API.
Fichiers FreeRADIUS à configurer
- /etc/freeradius/users : ajouter une règle par défaut pour utiliser Perl
- Exemple :
DEFAULT Auth-Type := perl
- Exemple :
- /etc/freeradius/modules/perl : pointer
module
vers le fichier Perl créé ci‑dessus - /etc/freeradius/sites-enabled/
: dans la section authenticate
appelerperl
Checklist de déploiement
- [ ] Adapter
$URL
dans le module Perl. - Installer et activer rlm_perl (module Perl) dans FreeRADIUS.
- Tester la connectivité HTTPS entre le serveur FreeRADIUS et LinOTP.
- Vérifier que les attributs RADIUS (User-Name / User-Password) sont transmis correctement.
- Activer le logging détaillé en environnement de test.
- Préparer rollback : restaurer configuration FreeRADIUS antérieure.
Critères d’acceptation
- Une requête avec un OTP valide retourne une autorisation RADIUS (Access-Accept).
- Une requête avec un OTP invalide reçoit un refus (Access-Reject) et un message explicite dans
Reply-Message
. - Les erreurs HTTP vers LinOTP entraînent un échec contrôlé et des logs explicites.
- Les modifications sont réversibles et testées en environnement de préproduction.
Sécurité et limites
Important: l’exemple ci‑dessous n’effectue pas de vérification stricte du certificat TLS du serveur LinOTP. En production, configurez LWP::UserAgent pour valider :
- la chaîne de certificats (CA trust),
- le nom d’hôte (verify_hostname),
- et limitez les ciphers si nécessaire.
Limites à considérer :
- tolérance et redondance : prévoyez un mécanisme de basculement si LinOTP devient indisponible ;
- journalisation et alerting pour les échecs répétés ;
- protections contre le rejeu d’OTP si nécessaire.
Alternatives et approches complémentaires
- Utiliser le module C fourni avec LinOTP Enterprise pour une intégration native à FreeRADIUS.
- Appeler un script externe via rlm_exec si vous préférez implémenter la logique dans un langage autre que Perl.
- Intégrer un cache local de validation pour tolérer de courts isolats réseau (attention à la sécurité).
Méthodologie rapide (mini‑méthode)
- Préparer un environnement de test dédié (FreeRADIUS + LinOTP).
- Déployer le module Perl et configurer FreeRADIUS pour l’utiliser.
- Tester avec comptes d’essai et OTP valides/invalide.
- Activer la vérification TLS et durcir la configuration.
- Monter en production avec supervision.
Matrice de risques (qualitative)
- Indisponibilité LinOTP : impact élevé — mitigation : redondance et bascule.
- Connexions TLS non vérifiées : impact élevé — mitigation : activer la vérification de certificat.
- Logs insuffisants : impact moyen — mitigation : augmenter le niveau de log et centraliser.
Test cases / Cas de test
- Cas 1 : OTP valide -> Access-Accept attendu.
- Cas 2 : OTP invalide -> Access-Reject attendu et message d’erreur.
- Cas 3 : LinOTP indisponible -> comportement défini (fail-close ou fail-open selon votre politique), logs d’erreur.
Diagramme de décision (flux simplifié)
flowchart TD
A[Requête d'authentification RADIUS] --> B{User-Password présent ?}
B -- Non --> C[Procéder à la méthode d'auth existante]
B -- Oui --> D[Appeler LinOTP via API HTTP]
D --> E{Réponse OK ?}
E -- Oui --> F[Return RLM_MODULE_OK 'Access-Accept']
E -- Non --> G[Return RLM_MODULE_REJECT 'Access-Reject']
D --> H{Erreur HTTP ?}
H -- Oui --> I[Log erreur & appliquer politique d'échec]
I --> G
Glossaire (1 ligne)
- LinOTP : backend d’authentification par mots de passe à usage unique (OTP).
- FreeRADIUS : serveur RADIUS open source pour authentification, autorisation et accounting.
Conclusion
Cette intégration montre une manière simple d’utiliser l’API LinOTP avec FreeRADIUS via rlm_perl. Avant la production, améliorez la gestion TLS, ajoutez la surveillance et testez la redondance. L’approche est flexible et vous permet d’adapter la logique (rlm_exec, module C Enterprise) selon vos contraintes opérationnelles.
Important: évitez toute mise en production sans validation TLS et sans tests en environnement contrôlé.
Résumé rapide :
- Intégration possible via l’API HTTP de LinOTP ;
- Exemple prêt à l’emploi avec rlm_perl ;
- Nécessite durcissement TLS, logging et plan de haute disponibilité.
Matériaux similaires

Figer les deux premières lignes dans Excel

Restaurer des canaux disparus Teams

Actualiser Windows 11 rapidement

Installer Remote Desktop Services sur Server 2008 R2

Malware noyau : risques et protection
