Guide des technologies

Utiliser FreeRADIUS avec LinOTP 2 pour l'authentification à deux facteurs (OTP)

5 min read Sécurité Mis à jour 01 Oct 2025
FreeRADIUS + LinOTP 2 : authentification 2FA (OTP)
FreeRADIUS + LinOTP 2 : authentification 2FA (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
  • /etc/freeradius/modules/perl : pointer module vers le fichier Perl créé ci‑dessus
  • /etc/freeradius/sites-enabled/ : dans la section authenticate appeler perl

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)

  1. Préparer un environnement de test dédié (FreeRADIUS + LinOTP).
  2. Déployer le module Perl et configurer FreeRADIUS pour l’utiliser.
  3. Tester avec comptes d’essai et OTP valides/invalide.
  4. Activer la vérification TLS et durcir la configuration.
  5. 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é.
Auteur
Édition

Matériaux similaires

Figer les deux premières lignes dans Excel
Productivité

Figer les deux premières lignes dans Excel

Restaurer des canaux disparus Teams
Support technique

Restaurer des canaux disparus Teams

Actualiser Windows 11 rapidement
Windows

Actualiser Windows 11 rapidement

Installer Remote Desktop Services sur Server 2008 R2
IT Administration

Installer Remote Desktop Services sur Server 2008 R2

Malware noyau : risques et protection
Sécurité

Malware noyau : risques et protection

Voir le nombre de dislikes sur YouTube
Tutoriel

Voir le nombre de dislikes sur YouTube