Claude DesktopのSSHでfail2banに弾かれる原因と回避策
Claude DesktopはSSHポートにつないで、ログインせずに切るテストをします。この接続をfail2banがmodeしだいで失敗と数える仕組みと、確かめ方、回避策の代償をまとめます。
Claude DesktopのSSHセッションを使っていると、ある日リモートサーバーに接続できなくなり、サーバー側ではfail2banが自分の接続元IPをBANしていた、ということがあります。原因はDesktopが行うSSHポートの疎通テストです。ログインに進む前に接続を閉じるため、fail2banのjailのmodeによっては、攻撃の下見と同じ失敗として数えられます。
対処は2通りです。jailのmodeをnormalに戻すか、ignoreipに接続元を入れるかです。どちらにも代償があります。この記事は、自分の環境で本当にこれが原因かを確かめる手順から、代償まで順に説明します。
Desktopのテストは何をしているのか
Desktopは、マシンが応答するかを調べるために、SSHポートへ接続して何も送らず、すぐ閉じることがあります。たとえば接続に失敗した直後です。1回で終わらず、連続して数回行われます。例として、約2分で8回という数が挙がっています。
ログインの試行は一切ありません。それでも、サーバーのログには「ログイン前に閉じた接続」が並びます。BANや拒否の仕組みが何も動いていなければ、このテストに対して手を打つ必要はありません。困るのは、接続元が弾かれたときだけです。
弾く側には2つの候補があります。
接続元が弾かれる2つの経路
fail2ban
ログの行を失敗として数え、しきい値を超えたアドレスをBANします。jailのmodeしだいで数えるかどうかが変わります。
sshd自身
OpenSSH 9.8以降は、認証を試みずに閉じる接続が続いたアドレスを、サーバー自身が拒否することがあります。
ログの行がテストかどうかを見分ける
まず、サーバーのsshdログに残っている行がDesktopのテストと同じ形かを確かめます。LinuxのOpenSSHサーバーがデフォルトのLogLevelで書く行は、バージョンで変わります。
| OpenSSHのバージョン | テストが残す行 | 挨拶が先に届いた場合 |
|---|---|---|
| 6.6〜7.9 | テストが残す行Did not receive identification string from 203.0.113.7 | 挨拶が先に届いた場合同じ行 |
| 8.0〜8.2 | テストが残す行error: kex_exchange_identification: Connection closed by remote host | 挨拶が先に届いた場合error: kex_exchange_identification: read: Connection reset by peer |
| 8.3〜9.4 | テストが残す行上の行に続けて Connection closed by 203.0.113.7 port 51234 | 挨拶が先に届いた場合read: Connection reset by peer の行に続けて Connection reset by 203.0.113.7 port 51234 |
| 9.5〜10.2 | テストが残す行Connection closed by 203.0.113.7 port 51234 | 挨拶が先に届いた場合8.3〜9.4と同じ2行 |
| 10.3〜10.5 | テストが残す行Connection closed by 203.0.113.7 port 51234 [preauth] | 挨拶が先に届いた場合2行とも末尾に [preauth] |
サーバーのバージョンは、手元のssh -Vでは分かりません。これはクライアントの版を示すからです。サーバー側のパッケージ情報や、接続時のバナーで調べます。行のアドレスが、Desktopを入れたPCの接続元であることも合わせて見てください。ポートを開いて閉じるだけの別のプログラムも、同じ行を残します。
8.0〜8.2の行にはアドレスが入りません。アドレスを出すには、sshd_configにLogLevel VERBOSEを足します。
sudo sshd -T | grep -i loglevelloglevel VERBOSEと表示されたら、SSHサーバーをリロードします。その後は、テストの行の前にConnection from 203.0.113.7 port 51234の行が出ます。
fail2banがテストを数えているかを確かめる
対象はfail2ban 0.10.5以降です。最初にjailのmodeを調べます。jail.conf、jail.local、jail.d/の中を探し、次のどちらかで決めます。
filter行にfilter = sshd[mode=aggressive]のようにmodeが書いてあれば、そのmode- 既定の
filter行([mode=%(mode)s])のままなら、mode行の値。mode行がなければnormal
normalなら、fail2banはテストの行を数えません。BANの原因は別にあります。ここで手順は終わりです。
normal以外のときは、自分のログから該当行をファイルに写して、fail2banの目で判定させます。行は日付から末尾まで丸ごとコピーします。テストが2行を残す場合は、両方を入れます。
fail2ban-regex -o ip lines.log "sshd[mode=aggressive]"aggressiveの部分は、自分のjailのmodeに置き換えます。fail2banが失敗として数えた行があれば、そのアドレス(例: 203.0.113.7)が1行ずつ表示されます。何も出なければ、そのmodeでは1行も数えていません。やはりBANの原因は別にあります。
フィルタの中身で裏づける
fail2banが配布するfilter.d/sshd.confを読むと、modeの違いが見えます。既定はmode = normalで、ddos・extra・aggressive(全部入り)が選べると書かれています。
ddosのパターンには、Did not receive identification string fromと、kex_exchange_identificationのConnection closed by remote host・Connection reset by peerが入っています。表で見たDesktopのテストの行と同じ文字列です。aggressiveはddosを含むので、同じ行を数えます。
normalの側には、接続のクローズ行をBANの対象から外す指定(F-NOFAIL)があります。ここまでを合わせると、BANが起きるのはnormal以外のmodeを選んだjailだと読めます。
Desktop側の記録とサーバーのログを突き合わせる
macOSとLinuxのDesktopは、1本のSSH接続の上で複数のセッションを動かします。サーバーのログに残るのは、セッションごとではなく、Desktopが接続するたびの1件です。ログインの記録は、セッションの数ではなく接続の回数に対応します。数が合わないときは、テストの回数と接続の回数を分けて数えてください。
サーバーの行が本当にDesktopのものかは、時刻で突き合わせると確かめやすくなります。Desktopは自分のSSH接続の記録をssh.logに残します。このファイルは、Help > Troubleshootingから、システムに合う項目(macOSならShow Logs in Finder)を選ぶと出てくるログのzipに入っています。
見るのは、接続に失敗した時刻と、その直後です。テストは「接続に失敗した後」にマシンが応答するかを調べるために行われるので、失敗の時刻のすぐ後ろに、サーバー側のログイン前クローズの行が並んでいれば、Desktopのテストである可能性が高まります。
ログを人に送る前に、中身を見直してください。Desktopは名前の一部を<host>や<user>に置き換えますが、すべては置き換えません。SSHのホスト名やユーザー名が、そのまま残ることがあります。どのホストがいつ失敗したかを添えると、受け取る側が追いやすくなります。
BANされたあとに起きること
接続がBANされると、Desktopからは同じホストに入れません。テストは接続失敗の後にも行われるため、失敗とテストが重なると、BANされたアドレスがさらにログを積む形になります。これは仕組みから考えた推測で、Desktop側に「BANを検知して止める」という記載はありません。
アドレスがBANされていても、サーバーの別の経路(クラウドのコンソールや、別の踏み台)から入れるなら、そこでmodeとignoreipを直せます。入る経路が1つもない場合は、ホスティング側の手順でBANを解く必要があり、これはfail2banやDesktopの範囲を超えます。
回避策は2つ、代償も2つ
テストが数えられていると分かったら、jailの設定を変えます。変更箇所は次のどちらかです。
| 設定 | やること | 代償 |
|---|---|---|
mode | やることnormalにする。mode行か、filter = sshd[mode=aggressive]のかっこの中を直す | 代償他のmodeだけが数える失敗を取り逃がす。たとえば、ログインを試さずに接続して去るクライアント |
ignoreip | やること接続元を入れる。例: ignoreip = 203.0.113.7 | 代償その接続元からは何も数えない。ログイン失敗も含む |
ignoreipは、接続元が固定されているチームに向きます。ただし、その接続元から総当たりの攻撃が来ても止まりません。社内のNATの内側に踏み台がある場合は、特に影響範囲を確かめてください。modeを戻す方法は、接続元が一定しない個人の環境で選びやすい反面、サーバー全体の防御が弱くなります。
設定の例を挙げます。
# /etc/fail2ban/jail.local
[sshd]
enabled = true
filter = sshd[mode=normal]# /etc/fail2ban/jail.local(接続元を除外する場合)
[sshd]
ignoreip = 127.0.0.1/8 ::1 203.0.113.7どちらも書き換えたら、fail2banの設定を再読み込みします。再読み込みの前にBAN済みのアドレスは残るので、解除の手順は別に必要です。
sshdが自分で拒否している場合
OpenSSH 9.8以降のサーバーは、認証を試みずに閉じる接続が続いたアドレスに、ペナルティを課すことがあります。ログには次の形で残ります。
drop connection #0 from [203.0.113.7]:51234 on [192.0.2.10]:22 penalty: connections without attempting authenticationデフォルトの設定では、数回のテストだけでこの行が出ることはありません。複数の人が同じ出口アドレスを共有している環境で、拒否が起きやすくなります。対策はsshd_configのPerSourcePenaltyExemptListです。
PerSourcePenaltyExemptList 203.0.113.7この設定に入れたアドレスには、ログイン失敗に対するペナルティも適用されなくなります。反映前にsudo sshd -T | grep -i persourcepenaltで一覧が出ることを確かめてから、リロードします。
Claude Codeに確認作業を任せる
BANされるとDesktopからは入れません。コンソールや別の経路でサーバーに入れる場合は、そこで動くClaude Codeに、読み取りだけの確認を頼めます。ファイルを書き換えさせず、調べて報告させるのがポイントです。
jail.conf、jail.local、jail.d/ を読んで、sshd jail の mode を特定してください。
次に /var/log/auth.log のうち "Connection closed by" と
"kex_exchange_identification" を含む行を lines.log に抜き出し、
fail2ban-regex -o ip lines.log "sshd[mode=<特定した mode>]" の結果を報告してください。
設定ファイルは変更しないでください。これは、手順を頼むプロンプトの一例です。ログのパスはディストリビューションで異なり、RHEL系では/var/log/secureのことがあります。出力を人が読み、modeとignoreipのどちらを変えるかは自分で決めてください。変更のあとは、同じ手順をもう一度流して、アドレスが表示されなくなったことを確かめます。
Desktopの接続を整えるなら
接続そのものが始まらないときは、原因がfail2banとは限りません。WindowsでFailed to spawn /usr/bin/sshが出る場合はWindows版Desktopのエラー対処に原因があります。複数人で同じ接続を使うなら、sshConfigsでSSH接続を配る方法で接続元の設定をそろえておくと、ignoreipに入れるアドレスの把握が楽になります。worktreeの置き場所はWorktree folderの設定で決められます。ターミナルからのSSH接続全般はClaude CodeのSSH接続ガイドにあります。
切り分けの順番
- サーバーのログに、上の表と同じ形の「ログイン前に閉じた接続」があるか
- その行のアドレスが、Desktopを使うPCの接続元か
- jailのmodeが
normalか。normalならfail2banは原因ではない normal以外なら、fail2ban-regexがそのアドレスを表示するか- 表示されるなら、
modeかignoreipのどちらの代償を受け入れるかを決める - OpenSSH 9.8以降で
drop connectionの行があれば、PerSourcePenaltyExemptList
最後に残る判断は代償の比較です。接続元が固定ならignoreipが影響の読みやすい選択で、固定できないならmodeを戻して、取り逃がす失敗の種類を受け入れることになります。