web-dev-qa-db-ja.com

接続が閉じた後にSSHリバーストンネルソケットをクリーンアップする方法は?

このようなものを実行すると:

ssh -4 -f -N -T -R "/home/dude/lol.socket:192.168.4.44:4444" dude@someserver -p 22 -i privatekey -o "ExitOnForwardFailure yes" -o ConnectTimeout=5 -o ConnectionAttempts=3 -o ServerAliveInterval=15 -o

そして、何らかの理由で接続が閉じられたか、または停止したとしましょう。メンテナンスやエラー、またはインターネット接続の問題などが原因でコンピュータが再起動したとしましょう->私たちは大きな問題を抱えています。 someserverに作成されたソケットファイル/home/dude/lol.socketは、sshdによって削除されません。したがって、リバーストンネルイニシエーターが回復していて、トンネルを再作成しようとしているため、次の理由で失敗します。

Error: remote port forwarding failed for listen path /home/dude/lol.socket

サーバー側では次のようなものが得られます:

error: bind: Address already in use
error: unix_listener: cannot bind to path: /home/dude/lol.socket

切断後にソケットをクリーンアップするためにサポートされている方法/最善のハックは何ですか?これはsshdのバグですか?切断が検出された場合に、自動的にそれが行われるのではないですか?

バックストーリー:

ソケットの使用の背後にある考え方は、サーバーがn個の "dudes"を処理し、どのポートでもm "lol"サービスのリバーストンネルを作成することであり、ソケットを使用すると、 "dude"がアクセスとバインドのみを行えるようにするのがはるかに簡単になります。彼自身のソケットに、しかし他の男のソケットには。また、どのサービスを公開するためにどのポートを使用しているのかを記録しておく必要もなくなります。そして、男が他のサーバー上のサービスに接続したいとき、彼が知る必要があるのはサービスの名前であり、それをランダムなローカルポート(または望めばソケット)にバインドすることです。

ssh -v -i -4 -N -T -L "127.0.0.1:3334:/home/dude/lol.sock" -p 22 dude @ someserver -o "ExitOnForwardFailure yes" -o ConnectTimeout = 5 -o ConnectionAttempts = 3 -o ServerAliveInterval = 15 -o ServerAliveCountMax = 3

サーバーのリバーストンネルが実行されていると想定されている魔法のポート番号を知っている必要はありません。だから、もしあなたがこの問題を解決する方法についてより良いアイデアを持っているなら、私は皆耳を傾けています。

Openssh-client/server version Debian 9を使用して、クライアント/サーバーの両方で7.4p1-10+deb9u2(クライアントは実際にはDockerコンテナー内のMac上にある)を実行してテスト済み

6
Timo

TL; DR;

解決策は、ssh構成でStreamLocalBindUnlinkの値をyesに設定することです:Sudo echo "StreamLocalBindUnlink yes" >> /etc/ssh/sshd_config

長い話

これが発生する理由は、ソケットが閉じられたときにUNIXソケットファイルが自動的に削除されないためです。ファイルパスを指定してremove/unlinkを呼び出すことでこれを行う場合は、閉じるときに手動でクリーンアップする必要がありますが、opensshはこれを行いません。しかし、私は主題をさらに調査したとき、UNIXソケットの「ベストプラクティス」はunlink正しいbeforeにバインドすることであることに気付きました(- これをチェックSO詳細については回答 )そして、これはまさにStreamLocalBindUnlink yesはsshdに指示します。

マンページは言う:

 StreamLocalBindUnlink
         Specifies whether to remove an existing Unix-domain socket file for local or remote port forwarding before creating a new
         one.  If the socket file already exists and StreamLocalBindUnlink is not enabled, sshd will be unable to forward the port to
         the Unix-domain socket file.  This option is only used for port forwarding to a Unix-domain socket file.

         The argument must be yes or no.  The default is no.

このアプローチの欠点は、古い接続がまだ残っている場合でも、ソケットへの再バインドが許可されることです。これを行うと、古いトンネルがそこにぶら下がったままになり、通過する既存のTCP接続はそのまま残りますが、新しい接続はすべて新しいトンネルに行きます。また、古いトンネルは永続的かつ不可逆的にファイルシステムのソケットアドレスから切り離されているようで、新しいトンネルが閉じられていても、新しい接続を受信できなくなります。

参考文献

11
Timo