テクノロジーガイド

FreeRADIUS を LinOTP 2 と使ってワンタイムパスワードで二要素認証を行う方法

2 min read 認証 更新されました 01 Oct 2025
FreeRADIUS と LinOTP2 で OTP 二要素認証を設定
FreeRADIUS と LinOTP2 で OTP 二要素認証を設定

目的と想定読者

このドキュメントは、LinOTP 2 Community Edition をバックエンドにして FreeRADIUS で OTP による二要素認証を実装したいシステム管理者、ネットワーク管理者、認証基盤担当者向けです。前提知識:基本的な FreeRADIUS の設定とサーバー管理の経験。

概要

LinOTP はワンタイムパスワードのバックエンドで、ハードウェアトークン、ソフトウェアトークン、SMS など幅広いトークン形式をサポートします。Enterprise Edition には FreeRADIUS 用の C モジュールが同梱されますが、Community Edition(AGPLv3)でも簡単な Web API により認証照会が可能です。本記事ではその Web API を呼び出す方法と、FreeRADIUS の rlm_perl モジュールを使った実装例を示します。

API の概要

LinOTP の認証 API は非常にシンプルで、たとえば次のエンドポイントで検証できます。

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

または

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

これらは指定したユーザーとパスワード(ここでは OTP)を受け取り、有効かどうかを返します。レスポンス形式は LinOTP のバージョンや設定によって異なるので、実環境での挙動を事前に確認してください。

重要: 本番導入では必ず TLS を有効にし、サーバー証明書を検証すること。

FreeRADIUS 側の考え方

FreeRADIUS は rlm_exec などで外部プログラムを呼び出す方法もありますが、ここでは rlm_perl を使って直接 HTTP リクエストを行う簡潔な方法を示します。rlm_perl を用いると、認証フェーズで LinOTP の API を呼び、結果に応じて RADIUS の戻り値を返すことができます。

rlm_perl のドキュメントにあるサンプルを authenticate 関数だけ書き換えれば実装できます。

サンプル実装(そのままコピー可)

以下は記事公開時点で動作する簡易サンプルの 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 変数を自分の LinOTP サーバーに合わせて編集してください。

FreeRADIUS の設定ポイント

  1. /etc/freeradius/users にて対象ユーザーのデフォルトを perl 認証に設定します。
    • 例: DEFAULT Auth-Type := perl
  2. /etc/freeradius/modules/perl の module パスを上で作成した perl ファイルに向けます。
  3. sites-enabled/ の authenticate セクションで perl を呼び出すようにします。

これらの設定は環境によりパスやファイル名が変わるため、既存の設定ファイルをバックアップしてから変更してください。

実運用での注意点

重要: このサンプルは「動作例」です。実運用に使う場合は以下を必ず行ってください。

  • TLS/証明書検証: LWP::UserAgent の設定でサーバー証明書を検証する設定を追加する。
  • タイムアウトとリトライ: API 呼び出しにタイムアウトとリトライロジックを実装する。
  • ログとモニタリング: 認証失敗や API エラーを適切にログ出力し、監視に連携する。
  • 冗長化: LinOTP サーバーを冗長化するか、API プロキシを用意する。単一障害点を避ける。
  • レート制限とブルートフォース対策: 高頻度リクエストへの保護を検討する。

代替アプローチ

  • FreeRADIUS の C モジュール(Enterprise Edition)を使う: パフォーマンスと堅牢性が向上します。
  • rlm_rest モジュールを使う: FreeRADIUS の REST 用モジュールで API 呼び出しを行う方法。構成がシンプル。
  • 外部プロキシ(認証ゲートウェイ)経由: 認証ロジックを専用サービスに切り出し、RADIUS と LinOTP の仲介をさせる。

いつこの方法が向かないか(反例)

  • 大規模かつ高スループットが要求される環境では、Perl スクリプト経由の同期 HTTP 呼び出しはボトルネックになり得ます。
  • セキュリティ要件で厳格な証明書管理や FIPS 準拠が必要な場合は、より管理された公式モジュールを選ぶほうが良いです。

導入チェックリスト(役割別簡易)

  • ネットワーク管理者: LinOTP サーバーの TLS 証明書配布、RADIUS サーバーからの接続経路確認
  • システム管理者: perl モジュールの配置、FreeRADIUS 設定のバックアップと反映
  • セキュリティ担当: ログ保存方針、監査ログ、レート制限、アラート設定

簡単なセキュリティ強化案

  • LWP::UserAgent で SSL_verify_mode を有効にし、CA バンドルを明示する
  • API 呼び出しにクライアント証明書(相互 TLS)を使用する
  • 認証結果のログは個人情報(OTP)を含めない

1行用語集

  • LinOTP: ワンタイムパスワード認証のバックエンドソフトウェア
  • OTP: ワンタイムパスワード、使い捨てパスワードのこと
  • rlm_perl: FreeRADIUS の Perl モジュール

まとめ

  • LinOTP の Web API はシンプルで、FreeRADIUS の rlm_perl を使えば短期間で OTP ベースの二要素認証を実装可能です。
  • ただしサンプルは最低限の動作実例であり、TLS 検証、エラーハンドリング、冗長化、ログの強化など実運用上の作業が必須です。

重要: 本手順をそのまま本番に投入せず、まずはステージング環境で十分なテストを行ってください。

まとめ(要点):

  • LinOTP と FreeRADIUS 連携は API で簡単に実現できる。
  • rlm_perl のサンプルはすぐに試せるが、運用前にセキュリティ対策を実施すること。
共有する: X/Twitter Facebook LinkedIn Telegram
著者
編集

類似の素材

Debian 11 に Podman をインストールして使う
コンテナ

Debian 11 に Podman をインストールして使う

Apt-pinning入門:Debianで複数リポジトリを管理
Linux

Apt-pinning入門:Debianで複数リポジトリを管理

OptiScalerでFSR 4を全対応ゲームに導入する方法
ゲーム

OptiScalerでFSR 4を全対応ゲームに導入する方法

Dansguardian と Squid(NTLM)を Debian Etch に導入する方法
ネットワーク

Dansguardian と Squid(NTLM)を Debian Etch に導入する方法

AndroidでSDカードのインストールエラーを修正する方法
トラブルシューティング

AndroidでSDカードのインストールエラーを修正する方法

KNetAttach と KDE の remote:/ でネットワークフォルダーを設定
Linux ネットワーク

KNetAttach と KDE の remote:/ でネットワークフォルダーを設定