事のあらまし
ネットワーク機器を触るときコンソールサーバがあるととても便利。Telnet接続でもいいがIPアドレスがいるうえ、管理ポートが落ちたりROMMONに入ったら現地に走っていくことになる。
というわけでコンソールサーバが欲しいのだが、なかなか予算も貰えないので自分で作ってみることにした。
最終的にこんな感じになった。コンソール7ポートでシステム停止ボタン付き。
1. 目標
- よくあるコンソールサーバと同じ使い勝手のものを作る
- PC~コンソールサーバ間はSSH接続
- コンソール操作はログを自動で残す
- コンソールサーバ上はコンソール操作専用とする
- ユーザができるのはコンソール操作と操作ログの参照のみ
- 踏み台サーバやスクリプト書いたりといった利用はさせない
- シリアルポートの設定は9600baud固定
- 57600, 115200baudあたりも最近はあるので対応したいが。。。
oresama@LocalPC$ ssh con@192.168.1.100 <-- ラズパイコンソールサーバに接続
Password:
con@PiCS$ console com1 <-- コンソールセッションを開く
[Enter `^Ec?' for help]
Username: hogehoge <-- シリアルポート経由でCisco機器が触れる
Password:
Router> enable
Router# conf t
Router(config)#
...
2. 実施環境
使用機材
- HW: RaspberryPi 4 Model B 4GB
- USBシリアルケーブル:今回はVANDESAIL USB RJ45(FT232)×2個
- USBハブ:安定動作のためセルフパワーが望ましい
- SW: RaspberryPi OS Lite 32bit, May 7th 2021
- bash 5.0.3(1)
- OpenSSH server 7.9p1
- Conserver 8.2.6
あとシリアルで操作するネットワーク機器か何か。今回はC1812-J×2台を使用。
接続構成
eth0 ttyUSB* con
PC ------------ PiCS ============ Cisco
192.168.1/24
-----SSH-----> ---Console--->
- PiCS: 構築するコンソールサーバ (192.168.1.100)
- PC: クライアントPC (192.168.1.xx)
- GW(図にない): ゲートウェイルータ (192.168.1.254)
- Cisco: シリアルコンソール接続で操作するネットワーク機器
3. OS初期設定
Raspberry Pi ImagerでSDカードにOSイメージを書き込み、/boot/ssh
を配置して起動する。OSが起動したらSSHで接続し、pi/raspberryでログインしたところからスタートする。
ホスト名・ネットワーク設定の設定
いつもの手順。
$ sudo raspi-config
- hostname: PiCS (何でもよい)
- timezone: Asia/Tokyoを選択
- locale: en_US.UTF8、ja_JP.UTF8を追加し、en_US.UTF8デフォルトに設定
$ sudo nano /etc/dhcpcd.conf
interface eth0
static ip_address=192.168.1.100/24
static routers=192.168.1.254
static domain_name_servers=8.8.8.8 8.8.4.4
$ sudo reboot
$ ifconfig eth0 <-- IPアドレスが指定したものになっていること
$ route <-- デフォルトルートがGWに向いていること
$ ping 192.168.1.254 <-- GWへの疎通確認
$ ping 8.8.8.8 <-- インターネット向けの疎通確認
ビルド環境の導入
いつもの手順その2。後で使うのでbuild-essentialとufwを入れておく。
$ sudo apt update
$ sudo apt upgrade -y
$ sudo apt install build-essential ufw
NTPサーバの登録
どこでもいいがログに時刻情報がつくようにするのでNTPで同期しておく。
$ sudo nano /etc/systemd/timesyncd.conf
(以下の部分にNTPサーバアドレスを記述。ntp.nict.jpなど)
[Time]
NTP=192.168.1.254
$ sudo systemctl enable systemd-timesyncd
$ sudo systemctl start systemd-timesyncd
$
$ timedatectl
Local time: Sat 2021-07-10 12:18:33 JST
Universal time: Sat 2021-07-10 03:18:33 UTC
RTC time: n/a
Time zone: Asia/Tokyo (JST, +0900)
System clock synchronized: yes
NTP service: active <-- ここがactiveになる
RTC in local TZ: no
USBシリアルケーブルの接続
USBシリアルケーブルをラズパイが認識し、デバイスファイルが作成されていることを確認する。あとでここで確認したデバイスファイル名をconserverの設定に登録する。
$ dmesg | grep ttyUSB
[ 6.788169] usb 1-1.2: FTDI USB Serial Device converter now attached to ttyUSB0
[ 6.795067] usb 1-1.3: FTDI USB Serial Device converter now attached to ttyUSB1
$ ls -l /dev | grep ttyUSB
crw-rw---- 1 root dialout 188, 0 Jul 10 10:58 ttyUSB0 <-- ここのファイル名をあとで使う
crw-rw---- 1 root dialout 188, 1 Jul 10 10:59 ttyUSB1
- USBシリアルケーブルをラズパイが認識すると
/dev/ttyUSB*
が作成される
4. Conserver導入・設定
RPiOSのaptリポジトリにはいまのところconserverが登録されていないためソースからビルドする必要がある。そのためのbuild-essential。
Conserverの導入
githubからソースを落としてきてビルドとインストールを行う。
$ wget https://github.com/bstansell/conserver/releases/download/v8.2.6/conserver-8.2.6.tar.gz
$ sudo tar zxf conserver-8.2.6.tar.gz -C /usr/src
$ cd /usr/src/conserver
$ sudo ./configure
$ sudo make && sudo make install
- ソースの配置先(どこでもいい)
/usr/src/conserver
- インストール先
/usr/local/sbin/conserver
/usr/local/bin/console
- 設定ファイル
-
/etc/hosts
:Linuxのシステムファイル。接続先情報を追記する -
/etc/services
:Linuxのシステムファイル。conserverのポート番号を追記する -
/usr/local/etc/conserver.cf
:サーバの動作設定。新規で作る -
/usr/local/etc/conserver.passwd
:認証パスワード設定。新規で作る
-
Conserverの設定
サンプル設定ファイルもあるのだが1から書いたほうが分かりやすい。
default * {
master localhost;
# コンソールポートごとのログ設定
logfile /var/log/console/&;
logfilemax 4m;
timestamp 1hab;
# コンソールポート設定
rw *;
type device;
baud 9600;
parity none;
# 接続時のメッセージで切断方法を表示
motd "Type Ctrl-E, C, '.' to close this session";
}
# 認証なしでlocalhostからのみ接続可
access * { trusted 127.0.0.1; }
# comXの名前でシリアルポートを登録(速度設定はdefault *から引継ぎ)
console com1 { type device; device /dev/ttyUSB0; } <-- 確認したデバイス名
console com2 { type device; device /dev/ttyUSB1; }
# (必要なだけ書く)
-
access *
:接続時(セッション開始時)の認証設定-
trusted 127.0.0.1
: localhostからのアクセスを認証なしで許可 - trustedの対象をallowedにも書く必要はない。先にマッチしたほう優先
-
-
default *
:全セッションのデフォルト設定-
master localhost
:よく分からないがとりあえずlocalhostでいいらしい -
logfile
,logfilemax
,timestamp
:コンソールセッションのログ設定。4MBまで、1時間ごとにタイムスタンプを残す -
rw *
:コンソールセッションに対して読み書きを全ユーザ許可 -
baud
,parity
:シリアルポートのデフォルト設定。Ciscoなどのデフォルトに合わせる -
motd
:セッション開始時に表示するコメント。セッションの切断方法を表示しておく
-
-
console <console-name>
:コンソールセッション設定-
type device
:接続先がデバイスファイル -
device <device-file>
: シリアルポートのデバイスファイルを指定 - シリアルポート設定やログ設定はdefault *から自動的に引き継がれる
-
$ sudo nano /usr/local/etc/conserver.passwd
*any*:*
- 今回はtrustしたlocalhostからの接続のみ受け付け、認証もしないので実際には使われない(はず)。
$ sudo mkdir /var/log/console
-
conserver.cf
のlogfile
で指定したディレクトリを作成 - あとでユーザから参照しやすいようシンボリックリンクを張る
$ sudo nano /etc/hosts
(末尾に以下を追加)
127.0.0.1 console
- ホスト名設定
- console: consoleコマンドの接続先アドレスを設定 (conserverを動かすホスト。今回はlocalhost)
- consoleコマンドは内部で接続先サーバをホスト名指定している模様。hostsかDNSで名前解決ができないと動かない。
$ sudo nano /etc/services
(末尾にサービスのエントリを追加)
# Local services
console 782/tcp conserver
- サービス設定
- console: consoleコマンドの接続先ポート
- conserver (alies): conserverコマンドの待ち受けポート
- 782/tcpはビルド時に--with-portオプションを変えていない場合のデフォルトポート
動作確認1
ターミナル2面を開いてconserverコマンド(サーバ)、consoleコマンド(クライアント)を実行してみる。クライアントからコンソール経由でシリアルケーブルの接続先機器が操作できればOK。
pi@PiCS:~ $ sudo conserver <-- 操作(1) conserverのテスト起動
[Sat Jul 10 10:57:49 2021] conserver (1163): conserver.com version 8.2.6
[Sat Jul 10 10:57:49 2021] conserver (1163): started as `root' by `pi'
[Sat Jul 10 10:58:20 2021] conserver (1164): [com1] login pi@localhost <-- 操作(2)の出力ログ
[Sat Jul 10 10:59:21 2021] conserver (1164): [com2] login pi@localhost <-- 操作(3)の出力ログ
[Sat Jul 10 10:59:21 2021] conserver (1164): [com1] logout pi@localhost
[Sat Jul 10 10:59:30 2021] conserver (1164): [com2] logout pi@localhost <-- 操作(4)の出力ログ
^C[Sat Jul 10 11:02:59 2021] conserver (1163): terminated <-- 操作(5 ) Ctrl-C
pi@PiCS:~ $ ps -ef | grep conserver -v grep <-- conserverプロセスの動作確認
root 1164 1 0 10:58 ? 00:00:00 /usr/local/sbin/conserver <-- これ
root 1165 1164 0 10:59 ? 00:00:00 /usr/local/sbin/conserver <-- これも
pi 1204 1174 0 11:01 pts/1 00:00:00 grep --color=auto conserver
pi@PiCS:~ $ netstat -a | grep console <-- consoleサービスの待ち受け確認
tcp 0 0 0.0.0.0:console 0.0.0.0:* LISTEN
pi@PiCS:~ $ console com1 <-- 操作(2) シェルでconsole com1を実行
[Enter `^Ec?' for help]
[-- MOTD -- Type Ctrl-E, C, '.' to close this session]
User Access Verification
Username:
[console: com2] <-- 操作(3) Ctrl-E, C, ';' -> com2
[Enter `^Ec?' for help]
[-- MOTD -- Type Ctrl-E, C, '.' to close this session]
com2-Router#
com2-Router#[disconnect] <-- 操作(4) Ctrl-E, C, '.'
pi@PiCS:~ $
5. Systemd設定
Conserverのサービス登録
$ sudo nano /etc/systemd/system/conserver.service
[Unit]
Description=conserver daemon
After=network.target
[Service]
Type=simple
RemainAfterExit=no
ExecStart=/usr/local/sbin/conserver
ExecStop=kill `cat /var/run/conserver.pid`
ExecRestart=kill `cat /var/run/conserver.pid` ; /usr/local/sbin/conserver
PIDFile=/var/run/conserver.pid
Restart=always
[Install]
WantedBy=multi-user.target
-
ExecStart
:単に起動しているだけ。停止したらrestartさせたいのでデーモン化しない -
ExecStop
:PIDファイルに記述されたPIDをkill - ``ExecRestart'':ExecStop・ExecStartと同じことを順に実行
-
RemainAdterExit
:ExecStart/ExecRestart実行後に制御が戻った場合にプロセスが動作しているか、終了しているかの情報 -
Restart
:プロセス終了を検知したときにExecStartを再実行するか。 -
After
:他のサービスの起動完了後に実行
Conserverサービスの起動
$ sudo systemctl daemon-reload
$ sudo systemctl enable conserver
$ sudo systemctl start conserver
6. ファイアウォール設定
$ sudo ufw reset
$ sudo ufw default deny
$ sudo ufw allow ssh
$ sudo ufw enable
- リモート接続用のSSHポートのみ許可して後はすべて閉じる
- conserverの782/tcpはローカルホストのみにサービスするため開ける必要なし
7. コンソール操作専用ユーザの設定
rbashを使ってコンソールポート操作だけ行える共通ユーザアカウントを作る。
共用サーバとして自由に使えてもいいのだが環境汚れるし、ラズパイのSDカード上であれこれやられたくないので操作を制限したユーザを置くことにする。
$ sudo adduser con
[sudo] password for pi:
Adding user `con' ...
Adding new group `con' (1002) ...
Adding new user `con' (1002) with group `con' ...
Creating home directory `/home/con' ...
Copying files from `/etc/skel' ...
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for con
Enter the new value, or press ENTER for the default
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n] y
$ sudo usermod -s /usr/bin/rbash con
- SSHでログインするコンソール操作用のユーザを追加。ここでは"con"とした
- 作成したユーザのログインシェルを
/usr/bin/rbash
に変更 - dialoutグループなどに追加でグループに所属させる必要はない
- 作成したユーザのログインシェルを
$ sudo rm /home/con/.profile
$ sudo rm /home/con/.bashrc
$ sudo rm /home/con/.bash_profile
$ sudo nano /home/con/.profile
export PS1='PiCS$ '
export PATH=/opt/rbash
shopt -s histappend
shopt -s checkwinsize
shopt -u progcomp
shopt -u promptvars
HISTCONTROL=ignoreboth
HISTSIZE=100
HISTFILESIZE=100
$ sudo ln -s /home/con/.profile /home/con/.bashrc
$ sudo ln -s /home/con/.profile /home/con/.bash_profile
-
root:rootのファイルとしてプロファイルを再作成
- PATHを
/opt/rbash
のみに上書き(重要)。このディレクトリに配置した実行ファイルのみ叩けるようになる - その他は任意。入力補完とかは切っておいた方がよいはず。
- ユーザ自身のファイルとしてプロファイルを作ると書き換えが可能になってしまうのでrootの持ち物にしておく
- PATHを
$ sudo mkdir /opt/rbash
$ sudo ln -s /usr/local/bin/console /opt/rbash/
$ sudo ln -s /usr/local/bin/cat /opt/rbash/
$ sudo ln -s /usr/local/bin/more /opt/rbash/
$ sudo ln -s /usr/local/bin/less /opt/rbash/
$ sudo ln -s /usr/local/bin/tail /opt/rbash/
$ sudo ln -s /usr/local/bin/grep /opt/rbash/
$ sudo ln -s /usr/local/bin/ls /opt/rbash/
$ ls -l /opt/rbash
total 4
lrwxrwxrwx 1 root root 12 Jul 8 09:37 cat -> /usr/bin/cat
lrwxrwxrwx 1 root root 22 Jul 5 17:55 console -> /usr/local/bin/console
lrwxrwxrwx 1 root root 13 Jul 8 09:38 grep -> /usr/bin/grep
lrwxrwxrwx 1 root root 13 Jul 8 09:41 less -> /usr/bin/less
lrwxrwxrwx 1 root root 11 Jul 8 09:37 ls -> /usr/bin/ls
lrwxrwxrwx 1 root root 13 Jul 8 09:38 more -> /usr/bin/more
lrwxrwxrwx 1 root root 13 Jul 8 09:50 tail -> /usr/bin/tail
- rbash向けに通したパスに実行を許可するコマンドのシンボリックリンクを貼る
-
console
がconserverに接続するための必須コマンド -
cat
,more
,less
,tail
,grep
はログ確認用 -
ls
はログディレクトリのファイル一覧表示用
-
$ sudo ln -s /var/log/console /home/con/log
動作確認2
PiCS$ console com1
[Enter `^Ec?' for help]
[-- MOTD -- Type Ctrl-E, C, '.' to close this session]
User Access Verification
Username: [disconnect]
PiCS$ tail -10 log/com1
[-- con@localhost attached -- Mon Jul 12 12:19:07 2021]
User Access Verification
Username: [-- con@localhost detached -- Mon Jul 12 12:19:11 2021]
PiCS$お
PiCS$ cd /
-rbash: cd: restricted
PiCS$ rm log/com1
-rbash: rm: command not found
PiCS$ telnet 192.168.1.254
-rbash: telnet: command not found
- SSH接続のあとコンソール接続・操作ができ、
~/log/com*
でログの閲覧ができた - 許可されていないコマンドはnot foundになり、カレントディレクトリの移動なども失敗する
お疲れさまでした!
99. 本文でやっていないこと
デフォルトユーザpiの無効化
ちゃんとした管理ユーザを設定し、piユーザを削除またロック。忘れずにやりましょう。
SDカードのRead-only化
高耐久のSDカードを使用すれば当面持つようだが、ログを書き込んでいるのでそのうちファイルシステムが飛んでしまうかも。
/
をROでマウント、tmpfsを/var/log
に割り当ててそこにログを記録する方がよいはず。
USBシリアルケーブルのデバイスファイル名の固定
USBシリアルケーブルは認識順で対応するデバイスファイルが変わることがあり、コンソールポートの並びが変わることがあります。
コンソールサーバだとそれは困るので、あるUSBシリアルケーブルには必ず同じデバイスファイルが割り当てられるようにudevに登録するか、/dev/serial/by-path/
配下のデバイスファイルからアクセスするようにするべき。
やり方
-
udevadm
でベンダーID・モデルIDとシリアル番号を調べる - 調べた内容をudevルールファイルに記入し、指定のシンボリックリンクが作成されるようにする (あるシリアル番号のケーブル=常に
/dev/ttyUSB-com1
など) - udevルールをリロードし、指定シリアルに対して設定したデバイスファイル(シンボリックリンク)が作成されることを確認
- conserverの参照先デバイスファイル名をシンボリックリンク側に変更
$ udevadm info -q property -n /dev/ttyUSB0 | grep -e _ID -e _SERIAL
ID_VENDOR_ID=0403
ID_MODEL_ID=6001
ID_SERIAL=FTDI_FT232R_USB_UART_A9XPPS**
ID_SERIAL_SHORT=A9XPPS**
$ udevadm info -q property -n /dev/ttyUSB1 | grep -e _ID -e _SERIAL
ID_VENDOR_ID=0403
ID_MODEL_ID=6001
ID_SERIAL=FTDI_FT232R_USB_UART_A9BF1J**
ID_SERIAL_SHORT=A9BF1J**
...
$ sudo nano /etc/udev/rules.d/50-usb-serial.conf
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="A9XPPS**", SYMLINK+="ttyUSB-com1", GROUP="dialout"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="A9BF1J**", SYMLINK+="ttyUSB-com2", GROUP="dialout"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="A9L4YM**", SYMLINK+="ttyUSB-com3", GROUP="dialout"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="A9A9KU**", SYMLINK+="ttyUSB-com4", GROUP="dialout"
$ sudo udevadm control --reload-rules
$ sudo udevadm trigger
$ ls -l /dev | grep ttyUSB
crw-rw---- 1 root dialout 188, 0 Jul 10 19:08 ttyUSB0
crw-rw---- 1 root dialout 188, 1 Jul 10 19:08 ttyUSB1
crw-rw---- 1 root dialout 188, 2 Jul 10 19:08 ttyUSB2
crw-rw---- 1 root dialout 188, 3 Jul 10 19:07 ttyUSB3
lrwxrwxrwx 1 root root 7 Jul 10 19:07 ttyUSB-com1 -> ttyUSB0 <--ここから4つ。別名がついている
lrwxrwxrwx 1 root root 7 Jul 10 19:07 ttyUSB-com2 -> ttyUSB1
lrwxrwxrwx 1 root root 7 Jul 10 19:07 ttyUSB-com3 -> ttyUSB2
lrwxrwxrwx 1 root root 7 Jul 10 19:07 ttyUSB-com4 -> ttyUSB3
$ sudo nano /usr/local/etc/conserver.cf
(コンソールのデバイスファイル名を変更)
console com1 { type device; device /dev/ttyUSB-com1; }
console com2 { type device; device /dev/ttyUSB-com2; }
console com3 { type device; device /dev/ttyUSB-com3; }
console com4 { type device; device /dev/ttyUSB-com4; }
$ sudo systemctl restart conserver
参考URL
- Conserver本家
- Conserverの先人ユーザ
- rbashの設定方法
- udevルールの書き方
- ラズパイをGPIOのボタンでシャットダウンさせる話