Guia de tecnologias

Como usar FreeRADIUS com LinOTP 2 para autenticação de dois fatores

5 min read Segurança Atualizado 01 Oct 2025
FreeRADIUS + LinOTP 2: Autenticação por OTP
FreeRADIUS + LinOTP 2: Autenticação por OTP

Introdução

Este howto orienta a configuração de autenticação RADIUS usando o LinOTP 2 Community Edition. O LinOTP é um backend de One Time Password (OTP) que permite autenticação de dois fatores com diversos dispositivos de hardware, tokens de software e SMS.

Importante: a edição Community usa a API web (AGPLv3) em vez de um módulo C específico. Por isso, a integração é feita através de chamadas HTTP simples.

A API

A edição Community do LinOTP fornece APIs web simples. Uma delas valida um OTP para um usuário. Exemplos de URL de validação:

https://yourServer/validate/check?user=....&pass=....

ou

https://yourServer/validate/simplecheck?user=...&pass=...

Use essas rotas para perguntar ao LinOTP se a senha de uso único (OTP) de um usuário é válida. A API é simples e fácil de integrar a scripts, módulos e proxies.

O FreeRADIUS

A combinação da API simples do LinOTP com módulos do FreeRADIUS facilita uma solução para OTP via RADIUS. Existem duas abordagens comuns:

  • rlm_exec: executar um programa externo (menos eficiente)
  • rlm_perl: usar Perl embutido no FreeRADIUS (o exemplo aqui usa rlm_perl)

Este howto mostra a versão com rlm_perl. O exemplo original do rlm_perl precisa apenas adaptar a função authenticate() para chamar a API do LinOTP e devolver o código apropriado ao FreeRADIUS.

A solução (módulo Perl para rlm_perl)

Abaixo está o módulo Perl de exemplo (pré-beta) que ilustra a ideia. Você precisará ajustar a variável $URL para apontar ao seu servidor LinOTP e configurar os arquivos do FreeRADIUS conforme indicado depois do código.

#
#  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;

Você precisará configurar alguns arquivos do FreeRADIUS e adaptar a variável $URL dentro do módulo Perl para apontar ao servidor LinOTP correto.

Configurações necessárias (resumo prático)

  • /etc/freeradius/users: adicionar regra DEFAULT Auth-Type := perl para usuários que usarão o módulo.
  • /etc/freeradius/modules/perl: apontar o campo module para o arquivo Perl acima.
  • /etc/freeradius/sites-enabled/: incluir a chamada ao módulo perl na seção authenticate.
  • Ajustar $URL no código Perl para https:///validate/simplecheck ou /check conforme sua escolha.

Segurança e boas práticas

Important: por simplicidade o exemplo não valida o certificado SSL do LinOTP. Em produção, sempre valide o certificado do servidor (CA confiável ou certificate pinning). Considere:

  • Usar LWP::UserAgent configurado com SSL_ca_file/SSL_ca_path.
  • Monitorar e limitar tentativas de autenticação (proteção contra brute-force).
  • Registrar eventos de autenticação sem expor OTPs.
  • Considerar redundância e balanceamento para alta disponibilidade.

Quando isso falha (cenários e sintomas)

  • Resposta HTTP com erro ou timeouts: verifique conectividade, firewall e certificados.
  • LinOTP responde que o OTP é inválido: checar horário do token, sincronização e políticas do LinOTP.
  • Erros no FreeRADIUS: verifique logs do rlm_perl e ative log_level/verbosity para depurar.
  • Escalabilidade: rlm_perl usa threads/proc do FreeRADIUS; para grande volume, prefira integração em C ou proxy dedicado.

Abordagens alternativas

  • rlm_exec: chamar um script externo (shell/python) — mais simples, porém menos performático.
  • Módulo C customizado: mais eficiente e integrado, mas exige desenvolvimento em C.
  • Proxy RADIUS: colocar um servidor RADIUS frontal que traduza e faça caching de respostas OTP.
  • Uso de PAM: integrar LinOTP via PAM e deixar FreeRADIUS apontar ao PAM local (se aplicável).

Checklist de implantação (para equipes)

  • Administrador de identidade:
    • Configurar tokens no LinOTP.
    • Testar API /validate/simplecheck com curl.
  • Administrador de rede/infra:
    • Garantir conectividade TLS entre FreeRADIUS e LinOTP.
    • Configurar certificados e verificação de CA.
  • Operações/Reliability:
    • Monitorar latência e erros HTTP.
    • Planejar escalonamento e redundância.
  • Segurança/Compliance:
    • Garantir logging seguro (sem OTPs).
    • Testar recuperação e procedimentos de bloqueio de usuários.

Mini-metodologia de implementação

  1. Validar endpoints do LinOTP via curl ou Postman.
  2. Ajustar $URL no módulo Perl e executar testes locais do rlm_perl.
  3. Atualizar /etc/freeradius/modules/perl e /etc/freeradius/users.
  4. Testar autenticação RADIUS com radclient ou com um NAS de teste.
  5. Habilitar logging e validar comportamento sob carga.
  6. Colocar em produção com monitoramento e rollback plan.

Glossário rápido (1 linha por termo)

  • OTP: One Time Password, senha de uso único.
  • RADIUS: Remote Authentication Dial-In User Service, protocolo de autenticação centralizada.
  • LinOTP: backend open-source para gestão e validação de OTPs.
  • rlm_perl: módulo do FreeRADIUS que permite executar código Perl durante o fluxo RADIUS.

Conclusão

Este exemplo demonstra como é simples integrar o FreeRADIUS ao LinOTP 2 via API web usando rlm_perl. É adequado para provas de conceito e ambientes de médio porte, mas em produção exige tratamento de erros, validação TLS, monitoramento e planeamento de alta disponibilidade.

Resumo final:

  • Use a API /validate para verificar OTPs.
  • Ajuste $URL e configure FreeRADIUS para chamar o módulo Perl.
  • Melhore segurança validando certificados e adicionando limites/monitoramento.

Important: este é um exemplo educativo. Não use em produção sem reforços de segurança (validação TLS, tratamento de erros, logs e redundância).

Notas: se preferir maior desempenho ou integração nativa, avalie escrever/usar um módulo em C ou um proxy RADIUS dedicado.

Autor
Edição

Materiais semelhantes

Configurar Remote Desktop Services no Server 2008 R2
Infraestrutura

Configurar Remote Desktop Services no Server 2008 R2

Malware em nível de kernel: Proteção e resposta
Cibersegurança

Malware em nível de kernel: Proteção e resposta

Ver contagem de dislikes no YouTube — guia prático
Tutoriais

Ver contagem de dislikes no YouTube — guia prático

FreeRADIUS + LinOTP 2: Autenticação por OTP
Segurança

FreeRADIUS + LinOTP 2: Autenticação por OTP

Manter o PC acordado com Caffeine
Utilitários

Manter o PC acordado com Caffeine

Isolar vocais com Audacity — guia prático
Áudio

Isolar vocais com Audacity — guia prático