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


26

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

ファイアウォールの背後にあるサーバー(A)を使用して、いくつかのポートで小さなDigitalOcean VPS(B)への逆トンネルを開始し、BのIPアドレスを介してAに接続できるようにします。トンネルは約3か月間一貫して機能していますが、過去24時間で突然4回故障しました。同じことが別のVPSプロバイダーでしばらく発生しました-数か月の完全な運用、その後突然の急速な障害。

マシンAに(ssh -R *:X:localhost:X address_of_B各ポートXに対して)トンネルコマンドを自動的に実行するスクリプトがありますが、実行すると、というメッセージが表示され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がポートのリッスンを拒否する理由を理解する必要があります。それが主な問題のようです。期待通りの動作を数か月続けた後(つまり、すぐにポートを閉じてスクリプトを再接続できるようにした後)、このように動作する原因が何なのかわかりません。


あなたの質問は何ですか?ポートバインディングエラーに対処する方法、またはsshが死んでいる理由を見つける方法、または他の何か?
MadHatterは

sshdがVPSのポートを開くことを拒否している理由を理解する必要があります(バインドエラー)。ポートバインディングエラーが問題の原因であると思われ、それを解決できればすべてが機能するはずです。
ジャスティンMrkva

2
接続を開いたままにするためのスクリプトを手動で作成する代わりに、後のlurkerの場合は、代わりにautosshを使用するだけで済みます。serverfault.com/questions/598210/...
oligofren

回答:


27

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

このような無効な接続が発生する可能性のある方法は3つあります。

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

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

orのClientAliveIntervalキーワードsshd_configServerAliveInterval間隔を調べる必要があります。ssh_config~/.ssh/config

sshループ内でコマンドを実行すると問題なく動作します。何らかの理由で接続が失敗したときにサーバーをフラッディングさせないように、ループにスリープを挿入することもお勧めします。

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


これが問題かもしれないと思っています。特に、A上の私のスクリプトは、sshプロセスが停止した場合にBへの再接続を試みます(もちろん、警告メッセージはsshプロセスを強制終了しないため、これが発生するとハングしますが、それは別の日に問題になります)。ただし、AがBへの再接続を速すぎると、BはAの再接続を待機している可能性があります。Aが再接続する前に、Bが常にタイムアウトすることを確認する必要があります。再接続する前にsshdプロセスを強制終了するというMadHatterの提案とこれを組み合わせることで、可能性のあるケースの95%をカバーできるでしょう。
ジャスティンMrkva

1
そして、SSHを殺さないという警告メッセージについて言えば、それは私に考えさせられました...そしてマンページを見ました。結局のところ-o ExitOnForwardFailure yes、私がまさに必要です。だから、それは私が理解する必要がある1つ少ないものです。考えてみると、これらの警告メッセージを解析するPythonスクリプトを書くつもりでした。これははるかに簡単です。:D
ジャスティン・ムルクヴァ

ExitOnForwardFailure答えを書くときに忘れてすみません。今すぐ答えに追加しました。
カスペルド

4
問題ありません-o ExitOnForwardFailure=yes。実際はそうでした(等号に注意してください)。したがって、誰かがこれに遭遇した場合、以前のコメントからコピーして貼り付けないでください、それは動作しません。:P
ジャスティンMrkva

そのため、私は約10時間サーバーを監視してきましたが、正常に動作しているようです。この時点で、この答えは正しい(私が見たものに基づいて約99%確信している)と、一連の急速な切断は、数か月後に発生したネットワークの問題に関連する偶然の一致であったと仮定しています各サービスを開始します。皆さん、助けてくれてありがとう。;)
ジャスティン・ムルクヴァ

4

そのサーバーのポートをバインドしているプロセスを見つけることができます

sudo netstat -apn|grep -w X

半分は無効になっている可能性が非常に高いようsshdですが、データを取得できるのに、なぜ仮定をするのですか?また、スクリプトがトンネルを再び起動する前に信号9を送信するPIDを見つける良い方法です。


以前のVPSプロバイダーでそれを確認したことを思い出し、sshdがそれらのポートをリッスンしているプロセスであることを確認しました。次回はここでチェックしますが、動作とセットアップがまったく同じなので、何か違うとは思わないでしょう。
ジャスティンMrkva

素晴らしいので、トンネルを再開するスクリプトで古いトンネルを強制終了してから、そうしようとします。
MadHatterは、2014

あなたが言っているのであれば、一度に複数のトンネルスクリプト(A上)が実行されることはありません。一方、スクリプトでBのコマンドをリモートで実行して、浮遊プロセスを強制終了することを意味する場合、それは実際には半分悪い考えではありません。しかし、1つの懸念は、デバッグしようとしている場合にすべてのSSH接続を繰り返し切断することです。Aのスクリプトがグリッチのために常にBを殺している場合、不正なAスクリプトによってBから絶えず追い出されることはできません。:Pそれがそれをしないことを確認するためにテストする必要があります。しかし、私が言ったように、半分悪い考えではありません。;)
ジャスティン・ムルクヴァ

あるとは思っていませんでした。バインドエラーのためにトンネルを起動しようとして失敗するスクリプトがリモートサーバーで実行されていると言いますが、必要なとき(つまり、既存のトンネルが良くないとき)にのみ実行されると仮定していますあなたが別の言い方をしていないからです。私が提案しているのは、新しいトンネルを起動しようとする前に、ポートを開いたままにしている特定のプロセスを強制終了することです。
MadHatterは、

sshを実行するスクリプトはサーバーAのみにあり、サーバーBは余分なスクリプトのない単純なバニラサーバーです。おそらく、サーバーBに配置するキルスクリプトを記述し、連続して特定の回数接続に失敗した場合、Aからリモートで呼び出すことになるでしょう。そうすれば、他のSSH接続に干渉する可能性が低くなります。また、実行されるたびにkillスクリプトのログを取得し、あまりにも頻繁に呼び出されると何もせずに終了する可能性があります。個人的には、sshdを殺すスクリプトをレート制限するのが賢明なようです。:P
ジャスティン・ムルクヴァ

3

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

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

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

より堅牢な方法は、autossh
Marco Lavagnino

-o ExitOnForwardFailure=yes私が探していたものでした、ありがとう!
vadipp

1

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

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

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


Aマシンで実行される完全なSSHコマンドはssh -o ConnectTimeout=10 -o BatchMode=yes -gnN -R *:X:localhost:X root@$TUNSRV 1>>tunnel.log 2>&1 &、特に-Nオプションのために、トンネル自体を除いてSSHによって実行されるものはありません。開いたままになっているものはすべて、sshd自体を使用してリモートサーバーBで実行されています。
ジャスティンMrkva
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.