autosshでsshトンネリング(ポートフォワード)を維持する

動的IPアドレスのクライアントにsshトンネリングを維持させる方法です。思いのほか苦労したので記録しておきます。ちなみにcygwinのautosshを使用しています。

1. sshd側でClientAliveIntervalを設定する

これを設定しないとネットワーク切断後にもsshdのプロセスが永久に残り続けるという恐ろしい事態が発生します。sshdのプロセスが残っているとトンネルしたいポートが占有され続けて、トンネルを再開できなくなります。

sshdClientAliveIntervalの間隔(秒)でクライアントに対して応答確認を行い、失敗したらsshクライアントを切断します。

この非常に重要なClientAliveIntervalGentooではデフォルト値が設定されているようですが、Ubuntuでは設定されていないようです。

# /etc/ssh/sshd_config
ClientAliveInterval 15
ClientAliveCountMax 3

sshd_configを編集したらsshdを再起動して設定を反映させる必要があります。 ebizou-rion.hatenadiary.org

2. autosshの-Mオプションで通信監視ポートを固定する

autosshの-Mオプションで通信監視ポートを必ず固定する必要があります。このポートには使用されていない適当なポートを指定します。トンネルしたいポートを指定してはダメです。

この設定により、ネットワーク切断後、上記1.の設定によりsshdのプロセスが終了されて通信監視ポートが解放されるまでautosshは接続をリトライし続けます。つまり、autossh接続は排他制御されて、同時に存在するautossh接続は1つだけになります。

この設定をしないと、sshdのプロセスがサーバーにまだ残っている段階でautosshがssh接続を再開してしまいます。そうするとssh接続は再開するものの、まだ残ってる古いsshdプロセスがトンネルしたいポートを占有しているので、トンネリングを再開させることができません。

なお、ちょっと設定は不明ですが、素のOpenSSHクライアントでもトンネリングに失敗した時ssh接続を切断するというに設定にもできるようなので、それで排他制御することも可能かもしれません。ここでは-Mオプションで排他制御しました。

ちなみに-Mオプションを使用しないと空いているポートが通信監視に使用されるためautossh接続の排他制御になりません。-M 0にすると通信が監視されません。これらの設定にしてはいけません。

3. その他必須ではないが必要かもしれない設定

3.1. autosshのポーリング間隔を設定する

autosshのポーリング間隔はデフォルトで600秒です。これは長すぎる気がするので短くしました。オプションではなく環境変数で設定します。

AUTOSSH_POLL=15

3.2. ExitOnForwardFailureは設定しない

ExitOnForwardFailureYesになっていると、ポートフォワードが失敗した時にautosshも終了してしまいます。ExitOnForwardFailureが設定されてないか~/.ssh/configの記述を確認しましょう。

3.3. autosshにハートビートさせる

上記1.で設定したClientAliveIntervalsshd側のハートビート設定で、クライアント側のハートビート設定がServerAliveInterval になります。タイムアウトさせないためにはどちらか片方だけ設定すれば十分なので、別に設定しなくていいですが、両方設定しても問題はないです。クライアントにもハートビートさせたい場合はどうぞ。

-o ServerAliveInterval=15 -o ServerAliveCountMax=3

ssh 接続をタイムアウトしないようにする · GitHub

autosshのコマンドライン

以上まとめると最終的なコマンドは以下のようになります。

AUTOSSH_POLL=15 autossh -vvv -M 54321 -i 証明書のパス -L 12345:127.0.0.1:12345 -R 8080:127.0.0.1:8080 ホスト名

-vvvは冗長なログを表示するオプションです。 localhostではなく127.0.0.1を使用しているのは私のcygwin環境だとlocalhostが解決できないためです。

所感

以上のとおりautosshのオプションとsshの設定が調和して初めてトンネルを維持できるようになっており、非常にややこしいです。