FreeRADIUS + LinOTP 2: двухфакторная аутентификация OTP
О чём этот материал
Это пошаговое руководство объясняет, как использовать LinOTP 2 (Community Edition) как backend для одноразовых паролей и подключить его к FreeRADIUS. LinOTP поддерживает множество аппаратных токенов, программных токенов и SMS. Вариант ориентирован на быструю и простой интеграцию с помощью HTTP API LinOTP и модуля rlm_perl FreeRADIUS.
API LinOTP
Enterprise-версия LinOTP поставляется с C-модулем для FreeRADIUS, но Community Edition (AGPLv3) такого модуля не содержит. Зато LinOTP предоставляет простой WEB API для проверок одноразовых паролей.
Пример запроса проверки пароля:
https://yourServer/validate/check?user=....&pass=....
или короткий вариант:
https://yourServer/validate/simplecheck?user=...&pass=...
Полный список API можно посмотреть в документации LinOTP.
FreeRADIUS и подход через rlm_perl
Самый простой подход — использовать rlm_perl и сделать HTTP-запрос к LinOTP из функции authenticate. Можно было бы применить rlm_exec и запускать внешний скрипт, но rlm_perl даёт более прямую интеграцию и меньшую задержку.
Модуль rlm_perl имеет пример, который нужно адаптировать в функции authenticate. Там мы формируем запрос к LinOTP и возвращаем ответ FreeRADIUS в зависимости от результата проверки.
Пример Perl-модуля для rlm_perl
Ниже приведён пример Perl-модуля. Важно: этот блок кода сохранён в оригинале и должен быть вставлен в файл модуля без изменений в синтаксисе.
#
# 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;
Примечание: вам нужно изменить переменную $URL в модуле Perl на адрес вашего LinOTP-сервера.
Настройка FreeRADIUS
Типичная настройка включает:
- В файле /etc/freeradius/users установить DEFAULT Auth-Type := perl для нужных пользователей.
- В /etc/freeradius/modules/perl указать модуль (файл с приведённым выше кодом).
- В /etc/freeradius/sites-enabled/<ваш_сайт> добавить вызов perl в секции authenticate.
Проверьте права доступа к файлу модуля и что FreeRADIUS может читать/исполнять его.
Безопасность и надёжность
Важно учитывать несколько моментов безопасности:
- SSL: пример кода не проверяет сертификат сервера LinOTP. В продакшене обязательно валидируйте сертификаты HTTPS и используйте надежные CA. Без проверки сертификата возможны MITM-атаки.
- Доступность: Single Point of Failure — если LinOTP недоступен, аутентификация по OTP провалится. Подумайте о резервировании и балансировке.
- Логирование: не записывайте в логи чистые пароли и одноразовые коды.
- Ошибки: добавьте обработку ошибок HTTP и таймауты, чтобы FreeRADIUS не зависал.
Совет: используйте системные средства проверки сертификатов (например, LWP::Protocol::https с корректной конфигурацией SSL) и ограничьте число повторных запросов.
Когда этот подход не подходит
- Если у вас строгие требования по задержкам и пакетной обработке, HTTP-запросы в authenticate могут не подойти.
- Если нужна высокая производительность при тысячах запросов в секунду, лучше использовать нативный C-модуль или локальное кеширование состояний.
- Если требуется централизованная поддержка и коммерческая поддержка, рассмотрите Enterprise-версию LinOTP.
Альтернативы
- Использовать rlm_exec и запускать внешний бинарник/скрипт (может быть проще с существующим кодом).
- Настроить нативный модуль для FreeRADIUS (если доступен в вашей сборке или в Enterprise-сборке LinOTP).
- Промежуточный прокси с кешированием ответов LinOTP для снижения нагрузки на сеть.
Мини-методология внедрения
- Тестирование в изолированной среде: настройте тестовую FreeRADIUS-сервер и LinOTP.
- Подключите rlm_perl с модулем, измените $URL на тестовый LinOTP.
- Протестируйте положительные и отрицательные сценарии.
- Добавьте обработку ошибок, таймауты и проверку SSL.
- Перенесите в продакшн с мониторингом и резервированием.
Рольовые чеклисты
Администратор FreeRADIUS:
- Убедиться, что rlm_perl подключён и файл модуля доступен.
- Проверить права и SELinux/AppArmor политики.
Оператор LinOTP:
- Убедиться, что API /validate доступен.
- Проверить сертификаты HTTPS и цепочку доверия.
DevOps:
- Настроить таймауты, алерты и метрики доступности.
- Организовать резервирование LinOTP и балансировку.
Критерии приёмки
- FreeRADIUS успешно аутентифицирует пользователя с корректным OTP.
- При неверном OTP запрос отклоняется и возвращается понятное сообщение.
- Логи не содержат открытых паролей или OTP.
- Сервер LinOTP валидирует TLS-сертификат и проходит интеграционные тесты.
Короткое заключение
Это простой и быстрый способ интегрировать LinOTP Community Edition с FreeRADIUS через HTTP API и модуль rlm_perl. Подойдёт для тестовых сред и небольших production-сценариев при условии доработки по безопасности, обработки ошибок и резервирования.
Важно: до ввода в эксплуатацию внедрите проверку сертификатов, обработку ошибок и планы отказоустойчивости.
Ключевые выводы:
- LinOTP предоставляет простой API для проверки OTP.
- rlm_perl позволяет выполнять HTTP-запросы в процессе аутентификации.
- Требуется доработка безопасности и высокодоступности перед продакшн-внедрением.
Похожие материалы

Закрепить верхние 2 строки в Excel — быстро

Восстановить исчезнувшие каналы Microsoft Teams

Как обновить Windows 11: 3 быстрых способа

Настройка терминального сервера RDS на Server 2008 R2

Вредоносное ПО уровня ядра: защита и восстановление
