rcmdnk's blog

Pro OpenSSH

OpenSSHの多重接続機能を使うと 複数の接続を1つにまとめる事が出来ます。

特に、1つ接続がある場合、次の接続は既にある接続に乗るだけなので 認証も飛ばしていきなりつなぐことが出来るので 接続を大分高速化することが出来ます。

Sponsored Links

必要なもの、できること

この高速化はOpenSSHの機能を使うだけなので、 sshを使ってる環境であれば特にインストールしたりするものはありません。

~/.ssh/configに数行設定を加えてあげれば出来ます。

結構昔からあるものなので最近のものであれば大概使えるはずです 1

この設定を行うことで、一度リモートホストへ接続すると、 その後の接続は最初の接続を再利用するので パスワード認証等をする必要がなくなります。 ssh鍵を使ってる場合でも認証を行わないで接続するので その分速く接続します。

接続時の確認作業を飛ばせる所が高速化出来る事なので、 接続中の作業が速く出来る様になるわけではありません。

それでも何度も接続することがあるようであれば大分快適に使える様になります。

多重接続設定

この機能を使うためには、~/.ssh/configの一番下に、

~/.ssh/config
1
2
3
4
Host *
  ControlMaster auto
  ControlPath ~/.ssh/master-%r@%h:%p.socket
  ControlPersist 10m

と書き加えます。

  • ControlMaster: autoにするとControlPathを指定することでセッションの共有を行う様になる。(初期値: no)
  • ControlPath: 制御用ソケットのパス名。noneだと共有を行わない。%r: リモートのユーザー名、%h: ホスト名、%p:ポート番号などの変数が使えるので これらを使って指定することで各ホスト毎等にユニークなパス名を簡単に付けられる。認証に関する物なので自分の隠しフォルダ(~/.ssh等)におくべき。(/tmp/とかには絶対置かない様に。)(初期値: none)
  • ControlPersist: noだと最初の接続(Master接続)を切ると全ての接続が切れる。yesとすると最初の接続を切ってもバックグラウンドで接続を続ける。この場合は手で消さない限り接続は生き続ける。次につながったものがMaster接続になる。数値を入れるとその時間だけ全ての接続が無くても保ち続ける様になる(単位無しは秒、10分なら10m、1時間なら1h等)。(初期値: no)

ControlMasterの説明がちょっと雑ですが、だいたいこんな感じです。 (詳しくはマニュアル参照2。)

$ ssh remote.example.com
~/.ssh/config: line 30: Bad configuration option: ControlPersist
~/.ssh/config: terminating, 1 bad configuration options
$

みたいなエラーが出たらOpenSSHが古くてControlPersist のオプションが無いので、OpenSSHをアップデートするか、 Persistな接続を諦めるかです(最初のMasterが切れたら全部切れる様になる)。

Host *とすることで全ての接続に対して有効化してますが、 この設定を使いたくない場合には、これより上で

~/.ssh/config
1
2
Host NotUseMultiConnection
  ControlPath none

な感じで無効にしておけばOK。 ~/.ssh/configでは、上から最初に見つかったものを使っていくので Host *の設定は一番下に書いておいて、 他の設定を使いたいものはそれより上で上書きします。

また、上の設定を指定していなくても

$ ssh -M -S [email protected] remote.example.com

そとして最初のMaster接続を作り、

$ ssh -S [email protected] remote.example.com

として後からその接続を使って接続を行うことも出来ます。

もしくは-oオプションを使って直接

$ ssh -o "ControlMaster=auto" -o "ControlPath ~/.ssh/master-%r@%h:%p.socket" -o "ControlPersist 10m" remote.example.com

としても勿論使えます3

この設定をするデメリットとしては X11やssh-agentがMasterのもの(最初に接続したもの、もしくは全て接続が無くなった後に最初につながったもの) になるので複数は使えなくなります。

通常使ってる限りは問題無いとは思いますが、 例えばポートフォワードだけに使う接続を最初にX無しでしてしまうと 後からの接続もXが飛ばせなかったりします 4

この様な場合には、この接続だけ

$ ssh -S none -2 -N -f -L 10022:destination.example.com:22 hook.example.com -lUSER

としてマルチ接続を無効にしたりすれば他に影響を与えずに使えます。

また、sshfsを使って リモートホストのファイルシステムをマウントしてるような場合にこの問題が良く起こるようです。

sshfs doesn’t forward X + conflict with “ControlMaster auto”

このポストではDynDNS というサービスを使ってホスト名に別名をつけて、的なことをやってますが、 このDynDNSは現在はフリーのバージョンは無いみたいです。

ただ、こんなことしなくても~/.ssh/configで適当な名前のHost設定を 別々に作って使えば良いだけだと思います。

実際sshfs使ってないのであれですが、 少なくとも通常の接続に関しては上のポートフォワードの件なんかもそれでいけます。

~/.ssh/config
1
2
3
4
5
6
7
8
9
Host normal
  HostName remote.example.com
Host portForward
  HostName remote.example.com
  ControlPath none
Host *
  ControlMaster auto
  ControlPath ~/.ssh/master-%r@%h:%p.socket
  ControlPersist 10m

的な感じで。

確認方法、削除方法

現在多重接続用のMaster接続が動いているかどうかは

$ ssh -O check remote.example.com
Master running (pid=12345)

とする事で確認することが出来ます。

$ ssh -O exit remote.example.com
Exit request sent.
$

とすると接続を終了します(全ての接続が切れます)。 ControlPersist yesとしてる場合にはこれで終了しない限り(もしくはプロセスをkillしない限り)は接続が継続します。

また、

$ lsof -U | grep master
ssh       12345 USER    3u  unix 0x0000000000000000      0t0      [email protected]:22.socket.XXXXXXXXXXXXXXXX

みたいな確認もできます5。 (この場合は勿論ControlPathmaster-と指定しているから。単にこのパス名で拾ってるだけなので違うパス名を指定してる場合にはそれでgrep。)

ssh -O checkで現在のMasterのPIDが得られますが、 ControlPersistnoの場合、 このPIDがは最初に接続したsshのプロセスになります。

ssh後、他のターミナルからローカルのプロセスを見てみると

$ ps -u$USER |grep ssh
  501 12345 ttys001    0:00.01 ssh remote.example.com

となっていてこのプロセスのPID(12345)はssh -O check remote.example.comで得られる 物と一緒です。

一方、ControlPersistyesや時間を指定している場合は

$ ps -u$USER |grep ssh
  501 12345 ??         0:00.00 ssh: [email protected]:22.socket [mux]
  501 12300 ttys001    0:00.02 ssh remote.example.com

と言った感じに、ssh: ...というプロセスが動いていて、 これがcheckで出てくるPIDになっています。

このプロセスが全ての接続が切れた後にも 接続をバックグラウンドで維持してくれています。

また、接続した先でプロセスを確認すると

$ ps -u$USER |grep ssh
  7721 ?        00:00:00 sshd

みたいな感じで1つsshdが確認できますが、 通常、さらに他から接続するとこのsshdプロセスが増えていく所、 多重接続を行うといくつつないでもsshdプロセスが一つだけであることが分かります。

また、ControlPersistnoでない場合には、全ての接続を切っても sshdプロセスは生きています。 (上でいうssh:...との繋がりがあるので。)

さらに、ネットワークが切れるとsshの接続は通常切れますが、 接続が切れてもssh: ...のプロセスは生き続けるので ネットワークに繋がった後に再びパスワードが必要な所へログインしようとすると 以前の認証情報を使ってパスワード無しで入れます。

この間、接続先のsshdは生き続けています。

この辺はsshやルーターの設定にもよるかもしれませんが、 ブチブチ切れる様な環境だとかなり楽になります。

1つちょっとした問題として、一度接続を作ってネットワークが切れた状態の時に 手元でssh -O exit remote.example.comみたいにして接続を消してしまうと、 接続先側のsshdプロセスは知らないまま生き続けてしまいます。

それ程気にするところでは無いかもしれませんが、頻繁に起こると 大量のsshdプロセスが生き続けてしまうので、 システムが適時処理してくれれば良いですが、そうでない場合は ちょっと気にしてkillしたりする必要がありかもしれません。 (そもそも非接続時に自分でexitするような事が無ければ多分問題ありませんが。)

まとめ

ControlMasterとかは結構古くから実装されてるもので、 ControlPersistの機能も数年前には大体の所で使える様になってるものです。

OpenSSHそのものの機能なので余計な物を入れずともそのまま使えます。

接続時だけの話ですが、複数繋げる事が多かったり、繰り返しログインすることが 多い場合は非常に時間の短縮になって一度設定したら無くてはならない物になります。

一方で、サーバーにログインして作業する時には、 ログイン先でGNU screentmux を立ち上げて作業を行ってその中でウィンドウを増やすので、 複数接続することはない、と言った場合にはそれ程、と言った感じかもしれませんが、 それでも接続先から更に他へログインすることがあったりだとか、 別のターミナルウィンドウを使って複数使うこともある、ということなんかがある場合には 便利です。

それから上に書いたように一度切れてしまってもう一度接続してscreenを復帰して、 みたいなことをする際にも素早く再接続出来ます。

多段ssh設定 なんかの場合でも、ポートフォワードしたところへの接続も この多重接続が行えます。

Mosh の場合はUDP接続で回してちょっと違う事をするので この設定は効きません。

また、WindowsでのCygwinでは(MobaXtermでも)では残念ながら 問題があって使えないようです 6

ということで、ポートフォワードを常にしていたり sshfsなんかを使ってる場合には少し注意が必要ですが、 設定しておくと思った以上に快適になると思うので、 取り敢えず設定して使ってみるべきだと思います。

Sponsored Links
Sponsored Links

« homebrew-fileのwrapper関数を'brew init'等一部コマンドを直接使える様にアップデート Bashの補完について »