多段ssh, 強制リブート - 第二回FreeBSD勉強会へ行ってきました〜内容編〜

さて、前回会場に行くまでのチラ裏的内容でしたので、今回は内容に触れてみたいと思います。
今回の内容は、勉強会のページの通り、1時間半の拡大版で入門レベルから発展レベルまでカバーしたリモート管理のノウハウについてでした。
FreeBSD固有のものというよりかは、サーバ管理全般に言える内容でしたので、*BSD系に限らずサーバ管理を行われている方にとっては非常に有益な情報かと思います。今回の勉強会資料が公開されたら、是非目を通してみる事をおすすめします。

内容は

ヨタの日々さんの日記にも詳細に書かれていますが、大まかに以下のとおりでした。
なお、今回は以下の中からsshについて(ssh-agentについて)と、何とかサーバを無理やりリブートさせる方法(デバッグオプション有効)について書きたいと思います。

  • sshについて
    • 秘密鍵ばら撒くな、多段で踏まなくてはならない場合はssh-agentを使おう
  • シリアルコンソールの活用
    • sshが使えなかったら、次にこれを試す
    • ブートローダから制御できる
    • OSがreboot/shutdown命令を受け付けない時には、これ経由で無理やりリブート
  • 接続の管理
    • シリアルコンソールは、アナログモデム用ダイアルアップモデムを活用するとかして纏める。
      • シリアル接続のハブとして活用。中古の払い下げ品で3〜4万程度(portmasters.com)
    • sshだとかtelnetだとかシリアルだとか、conserverを使えば意識せずに使える
  • IPMIについて
    • ハードウェア制御で、色々できる
    • 電源ON/OFFをLAN経由で。マイコン制御。
  • 安定しないサーバでも、何とか無理やり運用したい!
    • watchdog timerを使って、システムが刺さったら自分でリブートするようにしてやる(最終手段)

ssh

リモート管理に無くてはならないssh。サーバを遠隔操作するときに、まずのこのsshが使えるかどうかってのが、問題対処の一つの目安となります。
ここで気をつけなければならないのは、sshのセキュリティ。鍵の扱いがおざなりな人が多いようです。
言うまでもないですが、ssh認証には鍵が二つあって

  • プライベート鍵
    • 漏らしちゃ駄目!
  • パブリック鍵
    • これはバラまいておk

この、パブリック鍵をおいてあるサーバにプライベート鍵を用いてアクセスしに行くと、認証が行われてsshで接続が出来るという訳です。
プライベート鍵というのは、盗まれたりしないようにキチンと管理しなければならず、またむやみにコピーして複製を作ってはいけません。数が増えれば増える程、管理が行き届かなくなってしまいますし、盗まれる危険性も勿論増えてしまいます。
さて、では以下のような場合にはどうしたら良いでしょうか。

自分自身は現在計算機Aを利用している。そして、計算機Cへと接続したい。
+-------+    +-------+    +-------+
|       |    |       |    |       |
|計算機A| => |計算機B| => |計算機C|
|       |    |       |    |       |
+-------+    +-------+    +-------+
注:計算機Aから計算機Cへは直接sshで接続できない

このとき、「BにCへの接続に用いるプライベート鍵を置けば良い」と考える人が多いと思いますが、先にも申したとおり「プライベート鍵を増やすのは好ましくない」です。ここで登場するのがssh-agentです。

ssh-agentって?

ssh-agentは名前から分かるようにエージェント、代理人として動きます。流れとしては、

  1. 起動して
  2. 鍵を登録すると
  3. プライベート鍵をメモリに格納して
  4. sshクライアントからの問い合わせに答える

といったものです。
これにより、プライベート鍵に掛けたパスワードの入力が省かれたりなど、非常に便利にsshを利用できるようになるのです。そしてこれを用いると、ssh接続の先でのssh - 多段sshでの鍵認証も便利に、安全になります。

ssh-agent forwardingの仕組み。

ここで、ssh-agent forwardingを利用すると考えてみます。
計算機Aから計算機Bへとエージェントを利用して計算機Bに接続している状態を想定します。
計算機Bから計算機Cへ接続しようとすると、認証の要求が計算機B上の代理エージェントに送られます。この計算機B上の代理エージェントは、鍵の認証要求を計算機Aのエージェントにリレーして、その結果を受け取ります。

このように動作することより、計算機B上へ計算機Cへのプライベート鍵を置くことなく、計算機Cへと鍵認証することが可能となります。

実際に使ってみる。

では、実際に使ってみましょう。

  • 起動させる

まず、計算機Aでssh-agentを立ち上げます。以下のコマンドを実行して下さい。

$ eval `ssh-agent`
Agent pid 25366

これにより、ssh-agentが起動します。なお、上記コマンドではシングルクォーテーションでなくバッククォートという点に注意して下さい。バッククォートはPキーの右となり、@キーをShiftと同時押しすることにより入力出来ます(日本語配列の場合)。
さて、ここでevalで実行していることに気になるでしょう。evalで実行することにより、ssh-agentが出力する内容をコマンドとして実行している訳ですが、これはssh-agentを使う際に必要な環境設定を設定するためです。実際に、evalをせず実行すると以下のような結果となります。

$ ssh-agent 
setenv SSH_AUTH_SOCK /tmp/ssh-YkIlsV2Dby/agent.25365;
setenv SSH_AGENT_PID 25366;
echo Agent pid 25366;
  • 鍵を登録する

さて、代理で認証をやってくれるということならば、プライベート鍵とそのパスワードをエージェントに預けなければならないですね。その方法は以下の通りとなります。

$ ssh-add 
Enter passphrase for /Users/m-bird/.ssh/id_rsa: 
Identity added: /Users/m-bird/.ssh/id_rsa (/Users/m-bird/.ssh/id_rsa)

ssh-agentでは、複数の鍵を登録することも可能です。デフォルトの鍵(.ssh以下に置かれた鍵)以外を登録する場合は、引数に鍵までのパスを明示してやります。

$ ssh-add .key/server2.key 
Enter passphrase for .key/server2.key: 
Identity added: .key/server2.key (.key/server2.key)

確認してみましょう。-lオプションにて、鍵のフィンガープリントと共に、現在読み込まれている鍵が表示されます。

$ ssh-add -l
2048 01:01:01:01:01:01:01:01:01:01:01:01:01:01:01:01 /Users/m-bird/.ssh/id_rsa (RSA)
2048 02:02:02:02:02:02:02:02:02:02:02:02:02:02:02:02 .key/server2.key (RSA)

鍵の登録を外すには、-dオプションです。引数無しだと、デフォルトの鍵が登録削除されます。

$ ssh-add -d
Identity removed: /Users/m-bird/.ssh/id_rsa (/Users/m-bird/.ssh/id_rsa.pub)

鍵の登録済めば、いつも通りsshコマンドを実行すると、パスフレーズの要求など無しに接続できるようになります。

  • forwardingする。

さて、ここで本来の目的の「鍵を置かずの多段踏み」です。
これをやるには、ssh-agentを起動しているクライアントマシン、上の例で言う計算機Aのsshクライアントで以下のオプションを有効にする必要があります。~.ssh/configを作成して書き込んで下さい。

ForwardAgent yes

また、~.ssh/configを作成しなくとも、以下のようにsshで接続する際に引数で渡すことも可能です。計算機Aから計算機Bへと接続する際に、以下のようにコマンド引数を渡してやって下さい。

[keisankiA]$ ssh -o "ForwardAgent yes" m-bird@keisankiB

これで、計算機Bにプライベート鍵を置かずとも計算機Cへとアクセス出来るようになります。

終了方法

さて。ここで勘の良い人は気づいたかもしれません。「ssh-agentを終了するとき、環境変数も削除しなければいけないんか、面倒。」その通りです。
しかし、ssh-agentには便利に終了する方法があります。それが、-kオプションです。例によって、evalで実行します。

$ eval `ssh-agent`
Agent pid 25366
$ eval `ssh-agent -k`
Agent pid 25366 killed

また、一つssh-agentを使う際には気をつけたいことがあります。それは、ログアウト時にssh-agentが終了しないことです。これは.logoutファイル、又は.profileファイルにて対処しましょう。以下の内容を書き込んで下さい。

  • tcsh等のCシェル系(~/.logout)
if ( "$SSH_AGENT_PID" != "" ) then
	eval `ssh-agent -k`
endif
if ( "$SSH2_AGENT_PID" != "" ) then
	kill $SSH2_AGENT_PID
endif
  • bash等のbourneシェル系(~/.profile)
trap '
	test -n "$SSH_AGENT_PID" && eval `ssh-agent -k`;
	test -n "$SSH2_AGENT_PID" && kill $SSH2_AGENT_PID
' 0

これで、ssh-agentが大量に走っている!という問題からは解放されました!
zshは、、、多分どっちでも行けるんじゃないんですかね?分かりません!><

もう一つの実行方法、サブシェルを使用する方法

以上evalを使った方法をぐだぐだと書き連ねてきましたが、もっと単純な方法があります。それは、サブシェルを実行する方法です。

$ ssh-agent $SHELL

と打つだけの簡単なお仕事で、こうすることにより環境変数などをセットしたサブシェルが立ち上がります*1。こうする事により、ログアウト時などはサブシェルのプロセスも殺され、ssh-agentのプロセスも死にますので、上の様な面倒な作業は必要がなくなります。
ここで気をつけたいのは、「この方法を採るならば、真っ先に"ssh-agent $SHELL"というコマンドを実行しろ」ということです。そうでないと、サブシェルを立ち上げる前にバックグラウンドに移したプロセスのセッションに触れなくなったりしてしまいます。

$ vim tani			# vim起動
^Z				# vimを一時停止
$ ssh-agent $SHELL		# サブシェルを実行!
$ jobs				# プロセス確認。vimが見えない!
$ exit				# サブシェル終了
$ jobs				# もっかいプロセス確認
[1]  + Suspended vim tani

という訳で、.loginだとか.profileに書いておくべきでしょう。

ssh-agent forwardingの危険性

しかし、ここで気になってくるのが踏み台サーバの信頼性です。
上の例で、計算機Bについてroot権限があれば、計算機Aのssh-agentを利用できてしまいます。それは、計算機Aに置いた秘密鍵パスフレーズ無しで利用されてしまうのと同義です。
ssh-agentに手を加えてログ出力させ、自分のssh-agentが勝手に使われていないか監視する方法もあるようですが、しかしroot権限を疑い出したらキリがないので、

  • 信用ならない計算機を踏まない
  • 意味もなく踏み台にするマシンを増やさない
  • 秘密鍵を使いまわさない

といった対策も必要でしょう。
この件については以下のページが非常に詳しいので、詳細を知りたい人は併せてどうぞ。

デバッグオプションでリブートやるぞ!

さて、sshが使えない場合にそれでもリブートがしたいだとか、そもそも何らかの原因でreboot/shutdownコマンドが実行されない場合などがあるでしょう。
そんな時はどうすればいいでしょうか。簡単です。デバッガにたたき落として、そこでrebootコマンドを叩けばいいんです。デバッガに落ちてもシリアルコンソールなら操作出来ます。うれしい。
というわけで、以下のデバッグオプションをカーネルコンフィグファイルに書き加えてやりましょう。

options DDB
options KDB
options GDB
options ALT_BREAK_TO_DEBUGGER # <= エスケープキーの設定。man 4 ofw_console
options KDB_UNATTENDED

以上のオプションで構築したカーネルの場合、[CR][~][^b]でbreakがかかりddbプロンプトへと落ちます。panicしても、ddbに落ちます。という訳で、ddbのプロンプトでrebootしてやれば、とりあえずは帰ってきます。やったね☆
なお、「カーネルコンフィグとかわかんねーよ!」って方は、以下のハンドブックを参考にしてください。

最後に感想など

今回のスピーカーは東京理科大の佐藤広生氏でわかりやすく、非常に楽しんで聞くことができました。
しかし、今回は懇親会が無いということで、勉強会終了後すぐに皆さん帰られてしまって、他の参加者さんとの交流が出来なくて残念でした(当日に慌てて名刺作って行ったのだけれど......(泣))次回は是非、交流したい!ということで、またこれからの勉強会の抽選にも応募していきたいと思います。もし、会場でお会いする事になった時には、宜しくお願いしますね!

*1:シェルの上でシェルが走っている状態。コマンドラインbashやらtcshと打つと、bashの上でbashが走ったり、tcshの上でtcshが走ったり、bashの上でtcshが走ったり。。。等となる