Come usare FreeRADIUS con LinOTP 2 per l'autenticazione a due fattori con password monouso
Importante: la versione Community usa l’API web di LinOTP e non il modulo C disponibile nell’Enterprise. Verificate sempre i certificati SSL e aggiungete logging e ridondanza in produzione.
Panoramica
LinOTP è un backend per password monouso che permette l’autenticazione a due fattori con vari dispositivi hardware, token software e SMS. FreeRADIUS può interrogare LinOTP tramite le sue semplici API web per validare le OTP.
Questa guida copre:
- come funziona l’API di LinOTP;
- come usare rlm_perl per chiamare LinOTP da FreeRADIUS;
- uno script perl di esempio pronto da adattare;
- considerazioni operative, sicurezza e alternative.
L’API di LinOTP
L’API di LinOTP consente di verificare se una OTP è valida per un utente. Esempi di URL di validazione:
https://yourServer/validate/check?user=....&pass=....
oppure
https://yourServer/validate/simplecheck?user=...&pass=...
La risposta è testuale; lo script qui sotto controlla se il contenuto contiene “:-)” per determinare l’esito positivo.
Note: verificate la versione della vostra istanza LinOTP e i percorsi dell’API prima di mettere in produzione.
FreeRADIUS e rlm_perl
È possibile usare rlm_exec per eseguire un esterno, ma usare rlm_perl permette di integrare la chiamata a LinOTP direttamente nel flusso di autenticazione di FreeRADIUS. Adattate la funzione authenticate dello script di esempio per interrogare l’API di LinOTP.
Configurazione minima richiesta (esempi):
- in /etc/freeradius/users impostare DEFAULT Auth-Type := perl
- in /etc/freeradius/modules/perl puntare al modulo perl creato
- in /etc/freeradius/sites-enabled/
aggiungere perl nella sezione authenticate
Lo script perl di esempio
Copiate e incollate il seguente modulo perl nel file indicato dalla configurazione di rlm_perl. Adattate la variabile $URL al vostro server LinOTP. Il blocco di codice qui sotto è ripreso dall’originale e deve rimanere esattamente come mostrato se lo utilizzate così com’è.
#
# 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;
Modificate la variabile $URL nello script perl con l’endpoint corretto della vostra istanza LinOTP. Aggiungete inoltre il controllo del certificato SSL e timeout adeguati.
Quando questa soluzione può fallire
- Se l’API LinOTP non è raggiungibile (network, firewall o DNS).
- Se la risposta dell’API cambia formato rispetto all’aspettativa (non contiene “:-)”).
- Mancanza di validazione SSL espone a attacchi Man-in-the-Middle.
- Scalabilità: chiamate sincrone verso LinOTP possono creare colli di bottiglia.
Approcci alternativi
- Usare il modulo C fornito con LinOTP Enterprise per integrazione più stretta e performance superiori.
- Implementare una cache TTL delle risposte OTP per ridurre latenza (con attenzione alla sicurezza).
- Usare rlm_exec per chiamare un wrapper esterno (in Python/Go) che gestisce retry, logging avanzato e metriche.
Modello mentale e best practice
Mental model: FreeRADIUS delega la validazione delle credenziali a LinOTP. Trattate LinOTP come un servizio esterno: autenticarne e verificarne l’integrità e la disponibilità sono compiti chiave.
Heuristics:
- Validazione SSL obbligatoria in produzione.
- Timeout corti e circuit breaker per evitare di bloccare RADIUS.
- Logging strutturato e monitoraggio SLI/SLO per le chiamate API.
Hardening della sicurezza
- Abilitare e verificare i certificati TLS del server LinOTP.
- Limitare l’accesso al server LinOTP via firewall e VPN.
- Non loggare le OTP in chiaro.
- Usare canali interni sicuri per le chiamate tra FreeRADIUS e LinOTP.
Privacy e GDPR
- Le OTP sono dati sensibili; minimizzate la conservazione dei log che li contengono.
- Se memorizzate eventi di autenticazione, pseudonimizzate o limitate i campi a quelli strettamente necessari.
- Definite una retention policy per i log conformemente alle regole locali.
Checklist per ruoli
- Amministratore RADIUS:
- Impostare DEFAULT Auth-Type := perl e puntare al modulo.
- Testare autenticazioni con utenti di prova.
- Amministratore LinOTP:
- Verificare endpoint validate/simplecheck e certificati TLS.
- Monitorare la coda di richieste e carico.
- Security engineer:
- Configurare firewall, VPN e policy di logging.
- Eseguire penetration test sulle chiamate API.
Mini-metodologia di rollout
- Preparare ambiente di staging con FreeRADIUS e LinOTP.
- Installare e configurare rlm_perl con il modulo perl di esempio.
- Testare casi positivi e negativi (vedere criteri di accettazione).
- Abilitare logging e monitoraggio.
- Rilasciare in produzione in orari a basso traffico e monitorare.
Criteri di accettazione (test cases)
- Login utente valido con OTP corretto restituisce accesso.
- Login utente con OTP errato viene respinto e genera evento di log.
- Timeout verso LinOTP causa rifiuto con messaggio di errore controllato.
- Nessuna OTP salvata in log in chiaro.
Conclusione
Questa soluzione è semplice e rapida da implementare: dimostra come FreeRADIUS possa delegare la verifica delle OTP a LinOTP tramite l’API web. Per l’uso in produzione è essenziale migliorare il controllo dei certificati, aggiungere logging, metriche, retry e ridondanza.
Riepilogo rapido:
- Usate rlm_perl per chiamare l’API di LinOTP;
- Modificate $URL nello script perl e controllate TLS;
- Implementate hardening, monitoraggio e test prima del rollout.
Materiali simili

Ripristinare canali scomparsi in Microsoft Teams

Aggiornare il desktop in Windows 11: 3 metodi rapidi

Server Terminal su Server 2008 R2 — Guida RDS

Malware a livello kernel: come riconoscerlo e difendersi
