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
modulevers le fichier Perl créé ci‑dessus - /etc/freeradius/sites-enabled/
: dans la section authenticateappelerperl
Checklist de déploiement
- [ ] Adapter
$URLdans 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 --> GGlossaire (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
Installer et utiliser Podman sur Debian 11
Guide pratique : apt-pinning sur Debian
OptiScaler : activer FSR 4 dans n'importe quel jeu
Dansguardian + Squid NTLM sur Debian Etch
Corriger l'erreur d'installation Android sur SD