テクノロジーガイド

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
著者
編集

類似の素材

Teamsで消えたチャンネルを復元する全手順
トラブルシューティング

Teamsで消えたチャンネルを復元する全手順

Windows 11 を素早くリフレッシュする方法
Windows

Windows 11 を素早くリフレッシュする方法

Server 2008 R2でRDS(ターミナルサーバー)を導入する手順
システム管理

Server 2008 R2でRDS(ターミナルサーバー)を導入する手順

カーネルレベルマルウェアの危険性と対策
セキュリティ

カーネルレベルマルウェアの危険性と対策

YouTubeの低評価数を確認する方法と注意点
YouTube

YouTubeの低評価数を確認する方法と注意点

FreeRADIUS と LinOTP2 で OTP 二要素認証を設定
認証

FreeRADIUS と LinOTP2 で OTP 二要素認証を設定