SSHリモートポート転送に失敗しました

SSHリモートポート転送に失敗しました

フォローアップ:各サーバーを数か月間実行したのと同時期に、接続が急速に切断されたのは偶然の一致で、実際の問題が明らかになっただけのようです。再接続に失敗した理由は、ほぼ間違いなく AliveInterval 値によるものです (kasperd の回答)。ExitOnForwardFailure オプションを使用すると、再接続前にタイムアウトが適切に発生するため、ほとんどの場合、問題は解決するはずです。MadHatter の提案 (kill スクリプト) は、他のすべてが失敗した場合でもトンネルが再接続できるようにする最善の方法でしょう。

ファイアウォールの背後にサーバー (A) があり、このサーバーは複数のポートで小規模な DigitalOcean VPS (B) へのリバース トンネルを開始し、B の IP アドレス経由で A に接続できるようにしています。トンネルは約 3 か月間安定して動作していましたが、過去 24 時間以内に突然 4 回失敗しました。しばらく前に別の VPS プロバイダーでも同じことが起きました。何ヶ月も完璧に動作していたのに、突然、複数の障害が急激に発生しました。

マシン A に、トンネル コマンド (各ポート X に対して) を自動的に実行するスクリプトがありますssh -R *:X:localhost:X address_of_Bが、実行すると、次のように表示されますWarning: remote port forwarding failed for listen port X

/var/log/secureサーバーのsshd に入ると、次のエラーが表示されます。

bind: Address already in use
error: bind: Address already in use
error: channel_setup_fwd_listener: cannot listen to port: X

解決するには、VPS を再起動する必要があります。それまでは、再接続を試みると「リモート ポート転送に失敗しました」というメッセージが表示され、機能しません。トンネルが停止するまでに約 4 時間しか持続しない状態になっています。

VPS には何も変更がなく、リバース トンネルのエンドポイントとしてのみ機能する、単一使用、単一ユーザー マシンです。CentOS 6.5 で OpenSSH_5.3p1 を実行しています。接続が失われたときに sshd がポートを閉じていないようです。何ヶ月もほぼ完璧に動作していたのに、なぜ今突然このようなことが起こるのか、その理由を説明できません。

明確にするために、まず、トンネルが失敗した後に sshd がポートのリッスンを拒否する理由を理解する必要があります。これは、sshd がポートを開いたままにして閉じないことが原因であるようです。これが主な問題のようです。数か月間、期待どおりに動作していた (つまり、すぐにポートを閉じてスクリプトが再接続できるようにする) のに、なぜこのように動作するのかわかりません。

答え1

MadHatter さんの意見に同意します。これは、機能していない SSH 接続からのポート転送である可能性が高いです。現在の問題が別の原因であることが判明したとしても、遅かれ早かれ、このような機能していない SSH 接続に遭遇することが予想されます。

このような接続の切断は、次の 3 つの方法で発生する可能性があります。

  • 2 つのエンドポイントのうちの 1 つが再起動されましたが、接続のもう一方の端は完全にアイドル状態でした。
  • 2 つのエンドポイントのうちの 1 つが接続を閉じましたが、接続が閉じられた時点で、接続が一時的に停止していました。接続が閉じられた後も停止は数分間続いたため、もう一方のエンドポイントは接続が閉じられたことを認識していませんでした。
  • SSH 接続の両端ではまだ接続は完全に機能していますが、その間のどこかにステートフル デバイスが配置されており、アイドル状態のため接続がタイムアウトしています。このステートフル デバイスは NAT またはファイアウォールのいずれかであり、すでに言及したファイアウォールが主な容疑者です。

上記の 3 つのうちどれが起こっているのかを把握することはそれほど重要ではありません。なぜなら、3 つすべてに対処する方法があるからです。それが、キープアライブ メッセージの使用です。

ClientAliveIntervalのキーワードsshd_configと、またはServerAliveIntervalの間隔を調べる必要があります。ssh_config~/.ssh/config

コマンドをループで実行すると、ssh正常に動作します。何らかの理由で接続が失敗したときにサーバーに大量のデータが流れ込まないように、ループ内にスリープを挿入することもお勧めします。

サーバーで接続が終了する前にクライアントが再接続すると、新しい SSH 接続は有効であるもののポート転送がない状況に陥る可能性があります。これを回避するには、ExitOnForwardFailureクライアント側でキーワードを使用する必要があります。

答え2

私の場合、sshトンネルが切断されると、接続がリセットされるまでにしばらく時間がかかるため、sshプロセスはブロックし続け、アクティブなトンネルがなくなるのですが、その理由はわかりません。回避策としては、 をsshバックグラウンドに置き-f、古い接続がリセットされるのを待たずに新しい接続を生成することです。 を-o ExitOnForwardFailure=yes使用して、新しいプロセスの数を制限できます。 これにより、-o ServerAliveInterval=60現在の接続の信頼性が向上します。

sshコマンドは、たとえばcron、スクリプト内のループ内で頻繁に繰り返すことができます。たとえば、次の例では、sshコマンドを 3 分ごとに実行します。

while (1)
do
    ssh -f user@hostname -Rport:host:hostport -N -o ExitOnForwardFailure=yes -o ServerAliveInterval=60
    sleep 180
done

答え3

そのサーバーのポートをバインドしているプロセスは、

sudo netstat -apn|grep -w X

半分機能していない である可能性が非常に高いようですsshdが、データがあるのになぜ仮定するのでしょうか? また、トンネルを再び起動する前に、スクリプトがシグナル 9 を送信する PID を見つけるのにも適した方法です。

答え4

私の経験では、ssh は、リモート システムで「何か」がまだ実行されている場合、正常に終了しないという少し厄介な癖があります。たとえば、バックグラウンドで開始された場合などです。これを再現するには、次の操作を実行します。

ssh <server>
while true; do  sleep 60; done&
exit

ssh はログアウトしますが、リモート プロセスが終了するまで (「while true」ループなので終了しません)、実際にはセッションは閉じられません。同様のことが起きている可能性があります。セッションには、ssh によって生成された「スタック」プロセスがあります。ポートは使用中のままなので、ローカル プロセスで再利用することはできません。

関連情報