2017年9月19日火曜日

台風一過

朝起きると空気のよどみが吹き飛ばされ晴れ渡っている。絶好のシーイングを期待して望遠レンズを担いで出かけてみたが、思いのほか暑くなり、午後になるとややガスっぽくなった。




 Nikon D800E, AF-S NIKKOR 200-500mm f/5.6E ED VR

2017年9月1日金曜日

Raspberry Pi Zero Wでワイヤレスシリアルコンソールサーバー

Raspberry Piを使って、4G LTE回線とインターネット経由で、ワイヤレスアクセスできるシリアルコンソールサーバーを作成した。ルーターやサーバーなどのネットワーク機器のシリアルコンソールにリモートアクセスして、いわゆるOOBM(Out-Of-Band-Management)というオペレーション行うための装置だ。こういった用途には、かつてはアナログモデムと電話回線が使われていたが、専用のアナログ電話回線を用意しなければならず、コスト的にも割高なこの方法は現代では敬遠されつつある。ルーターやサーバーなどのネットワーク機器は、通常TelnetやSSH接続を使ってインバンドで管理されるが、回線障害や設定ミスなどによりリモートログインができなくなってしまった場合や、機器の導入時に専任の担当者が遠隔操作で初期設定を行う場合などは、こういったOOBMの手段が有用となる。3G/4G回線を使う「セルラーモデム」という専用のデバイスがあるそうだが、産業用などに使われる高価な代物のようで、一般のオフィスはもちろんデータセンターなどでもあまりお目にかかったことは無い。代替手段としてはリモートデスクトップ経由でのアクセスや、Ethernet経由のコンソールサーバーなどがあるが、セキュリティ上の事情や責任分界点の関係で、リモートサイトでこういった手段が使えない場合もある。今回は、こういった課題を解決することができる手段の一つとして、安価に手に入るRaspberry Piとモバイルルーターを使ったOOBMデバイスを作成したのでご紹介したい。なお、具体的なビルド方法については、公開するための整理ができていないので、恐縮ながら今回は割愛させてもらう。

リモート側デバイス。今回のシステムでは「モバイルコンソール」と呼ぶ。
Raspberry Pi Zero Wを使ったもの(上)
Raspberry Pi 1 Model Bを使ったもの(下)

最近日本でも入手ができるようになりつつある「Raspberry Pi Zero W」。

GPIO端子のひとつにステータス表示用のLEDを接続し、サーバーとのSSHトンネリングが完了したらこれを点灯させる仕組みだ。Raspbianを使う動作環境や動作用のスクリプトはRaspberry Pi各モデルに共通なので、有線LANポートのついたこれまでのRaspberry Piモデルも使用できる。最初のデバイスは、デスクの引き出しで眠っていたRaspberry Pi 1 Model Bを使って作成した。リモート側のデバイスは、Raspberry Pi本体と電源、USBシリアル変換ケーブル、それにモバイルルーターによって構成される。このセットを今回は便宜上「モバイルコンソール」と呼ぶ。(もっと素敵な呼び方があれば変更してもいい)
Raspberry Pi 1 Model Bは無線LANを内蔵していないので、Wi-Fiでインターネットに接続するにはUSBの無線LANアダプタが必要になる。Raspberry Pi Zero Wは無線LANはあるが有線LANポートを持たないので、有線接続をしたい場合にはマイクロUSB-RJ45のLANアダプタが必要になる。現在主流のモデル、Raspberry Pi 3 Model Bは有線LANポートと無線LANアダプタを内蔵しているのでこれが望ましいが、消費電力がやや大きいため別途専用のACアダプターが必要になる。

資料用に作成したスライド。

モバイルコンソールのRaspberry Piは、起動後自動的にインターネット上の中継サーバーにSSHトンネルを張る(掘る?)仕組みになっている。今回のシステムでは接続の中継に使うこのサーバーを「Pi Server」と呼ぶことにした。当初、Pi Serverは先日の企画でも使った趣味用途で個人契約しているVPSを使っていたものの、本来このサーバーではパスワード認証を有効にしたくない。しかし、仕事で使うことを想定すると鍵を使っての認証は手順が煩雑すぎ、利便性を優先するとやはりパスワード認証が好ましい。そこで、当初リモート側デバイスとして使っていたRaspberry Pi 1 Model Bのハードウェアを流用し、独立したこの目的専用のサーバーを用意することにした。中継用のPi ServerはSSHDが動作するLinuxシステムなら何でも良いが、インターネットから接続するためのグローバルIPは必要になる。

図中のIPアドレスは架空。

ユーザーがモバイルコンソールへ接続するには、PuTTYやTeratermなどのSSHクライアントを使ってPi ServerのインターネットアドレスへSSH接続をおこなうだけでいい。接続に必要なパラメータは「Pi ServerのIPアドレス」「ユーザー名」「パスワード」「ポート番号(443固定)」だけだ。
Pi Serverへログインする際のユーザー毎に、ループバック接続をするポート番号を各々割り当て、対応するモバイルコンソールデバイスへ接続させる仕組みになっている。これにより、一台のPi Serverを多数のモバイルコンソールの中継に使うことができる。今のところモバイルコンソールと呼んでいるが、多数のリモートサイトに常設するような使用方法を想定してもいい。その場合、同時に稼働できるモバイルコンソール数の上限は、サーバーがSSHトンネリングのTCPセッションを同時にいくつ維持できるかによるため、実際のところは不明だが、数十か数百あたりではないかと思う。SSHサーバー側は公開鍵認証にも対応させているが、利便性を優先しパスワード認証でのログインを有効にしてある。トンネルを張る際のSSHセッションの非対話ログインにはsshpassというプログラムを使っており、このプログラムはSSHサーバー側がパスワード認証でも公開鍵認証のパスフレーズ付きまたはパスフレーズ無しのいずれの場合でも動作するので、サーバー側の認証方式を変更してもモバイルコンソール側のスクリプトに変更の必要は無い。モバイルコンソール側のターミナルソフトにはLinuxの「cu」を使っている。これの代わりに「jerm」というプログラムを使えばシリアルポートをTCP接続に変換することが容易なのだが、jermはTCP接続経由では「ブレーク信号」を送ることができないことがテスト中に分かったため、採用を断念した。ブレーク信号の送信は、CISCOルーターのブート時にROMMONに入るために必要な操作で、これができることは重要である。

モバイルコンソール側とこれにリモート接続する管理者側では、ファイアーウォールやルーター上にポート開放などの設定を追加する必要は一切無い。インターネット上のPi Server(固定IPまたはDDNS)に対してモバイルコンソール側と管理者側の両側から、TCP 443ポートへのTCPセッションを外向きに開始するだけなので、モバイルルーターやブロードバンドルーターのIPマスカレードNATを超えて、また片方あるいは双方がファイアーウォールの内側にあっても接続が可能である。TCP 443ポートはTCP 80番同様、極めて一般的なポートなので、企業内のファイアーウォールでも外向きの接続がブロックされることはほぼ無い。(FWによってはダメな場合はある)

接続の仕組み。

その他の機能。

モバイル回線やインターネット接続が不安定な場合や、DHCPのリース期限切れなどにより、モバイルコンソールとPi Server間のトンネルセッションがダウンする場合がある。その場合に備え、モバイルコンソール側ではスクリプトを使って自動的にトンネルを復旧させる仕掛けを設けてある。実はここが最も苦労した点で、当初はnmapとpgrepを使ってポートが開いているかどうかと、プロセスが存在するかどうかによってチェックをしていたが、トンネルセッションを起動したSSHプロセスが残っているのに通信できない場合があったり、リモート側の回線が切れているのにサーバー側のTCP接続が残留し続け、回線復旧後に自動再接続をしてもポートのバインドに失敗したりなど、通信できないといっても色々なパターンがあり、トンネルのダウンを客観的に検出するのが結構難しかった。確実に異常を検出するために、最終的に行ったのは「トンネルループバックテスト」と呼んでいる方法だ。これは待機時に張っているトンネルとは別に、一時的にチェック用のトンネルをサーバー向けに作成し、ワンラインでSSHコマンドの後ろに追加したコマンドをサーバー側で実行させ、待機中のトンネルを折り返してリモート側のディレクトリにテンポラリファイルを作成させる方法である。コマンド実行後にテンポラリファイルが存在しなければトンネルが機能していない、というわけだ。これにより、トンネルが正常に機能しているかどうかを割と確実に検出することに成功した。
その他の機能としては、モバイルコンソールになるRaspberry Piが有線LANポートを持っている場合は、無線LANでのインターネットアクセスと合わせ、ルーターとして機能させることができる。モバイルコンソールデバイスをサイトに常設することで、メインの通信回線がダウンした場合のバックアップ回線に4G LTEインターネット回線を割り当てる「セルラーフェイルオーバー」用のデバイスとしても同時に使用することができる。Raspberry Piをルーターとして動作させた場合、回線側が高速でも実行スループットは4.5Mbps程度のようなのであまり高速とは言えないが、バックアップ用途であればこれで十分な場合もあるだろう。なお、有線LANポートはトンネルを張るためのインターネット接続にも使用できる。ブロードバンドルーターのLANポートや企業内の既存のイントラネットなどに有線接続し、NATやファイアーウォールを通ってインターネットへ抜けることが出来さえすればトンネルを張ることができる。有線LANポートのIPアドレスは、Raspberry Piの起動時にDHCPでの取得が試行され、DHCPサーバーからの応答がなければあらかじめ設定済みの固定IPアドレスになるようにしてある。モバイルコンソールはFTPサーバーも有効にしてあり、有線LANポートからFTPサーバーへアクセスする際はこの固定IPを使えばいい。

今回、SSHDが作動する中継サーバー「Pi Server」はADSLのインターネット回線に接続した。この回線は、ISPの固定IPアドレスオプションを契約しているが、DDNSを使う方法でも構わない。回線事業者が提供するモデムルーターに設定を追加し、インターネットからTCP 443宛の接続をLAN内のPi Serverに転送するわけだが、今回使ったこのNTTフレッツADSL用のモデムルーター(NVIII)は、ポート番号を指定してLAN内の任意ホストへ転送する機能が無いらしい。プロトコルに関わらずDMZとしたLAN内の任意のホストへ転送することはできるようなので今回はその機能を使うことにした。Pi ServerはSSHD以外のポートは開いていないが、念のためiptablesの設定でTCP 443宛の接続以外はDROPするようにしている。

モバイルコンソールを使用するためにPi Serverに接続したユーザーは、.bash_profileの記述によってログイン後すぐにSSHコマンドが起動されてモバイルコンソールへ転送され、ログイン後にまたすぐに.bash_profileで自動的にcuプログラムが起動される仕組みになっている。仕組み上、Pi Serverからモバイルコンソールへの転送中に^Cを連打すればPi Serverかモバイルコンソール上のコマンドプロンプトに抜け落ちてしまうことは可能で、また、cuプログラムにはシェル経由でコマンドを実行することができてしまう$!エスケープシーケンスがあるため、想定しないシェルコマンドがユーザーによって実行されるのを防止するために、Pi Server側とリモートコンソール側で使用されるユーザーは、rbashによるコマンド制限を行っている。Pi Serverには通常のシェルコマンドを実行することができる管理用のユーザーも用意してあり、トンネルを通ってリモート側のRaspberry Piにログインすることができる。これを使って、新しい無線LANアクセスポイントを使う場合のパラメータ設定や、cuコマンド起動時のシリアルポートのスピード変更などを行う。

バージョンアップ履歴。

SSHトンネリングを使ってNAT/FW超えする機能は当初からあったが、初期バージョンは一台のモバイルコンソールにしか対応していなかった。その後のバージョンアップで、複数デバイスへの対応やトンネル切断時の再接続方法の見直し、GPIOを使ったステータスLEDの実装、パスワードファイルの暗号化や公開鍵認証への対応などを行い、最終的には中継サーバーもRaspberry Piを使う現在の方式にした。
途中のマイナーバージョンアップでは、TTSエンジンとWaveファイルによる音声ステータスの機能をひそかに実装した。これはアナログオーディオジャックのあるRaspberry Piモデルで有効で、起動時にDHCPで取得したインターフェースのIPアドレスやESSID名、トンネルの接続状況や再接続中のステータスなどをイヤホンや小型スピーカーから音声出力させるものである。日本語にはTTSエンジンOpen JtalkとMMD Agentの音響モデル「メイ」、英語にはESPEAK TTSを使っている。なお、Raspberry Pi Zero Wについては、せっかく付いているBluetooth経由での音声出力も実験してみたが、接続が不安定でペアリングの操作もたびたび必要なことから実用的ではなく、実際のところGPIOに接続した一灯のLEDでも十分に用を足すので、Raspberry Pi Zero Wではしゃべる機能は使っていない。
また、最新のバージョンでは、シリアルコンソールを使用中のユーザーが一文字づつタイピングしている様子を、別のセッションからリモートデスクトップの画面共有のようにリアルタイムに表示することができる機能を装備した。ちょっと不思議な機能だが、方法は意外と簡単で、cuコマンドにはログを出力する機能が無いが、scriptコマンドを使えばログの記録が簡単にでき、-f オプションを付けることでファイルがリアルタイムに保存されるので、別のセッションでPi Serverの管理用ユーザーにログインし、記録中のログファイルをtailfコマンドで表示させるだけである。