Ubuntu Weekly Recipe

第879回ser2netで作るリモートシリアルコンソール環境

世の中には「シリアルコンソールサーバー」という個人にはそこそこお高い製品があります。これは複数のシリアルポートをネットワーク越しに利用できる機材です。今回はこのシリアルコンソールサーバーを、Ubuntuと適当なマシンを使って安価に作ってみましょう。

シリアルコンソールサーバーが必要な理由

世の中のネットワーク機器をはじめとする組み込み製品は、普段使っているPCのようなリッチなインターフェースを持っているとは限りません。LAN経由で操作できればまだいいほうです。大抵の場合はD-Sub 9ピン(EIA-574)やRJ45ライクなコネクターのシリアルポートだけなことも多いでしょう[1]

図1 Raspberry Piを始めとするシングルボードコンピューターでは、このようにUART端子をPCのシリアルポートに繋ぐ。ボードの下にあるRaspberry Pi Debug Probeが、USBシリアルケーブルとなり、PCに接続される

よってこれらの機器を操作するためには、シリアルコンソールが必要になります[2]。しかしながら最近のPCには、D-Sub 9ピンのポートはありません。そこでよく使うのが、シリアルポートが搭載されたPCIeカードや、USBシリアル変換ケーブルです。しかしながら、ミニPCだとPCIeカードスロットがないことも多いですし、USBの場合はたくさんの機器をつなぐとなると十分な数のUSBポートが必要になります。また、どのポートがどの機器に繋がっているかの管理も必要です[3]

安価なシリアルケーブルはノイズに弱く、距離を稼げません。つまりターゲット機材から手元のマシンまでコンソールケーブルをひけないことも多々あります。よしんばひけたとしても、使う人が変わる度にケーブルをひきなおすのは面倒です。このケーブルはどのデバイスに繋がっているのか、クロスとストレートどっちだっけ、ジェンダーチェンジャーどこやったーなど、きちんと管理できていないと悲惨です。

そこで出てくるのがシリアルコンソールサーバーです。とりあえずコンソールケーブルをつなぐマシンを用意しておき、あとはネットワークを経由してそのサーバーにアクセスしましょうという仕組みです。きちんとした品質・機能を備えた製品を買おうとすると、それなりの価格になります。しかしながら単に複数のシリアルコンソールを単一のPCへとつなぐだけであれば、SSHログイン可能なPCを用意すればいいだけではないか、とも考えるでしょう。たとえばRaspberry PiにUSBシリアル変換ケーブルを4ポート分接続すれば、4台のRaspberry Piやその他のシリアルコンソールデバイスへとアクセスできます。よって大本のRaspberry PiにSSHアクセスできれば、何らかの理由により残りのRaspberry Piのネットワークに応答しなくなったとしても原因を追求できます。

それで事足りるのであれば今回の話は終了です。次週のRecipeにご期待ください。ありがとうございました。

さて、単にSSHサーバーを用意するだけでなく、もう少しコンソールポートの管理っぽいものも導入したいことがあるでしょう。たとえば次のような状況を考えてみます。

  • シリアルポートごとにボーレートなどの設定が異なり、アクセスするたびに正しい速度を確認するのが面倒
  • 誰かが使っていても同時に接続したいもしくは使用しているユーザーをキックしたい
  • シリアルコンソールの操作履歴をログとして常に残しておきたい
  • 踏み台となるSSHサーバーのアカウント管理をしたくない、使用者全員をdialoutグループに入れたくない

いずれも踏み台サーバーでも実現できるけれども、若干面倒な対応が必要となります。これを実現するのが今回紹介するser2netです。

ser2netはYAML形式でシリアルコンソール設定を管理でき、なおかつ上記のような機能を実現できます。具体的には次のような機能を備えています。

  • 一般的なtelnetコマンドでシリアルコンソールにアクセスできる
  • 複数ユーザーの同時接続のサポート
  • 新規ユーザーが接続した場合に、古いユーザーを排除する機能
  • IPMI SOLのサポート
  • YAMLベースの柔軟な設定ファイルフォーマット
  • 操作ログのファイルへの保存機能
  • 無操作時の自動タイムアウト機能
  • アクセス時のバナー表記のカスタマイズ
  • mDNSによるシリアルコンソールごとの名前の公開
  • SSL/TLS証明書を利用したユーザー認証
  • RFC 2217に準拠したコンソール接続オプションの変更

端的にいうと、ser2netはネットワーク経由でシリアルコンソールにつなげるプロキシサーバーです。また、ser2netはgensioをベースに作られています。よってgensioがサポートしているストリームであれば、たとえば音声なども同じ仕組みでやりとりできたりします。

基本的にtelnetプロトコルを使うため、経路は暗号化されない点に注意してください。もし暗号化したい場合は、SSL/TLS等を利用して暗号化することになりますし、ser2net/gensioはその設定方法も存在します。

ちなみに似たような機能を持ったソフトウェアとしてConManが存在します。こちらもser2netでできることはたいていできます。ser2netはどこでもインストールされている可能性が高いtelnetコマンドを使えるのが利点で、ConManのほうはconmanプログラムを使って接続することになっています。それ以外の部分はConManのほうが高機能な印象です。ser2netでは管理が大変と感じるようになったら、ConManも試してみると良いでしょう。

Ubuntuにser2netをインストールする

前置きが長くなってしまいましたが、Ubuntuにser2netをインストールしましょう。

$ sudo apt install ser2net

一緒にALSAやAvahi関連のライブラリも入ってしまいます。これはser2netがlibgensioを使っており、libgensioがオーディオストリームの転送やmDNSによるサービス名の公開に対応しているためです。

次に/etc/ser2net.yamlを変更します。この設定ファイルはかなり設定項目が多くなっています。詳しいことはmanページを参照してください。ここでは簡単な設定例を示します。

%YAML 1.1
---
define: &banner \r\nser2net port \p device \d [\B] (Ubuntu)\r\n\r\n

connection: &con1
  accepter: telnet,2000
  enable: on
  options:
    banner: *banner
    kickolduser: true
  connector: serialdev,
            /dev/ttyUSB0,
            115200n81,local

最初の2行はYAMLのメタデータです。

3行目は、YAMLのアンカーを定義しています。ここでは&bannerとしてアンカーを定義し、*bannerでエイリアスとして参照できるようになっています。bannerで使用しているエスケープシーケンスはそれぞれ次のとおりです。

  • \r\n:CRLF
  • \p:accepterで指定しているポート番号を含む文字列(今回の例だとtelnet,2000
  • \d:connectorで指定しているデバイス情報(今回の例だとserialdev,/dev/ttyUSB0,115200n81,local
  • \B:実際に使われているシリアルポートの情報(たとえば115200N81,CLOCAL,HANGUP_WHEN_DONEなど)

他に使えるエスケープシーケンスはmanページの「SPECIAL STRING HANDLING」を参照してください。

connection: &conで個々のシリアルポートの接続情報を定義します。

  • accepter:外部から接続するために必要なプロトコル、ポート番号などを指定する(今回の例だとtelnetプロトコルで2000番ポートを使う)
  • enable:この接続情報を有効化するかどうか。設定はするけれども一時的に使えなくしたい場合はoffを指定する
  • options:各種オプション
  • banner:接続時に表示するバナー文字列。どのデバイスへと接続したかを明示したい場合に便利
  • kickolduser:新規接続時に古いユーザーを切断させるかどうかを指定する
  • connector:シリアルポートに接続するオプション等を指定する(今回の例だと/dev/ttyUSB0に対して115200n81で接続する)

おおよそ上記の説明を把握すれば、最低限の設定方法は想像できるでしょう。

accepterconnectorはgensioのオプションがそのまま使えます。たとえばacceptertelnet(rfc2217)を指定すると、RFC 2217準拠の手順でシリアルポートのボーレート等を設定変更できます。他にもtelnet,::1,2000などとポート番号の前にホスト名やIPアドレスを指定することで、特定のネットワークインターフェースでのみ待ち受けることも可能です。

connectortelnetを指定するとtelnetプロトコルでカスケード接続できますし、ipmisolでIPMI SOLに接続できます。ちなみに上記の「local」はCLOCALを設定する(モデム制御をしない)オプションです。

設定を変更したら、ser2netを再起動しましょう。

$ sudo systemctl restart ser2net.service

ちなみに既に誰かがser2netに接続している場合は、すべて切断されてしまう点に注意してください。これで準備完了です。ser2netをインストールしたマシンもしくはその外からアクセスしてみます。

$ telnet 192.0.2.1 2000
Trying 192.0.2.1...
Connected to 192.0.2.1.
Escape character is '^]'.

ser2net port telnet,2000 device serialdev, /dev/ttyUSB0, 115200n81,local [115200N81,CLOCAL,HANGUP_WHEN_DONE] (Ubuntu)

ubuntu login:

無事にシリアルコンソールに接続し、ログインプロンプトが表示されました。あとはシリアルポートごとに設定connectionブロック)を追加し、それぞれに別のポート番号を割り当てるだけです。

複数人が同時に接続できるようにする

ser2netの初期設定では、誰かが接続中の状態で別のユーザーが接続しようとしても、接続できないようになっています。これはoptionsブロックの次の設定で変更可能です。

  • kickolduser:新しい接続が優先され、古い接続は切断される
  • max-connections:同時接続可能な数を設定する
  options:
    kickolduser: false
    max-connections: 3

たとえば上記のように設定した場合、新しいユーザーが接続しようとしても古い接続は維持されます。さらに最大3コネクションまでは同じポートで接続できるのです。

ちなみに同時接続した場合、あるコネクションで入力した文字列は、別のコネクションでもエコーバックされます。複数人が同時に接続して状態を操作手順を確認したい場合に便利です。

無操作時に自動でタイムアウトする

timeoutで、無操作時にconnectorが何も受信しないときに)自動で切断する秒数を指定できます。

  timeout: 30

前述のkickolduserを使わない場合に、ずっとシリアルポートを握り続けることがないようにできます。

ロギング機能で常時ログを残すようにする

trace機能を使うと、ユーザーがaccepterに接続した間の操作ログをファイルに記録できます。

  options:
    trace-read: /var/log/con1.log
    trace-timestamp: true

ここでtrace-readはデバイスから受信したデータを指定したファイルに残すオプションです。エコーバックが有効なデバイスであれば、ユーザーが入力した文字列も受信しますので、ファイルに保存されます。通常はtrace-readだけでいいでしょう。trace-writeはユーザーが入力したデータ、trace-bothは両方を保存するオプションとなります。ユーザーが入力したデータだけほしい場合やエコーバックしない環境で使えます。ちなみに、trace-{read,write,both}は同時に指定することで、それぞれ別のファイルに保存できます。

ファイル名にはbannerと同じエスケープシーケンスが使えます。ポート名にあわせたファイル名を指定したい場合に便利です。

trace-timestampはタイムスタンプを残してくれる機能なのですが、上記の設定だと接続時と切断時しか残してくれません。1行ごとに残したい場合は、trace-hexdumpオプションを使う必要があります。これは受信したデータのバイト列を十六進数で表示する仕組みです。何か変なバイト列が含まれていないかを確認する場合に便利です。

ちなみにtrace機能で記録できるのはユーザーがアクセスしている間のログだけです。常時保存されるわけではありません。たとえばカーネルのpanicのようにいつ起こるかわからないものを記録にとりたい場合は、常時接続する必要があります。前項の複数同時接続と組み合わせて使うことになるでしょう。

IPMI SOLに接続する

connectorにはシリアルコンソールデバイスだけでなく、IPMIのSerial Over Lan(SOL)にも接続可能です。これはいわゆる一般的なサーバーマシンに搭載されている、マネージメント用のインターフェース経由でシリアルコンソールに接続する仕組みです[4]

  connector: ipmisol,lan -U USER -P PASSWORD -p 9001 ipmi.example.org,115200

IPMIパスワードの指定が必要になります。たとえば-P *{パスワードファイル名}と書くことで、直接設定ファイルに書かない方法も存在します。


一般的なPCを使う上でシリアルコンソールが必要になることはそこまでありません。そもそも最近のPCだとシリアルコンソール出力はもちろんのこと、シリアルポート自体がないことも普通です。

しかしながらサーバースペックなマシンに触れたり、ネットワーク装置や何らかの制御装置を使ってみると、シリアルコンソールの便利さが身にしみます。だってネットワークの設定を間違えても、まだリモートマシンを操作できる可能性が残るのですから。あと変なデバイスがいるせいで、NICデバイスをうまく認識できない場合もなんとかなりますし。ディスプレイがない環境でもGRUBを操作できるのもうれしいですよね。

このように何かあったときのための安心感を得る上で、シリアルコンソールは大変便利な仕組みです。ぜひご家庭の1Uサーバーやネットワークスイッチ、制御機器、Raspberry Piなどのシリアルコンソールの運用も、ser2netを使って見直してみてはいかがでしょうか。

おすすめ記事

記事・ニュース一覧