Google 認証システムの確認コード生成方法に準じたワンタイムパスワードを設定する

本設定例では、Luaスクリプト機能とL2TP/IPsecを用いたリモートアクセスVPN機能を使用しています。

Luaスクリプト機能の対応機種は、RTX5000RTX3500RTX1210RTX1200(Rev.10.01.16以降)、RTX830RTX810NVR700WNVR510NVR500FWX120です。

リモートアクセスVPN機能の対応機種は、RTX5000(Rev.14.00.12以降)、RTX3500(Rev.14.00.12以降)、
RTX1210RTX1200(Rev.10.01.59以降)、RTX830RTX810(Rev.11.01.21以降)、NVR700WNVR510(Rev.15.01.03以降)、FWX120(Rev.11.03.08以降)です。

構成図 Google 認証システムの確認コード生成方法に準じたワンタイムパスワードを設定する

ルーターの時刻とルーターにあらかじめ設定してあるユーザー情報に基づいて、30秒ごとに各L2TPクライアントに対してワンタイムパスワードをGoogle 認証システム(iOS端末、Android端末で使用するアプリケーション)の確認コード生成方法に準じて生成し、生成したワンタイムパスワードを「pp auth username」に設定するスクリプトです。

ユーザーは、ルーターに設定されているユーザー情報のキーをGoogle 認証システムに設定し、Google 認証システム上に表示される確認コードを、以下のiOSとAndroidの設定例に記載してある「認証用パスワード」として設定します。
Google 認証システムの設定方法はGoogle アカウントのヘルプページを参照してください。

ルーターの時刻とL2TPクライアントとの時刻がずれていると、スクリプトで生成するワンタイムパスワードと、Google 認証システムで生成する確認コードに不一致が生じることがあります。本設定例では、一例としてL2TPクライアントとの時刻の同期を取るため、毎日23:50にNTPによる時計の設定をします。

本設定例は、以下のファイルを使用します。すべてのファイルをルーターにコピーしてください。

  • ユーザー情報のチェックと生成するワンタイムパスワードを設定するためのLuaスクリプト
  • ワンタイムパスワードを生成するためのモジュールファイル
  • 外部ページからダウンロードすることができるsha1.luaファイルを元に作成した、SHA-1のhashとHMAC-SHA1のsignatureを計算するためのモジュールファイル

光回線に接続するためには、別途ONUが必要です。
NVR700WとNVR510は、本体のONUポートに小型ONUを装着することで、光回線に接続できます。

設定手順

本設定例は、以下の流れで設定します。

  1. ルーターにConfigを設定
  2. 構成を構築
  3. ルーターにモジュールファイルをコピー
  4. ルーターにLuaスクリプトをコピー

対応機種のうち、設定例を掲載している機種は、以下のとおりです。

機種 掲載内容 備考
RTX1210 RTX1200 RTX830 NVR700W NVR510 FWX120 コマンド設定例
Luaスクリプト例
モジュールファイル
Luaスクリプト機能、
リモートアクセスVPN機能

モジュールファイルを利用した設定の導入手順

ルーターに本設定例のconfigを設定する

  1. 設定例のリンクから設定ファイルをダウンロードし、ルーターに設定を追加してください。
    pp auth myname (ISPに接続するID) (ISPに接続するパスワード)
    dns server (ISPより指定されたDNSサーバーのIPアドレス)
    [解説]
    上記のように設定例に赤字で記述されている設定値は、適切な値に変更してください。
    ルーターにはTFTPを使用してPCからルーターに設定ファイルをコピーすることができます。
    この方法については取扱説明書をご参照ください。
  2. L2TPクライアントの数だけ以下のフォーマットでユーザー情報を、ルーターにsetコマンドで設定してください。

    set PP(ユーザー番号)=(ユーザー名):(キー)

    ユーザー番号
           1からスクリプトの「使用できるPPの数」に設定したまでの数字
    ユーザー名
           ":" (コロン)を含まない任意の文字列(1文字以上64文字以内)
    キー
           半角の英数字のうち、AからZおよび2から7を使用した任意の16文字
           英字の大文字小文字は区別されません

    ユーザー名とキーの間は ":" (コロン)で区切ります。
    ユーザー情報の設定内容を変更した場合は、スクリプトを実行し直してください。

    [設定例]
    # set PP1=BNR114:7a436biziktj23S3
    # set PP2=u77dcI:6u3tkx42hkemc72a
    # set PP3=Ixd@216:rx36mwd2jker4csy

構成を構築する

  1. 本設定例ページのトップにある通常時の構成図と同様の構成を構築します。

モジュールファイルをルーターにコピーする

  1. モジュールファイルをPCにダウンロードします。
    [解説]
    モジュールファイル名はLuaスクリプトの動作にも関係があります。本設定例では、ファイル名を変更せず、そのままご使用ください。
  2. PCにUSBメモリを接続して、ダウンロードしたモジュールファイルをUSBメモリにコピーします。
  3. USBメモリをPCから取り外し、ルーターに接続します。ルーターのUSBランプが点灯します。
  4. ルーターコンソールを開きます。
    [解説]
    ルーターコンソールはシリアルケーブルやtelnetで接続して使用します。
    使用方法については取扱説明書をご参照ください。
  5. 管理者権限でログインし、コマンドでルーターにモジュールファイルをコピーします。
    copyコマンドを使用します。
    USBメモリのルートディレクトリーからルーターのルートディレクトリーにモジュールファイルをコピーする場合:
    [ルーターコンソール]
    # copy usb1:/onetimepass.lua /onetimepass.lua
    # copy usb1:/sha1.lua /sha1.lua
    #
    [解説]
    copyコマンドの詳細はこちらをご参照ください。
    ファイルをルーターのルートディレクトリー以外にも格納することができます。
    その場合は、ディレクトリーを作成してから、そのディレクトリーにコピーします。
    そちらの方法については参考資料をご参照ください。
    技術資料「RTFS」-「コマンド一覧」-「ディレクトリの作成」
    モジュールファイルをディレクトリーにコピーした場合、環境変数LUA_PATHでそのディレクトリーを指定する必要があります。そちらの方法は参考資料をご参照ください。
    技術資料「Lua スクリプト機能」 - 「詳細」にある「5.ルーターの環境変数 」をご参照ください。
    モジュールファイルの名前を変更した場合、Luaスクリプト内「require関数」の引数の名前も変更する必要があります。

Luaスクリプトの導入手順

[解説]
Luaスクリプトのダウンロードから実行までの手順はLuaスクリプト導入手順マニュアルをご覧ください。

ルーターの設定例

環境変数の設定 set LUA_PATH="./\?.lua;" # 注釈1
経路の設定 ip route default gateway pp 1
LANインターフェースの設定
(LAN1ポートを使用)
ip lan1 address 192.168.100.1/24
ip lan1 proxyarp on
WANインターフェースの設定
(LAN2ポートを使用)
pp select 1
pp always-on on
pppoe use lan2
pp auth accept (認証方式)
pp auth myname (ISPに接続するID) (ISPに接続するパスワード)
ppp lcp mru on 1454
ppp ipcp ipaddress on
ppp ipcp msext on
ppp ccp type none
ip pp mtu 1454
ip pp nat descriptor 1
pp enable 1
L2TP接続を受け入れるための設定 pp select anonymous
pp bind tunnel1-tunnel3
pp auth request (認証方式)
pp auth username (ユーザー名1) (ワンタイムパスワード1) # 注釈2
pp auth username (ユーザー名2) (ワンタイムパスワード2) # 注釈2
pp auth username (ユーザー名3) (ワンタイムパスワード3) # 注釈2
ppp ipcp ipaddress on
ppp ipcp msext on
ip pp remote address pool 192.168.100.10-192.168.100.20
ip pp mtu 1258
pp enable anonymous
L2TP接続で使用するトンネルの設定
(クライアントA)
tunnel select 1
tunnel encapsulation l2tp
ipsec tunnel 101
ipsec sa policy 101 1 esp (暗号化アルゴリズム) (認証アルゴリズム)
ipsec ike keepalive use 1 off
ipsec ike local address 1 192.168.100.1
ipsec ike nat-traversal 1 on
ipsec ike pre-shared-key 1 text (事前共有鍵)
ipsec ike remote address 1 any
l2tp tunnel disconnect time off
l2tp keepalive use on 10 3
l2tp keepalive log on
l2tp syslog on
ip tunnel tcp mss limit auto
tunnel enable 1
L2TP接続で使用するトンネルの設定
(クライアントB)
tunnel select 2
tunnel encapsulation l2tp
ipsec tunnel 102
ipsec sa policy 102 2 esp (暗号化アルゴリズム) (認証アルゴリズム)
ipsec ike keepalive use 2 off
ipsec ike local address 2 192.168.100.1
ipsec ike nat-traversal 2 on
ipsec ike pre-shared-key 2 text (事前共有鍵)
ipsec ike remote address 2 any
l2tp tunnel disconnect time off
l2tp keepalive use on 10 3
l2tp keepalive log on
l2tp syslog on
ip tunnel tcp mss limit auto
tunnel enable 2
L2TP接続で使用するトンネルの設定
(クライアントC)
tunnel select 3
tunnel encapsulation l2tp
ipsec tunnel 103
ipsec sa policy 103 3 esp (暗号化アルゴリズム) (認証アルゴリズム)
ipsec ike keepalive use 3 off
ipsec ike local address 3 192.168.100.1
ipsec ike nat-traversal 3 on
ipsec ike pre-shared-key 3 text (事前共有鍵)
ipsec ike remote address 3 any
l2tp tunnel disconnect time off
l2tp keepalive use on 10 3
l2tp keepalive log on
l2tp syslog on
ip tunnel tcp mss limit auto
tunnel enable 3
NATの設定 nat descriptor type 1 masquerade
nat descriptor address outer 1 ipcp
nat descriptor address inner 1 auto
nat descriptor masquerade static 1 1 192.168.100.1 esp # 注釈3
nat descriptor masquerade static 1 2 192.168.100.1 udp 500 # 注釈3
nat descriptor masquerade static 1 3 192.168.100.1 udp 4500 # 注釈4
IPsecのトランスポートモード設定 ipsec transport 1 101 udp 1701
ipsec transport 2 102 udp 1701
ipsec transport 3 103 udp 1701
ipsec auto refresh on
DHCPの設定 dhcp service server
dhcp server rfc2131 compliant except remain-silent
dhcp scope 1 192.168.100.30-192.168.100.191/24
DNSの設定 dns server (ISPより指定されたDNSサーバーのIPアドレス)
dns private address spoof on
L2TP設定 l2tp service on
Luaスクリプトのスケジュール設定 schedule at 1 startup * lua (Luaスクリプトファイル名)
NTPのスケジュール設定 schedule at 2 */* 23:50:00 * ntpdate (NTPサーバーのアドレス) syslog

[注釈の説明]

注釈1:
モジュールファイルをロードするために使用するパスを設定します。
Lua ではモジュール名の置換記号として "?" を使いますが、ルーターコンソールで '?' を入力するとヘルプが表示されてしまうため、'?' を入力する前にエスケープシーケンスの '\' を入力しています。

require関数の引数と環境変数LUA PATHの設定値についての詳細はこちら

注釈2:
Luaスクリプトによって変更されます。

注釈3:
VPN(IPsec)に関係するパケットを通過させる設定です。

注釈4:
VPN(IPsec:NATトラバーサル機能を使用時)に関係するパケットを通過させる設定です。

Luaスクリプト例

設定値 -- ユーザー情報の設定値に誤りがあったときに出力する SYSLOG のレベル (info, debug, notice)
log_level = "(SYSLOGレベル)"
-- 使用できるPPの数
max_pp = (使用できるPPの数)
モジュールの読み込み require("onetimepass")
定数 -- 30秒
SEC30 = 30
-- 10年を秒に変換
DECADE = 315532800 -- (365日×10年+2日(閏年分)(日) = 3652×24×60×60(秒))
-- ユーザー名の最大文字数
MAX_USER = 65
-- キーの文字数
KEY_LEN = 16
ユーザー情報をチェックする関数

function user_info_check()
 local user, key, user_key
 local match_ptn = "(.+)%:([a-zA-Z2-7]+)"
 local find_ptn = "(.*)%:(.*)%:(.*)"
 local user_num = 0

 for i = 1, max_pp do
  user_key = os.getenv("PP" .. i)

  if user_key then

   -- ユーザー情報に":" (コロン)が2つ以上含まれていればエラー終了
   if user_key:find(find_ptn) then
    return false
   end

   -- ユーザー情報からユーザー名とキーを抽出する
   user, key = user_key:match(match_ptn)

   -- ユーザー名かキーが見つからなければエラー終了
   if not user or not key then
    return false
   end

   -- ユーザー名が65文字以上もしくはキーが16文字でなければエラー終了
   if user:len() >= MAX_USER or key:len() ~= KEY_LEN then
    return false
   end

   user_num = user_num + 1
  end
 end

 -- ユーザー情報が1つも設定されていなければエラー終了
 if user_num < 1 then
  return false
 end
 
 return true
end

PPにauth usernameコマンドを設定する関数

function set_auth_user(t)

 -- PP anonymousを選択
 rt.command("pp select anonymous")

 -- auth usernameコマンド実行
 for i, v in ipairs(t) do
  if v then
   rt.command(v)
  end
 end
end

ワンタイムパスワード用のカウンターを生成する関数

function create_counter()
 local t = os.time() + DECADE + SEC30

 return onetimepass.hex2bin(string.format("%.16X", t / SEC30))
end

メインルーチン

local id, pass, id_pass
local cnt, cmd
local cmd_tbl = {}
local match_ptn = "(.+)%:(%w+)"
local cmd_ptn = "pp auth username %s %s"

-- ユーザー情報のチェック
if not user_info_check() then
 rt.syslog(log_level, "[OTP] Invalid Parameter.")
 os.exit(1)
end

while true do
 -- 次の0秒 or 30秒までスリープ
 rt.sleep(SEC30 - os.time() % SEC30 + 1)

 -- ワンタイムパスワードをauth userコマンドで設定
 -- スクリプト起動直後はパスワード未作成なので何もしない
 if cnt then
  set_auth_user(cmd_tbl)
 end

 -- カウンターを生成
 cnt = create_counter()

 -- 次の30秒のワンタイムパスワードを生成
 for i = 1, max_pp do
  cmd = false

  -- 環境変数からユーザー名とキーを抽出
  id_pass = os.getenv("PP" .. i)
  if id_pass then

   id, pass = id_pass:match(match_ptn)

   if id and pass then
    cmd = string.format(cmd_ptn, id, onetimepass.otp(pass, cnt))
   end
  end

  -- コマンドを登録
  cmd_tbl[i] = cmd
 end
end

モジュール例1

モジュールの読み込み、登録 require("sha1")
module("onetimepass", package.seeall)
定数 constF = tonumber("f",16)
const7F = tonumber("7f",16)
constFF = tonumber("ff",16)
キーに対応する数値を返す関数 function lut(c)
 if c >= 50 and c <= 55 then   -- 2 to 7
  return c - 24
 elseif c >= 65 and c <= 90 then -- A to Z
  return c - 65
 else
  error("[OTP] Invalid charactor for Key.")
 end
end
Base32文字列をバイト型配列に戻す関数

function base32_decode(b32)
 local n = 0
 local j = 0
 local aa, bb, cc, len

 -- 小文字があれば大文字に
 b32 = string.upper(b32)

 -- 文字列長を取得
 len = b32:len()

 -- 文字列を配列に入れる
 t = {b32:byte(1, len)}

 local binary = ""
 for i = 1, len do
  n = bit.bshift(n, 5)

  n = n + lut(t[i])
  j = j + 5

  if j >= 8 then
   j = j - 8
   binary = binary .. string.char(bit.bshift(bit.band(n, bit.bshift(constFF, j)), -j))
  end
 end
 return binary
end

16進数をバイト型配列に変換する関数 function hex2bin(hex)
 local binary = ""
 local len = hex:len() / 2
 for i = 1, len do
  binary = binary .. string.char(tonumber(string.sub(hex,(i-1)*2+1,i*2),16))
 end
 return binary
end
バイト型配列から6桁の乱数を生成する関数

local function oath_truncate(hash)
 local offset, sn

 offset = bit.band(string.byte(string.sub(hash,20,20)), constF)
 sn = bit.bshift(bit.band(string.byte(string.sub(hash,offset+1,offset+1)), const7F), 24)
 sn = sn + bit.bshift(bit.band(string.byte(string.sub(hash,offset+2,offset+2)), constFF), 16)
 sn = sn + bit.bshift(bit.band(string.byte(string.sub(hash,offset+3,offset+3)), constFF), 8)
 sn = sn + bit.band(string.byte(string.sub(hash,offset+4,offset+4)), constFF)

 return string.format("%.06d",sn % 1000000)
end

ワンタイムパスワードを生成する関数 function otp(key,counter)
 return oath_truncate(hex2bin(sha1.hmac_sha1(base32_decode(key), counter)))
end

モジュール例2

  1. 以下のページ(外部ページ)からsha1.luaファイルをダウンロードしてください。
    http://regex.info/blog/lua/sha1
  2. ダウンロードしたsha1.luaファイルの以下の部分を修正してください。
    [修正箇所1]
    1行目に以下を追加してください。
    module("sha1", package.seeall)
    [解説]
    モジュールを登録します。
    [修正箇所2]
    306行目に以下を追加してください。
    function mathmodf(a,b)
      local amari = a % b
      local result1 = (a-amari) / b
      local result2 = amari
      return result1,result2
    end
    [解説]
    math.modf()関数はヤマハルーターのLuaスクリプトで利用できないので与えた値の整数部と小数部を返すmathmodf関数を追加します。
    [修正箇所3]
    330行目から333行目を以下に変更してください。
    変更前
      local B1, R1 = math.modf(msg_len_in_bits / 0x01000000)
      local B2, R2 = math.modf( 0x01000000 * R1 / 0x00010000)
      local B3, R3 = math.modf( 0x00010000 * R2 / 0x00000100)
      local B4 = 0x00000100 * R3
    変更後
      local B1, R1 = mathmodf(msg_len_in_bits, 0x01000000)
      local B2, R2 = mathmodf( R1 , 0x00010000)
      local B3, R3 = mathmodf( R2 , 0x00000100)
    local B4 = R3
    [解説]
    math.modf関数の代わりに[修正箇所2]で追加したmathmodf関数を利用するように変更します。

【ご注意】
本設定例は、設定の参考例を示したもので、動作を保証するものではございません。
ご利用いただく際には、十分に評価・検証を実施してください。