Software-Defined どさにっき 〜2015年6月下旬〜

by やまや
<< = >>

2015年6月24日(水)

ssh ポートフォワード

_ sh スクリプトの中から、ポート転送しないとたどりつけないホストにアクセスしたい。ただし、常時ポートフォワードしてるわけではないので、スクリプトの中でポート転送を開始し、終了もスクリプトの中で面倒見る必要がある。

_ 教科書的なやりかたはこう。

ssh -L ... -f -N hostname

_ ssh をバックグラウンドで起動し(-f)、リモートホストでは何もコマンドを実行しない(-N)。が、バックグラウンドにいってしまったプロセスを止めるために ps | grep でいくつも動いてる(かもしれない) ssh のコマンドの中からスクリプトで起動したものを特定して kill するという処理が必要になる。めんどくさい。pid をどっかのファイルに吐き出してくれる、とかいうオプションがあればいいんだけど、そういうものはなさげ。

_ じゃあどうしよう。

ssh -L ... -f hostname sleep 60

_ -N を指定せず、リモートで実行するコマンドをあえて指定してみる。60秒寝てるだけ。60秒過ぎて sleep が終了すると ssh も終了する。よって、スクリプトの側では起動だけで終了の面倒を見なくてもいい。ただし、トンネルを利用する後続の処理が60秒で完了しなかったとしても、強制的に切断されてしまう。

_ 別の案。

ssh -L ... -N -n hostname &
trap "kill $!" EXIT

_ あえて -f を使わず、シェルの機能の & を使ってバックグラウンドにまわしてみる。$! で直前に起動したバックグラウンドプロセスの pid を取得できるので、スクリプト終了時にそいつを kill するよう trap で仕込んでやる。いいアイデアじゃね、と試してみたけどうまくいかないことがある。どうやら & で裏にまわすとトンネル掘りが完了してなくても処理が sh の側に戻ってきちゃうため、すぐに次の処理に移るとまだトンネルを使える状態になってなくてコケてしまうようだ。なので、実際に使うには3秒ぐらい sleep しないとダメ(場合によってはそれでもダメだが)。

_ 結論。

ctlpath=sshctl.$$
ssh -L ... -N -f -M -S $ctlpath hostname
trap "ssh -S $ctlpath -O exit hostname" EXIT

_ わりと最近の OpenSSH なら ControlMaster という機能が使える(最近といっても、これが使えない環境はそろそろ絶滅してるころだと思う)。あるホストに対して多数の ssh 接続するのではなく、1本の接続で複数のセッションを束ねて使うというのが基本的な使いかただけど、そのセッション管理のために制御用のソケットを開けてくれる。ちうことで、1セッションしか必要ないけどこの機能を利用して(-M)、スクリプト終了時にバックグラウンドプロセスに切断を通知してやる(-O exit)。ControlPath (-S) は通常 %h とか使ってホスト名とか埋めこむけど、スクリプトから実行する場合はそういうことはせず、実行のたびに毎回変わるようにする。上の方法と違って、この方法はとくにデメリットはないかな。一見して何をやってるのかひじょーにわかりづらい、というのはあるけど。

_ なお、長くなるので上の例では省略してるけど、実際に使うときにはさらに -o ExitOnForwardFailure=yes (ポート転送に失敗したらただちに終了)というのもつけた方がいいかも。


<< = >>
やまや