目次
handshake failed は、サブスクリプションのダウンロード、プロキシサーバーへの接続、目的のサイトへのアクセスのいずれでも出ます。関係する証明書や設定は異なり、闇雲なノード変更や検証の無効化では元の問題が隠れます。失敗箇所を特定してエラー原文に沿って時刻、信頼チェーン、サーバー名を確認し、最後に同じリクエストを再試行します。
TLS ハンドシェイクの失敗:ログ原文と失敗箇所で調べる対象を決める
TLS エラーの全文を残し、サブスクリプションの取得、ノードへの接続、目的のサイトへのアクセスのどこで発生したか記録します。ハンドシェイクでは暗号化通信の条件を決め、一般的な HTTPS では通常クライアントがサーバーの身元を確認します。下の表で時刻、証明書、ノードの項目を切り分けてください。
代表的な原文
| ログの文言 | 優先して確認する項目 |
|---|---|
| x509: certificate has expired or is not yet valid | システム時刻と証明書の有効期間 |
| x509: certificate signed by unknown authority | システム CA、企業による SSL 検査、プライベート証明書 |
| remote error: tls: handshake failure | SNI、プロトコルのフィールド、サーバー側の TLS 設定 |
| tls: failed to verify certificate: x509 | 証明書チェーン、servername、実際の証明書名 |
| EOF / connection reset by peer | 接続先による早期切断またはネットワークでの遮断。この一文だけで証明書の問題とは断定できない |
エラーの前後数行をコピーし、時刻、対象ドメイン、ポートは残したまま、サブスクリプションの token、UUID、パスワードを伏せます。「TLS failed」だけを切り取ると、原因の判別に必要な部分が失われます。
サブスクリプション、単一ノード、ウェブサイトではエラーの確認対象が異なる
Profile の更新で TLS エラーが出るが、既存のノードは使用できる
サブスクリプションの URL で失敗しています。そのドメインの証明書、システム時刻、ネットワーク検査機器を確認します。
一つのノードだけで handshake failure が発生する
クライアントからプロキシサーバーまでの区間で失敗しています。そのノードの server、port、SNI、TLS フィールドを比較します。
ノードには接続できるが、一つの HTTPS サイトだけで証明書エラーが出る
対象サイトの証明書、ブラウザーまたはアプリの証明書ストア、セキュリティソフトによる復号の有無を確認します。
すべてのノードで同時に x509 エラーが発生し始める
まずシステム時刻、コアのアップグレード、ネットワーク全体で証明書が置き換えられていないかを確認します。
証明書が期限切れ、またはまだ有効ではないと表示されたら、システム時刻を確認する
# Linux
date -R
timedatectl status
# Windows PowerShell
Get-Date
w32tm /query /status証明書には Not Before と Not After があります。デュアルブートの切り替え、仮想マシンのスナップショット、長期間電源を切っていた OpenWrt、自動時刻合わせの無効化によって、時刻がずれることがあります。日付、タイムゾーン、NTP 同期を直した後、Mihomo を再起動してもう一度再現してください。エラーがまったく同じ場合に限り、ハンドシェイクのフィールドを引き続き確認します。
一つのノードだけが失敗する場合は、SNI とサーバー証明書の名前を照合する
TLS ノードの接続先となる server が IP で、証明書はドメイン向けに発行されている場合があります。この場合、通常は正しい servername または sni を設定する必要があります。値はサーバー側の設定に基づく必要があり、インターネット上でよく見かけるドメインを推測で指定してはいけません。ポート、転送方式、ALPN、Reality public-key/short-id などのフィールドも、一式を正しく対応させる必要があります。
proxies:
# 下列 IP、域名和密码都是占位值,不能直接连接
- name: tls-example
type: trojan
server: 203.0.113.10
port: 443
password: REDACTED
sni: edge.example.com
skip-cert-verify: false同じネットワークから、サーバーが実際に返す証明書を確認する
openssl s_client -connect edge.example.com:443 -servername edge.example.com -verify_hostname edge.example.com -verify_return_error -showcerts </dev/null
curl -Iv https://edge.example.com/openssl の出力で subject、issuer、有効期間、Verify return code を確認します。ドメインで接続すると SNI が自動送信される場合があるため、引数を削除しただけでは SNI なしとは判断できません。
SNI なしの結果と比較する前に、使用中の OpenSSL が -noservername に対応しているか確認します。-servername とその値を削除してから -noservername を追加し、接続先、-verify_hostname、-verify_return_error は変えません。二つの SNI オプションは併用できません。
同じ接続先で、スマートフォンのテザリングでは通常の証明書、社内ネットワークでは企業 CA の証明書が返る場合は、検査機器が証明書を置き換えている可能性があります。両方の結果を管理者に伝え、知らないルート証明書を自分で入れないでください。
プロキシプロトコルのポートが通常の HTTPS ページを提供するとは限らないため、curl の失敗だけでノードの故障とは判断できません。openssl が示す証明書とハンドシェイク情報のほうが有用です。Reality などのプロトコルも通常の Web サイト証明書と同じ項目で判断せず、対応するプロトコルのドキュメントを確認してください。
社内ネットワークのHTTPS検査で証明書が差し替えられている?
企業プロキシ、セキュリティゲートウェイ、一部のウイルス対策ソフトは、HTTPS 証明書を再発行します。管理対象端末では、管理者が組織の CA をインストールし、検査を許可する通信の範囲を説明する必要があります。個人の判断でエラーページから見覚えのないルート証明書をダウンロードしたり、Clash の更新を成功させるためにシステム全体の証明書検証を無効にしたりしないでください。
スマートフォンのテザリングへ切り替えると同じサブスクリプション URL がすぐ正常になる場合は、ネットワーク経路の違いを示す証拠になります。社内ネットワークで確認した issuer、エラー時刻、対象ドメインを管理者へ伝えるほうが、ノードを次々に切り替えるより有効です。
修正後、当初失敗した接続を同じ条件でもう一度試す
検証結果
- システム時刻とタイムゾーンが正しく、コアの再起動後は not yet valid/expired が表示されない
- サブスクリプションが失敗していた場合、手動更新が成功し、更新時刻も変化する
- ノードが失敗していた場合、同じノードでハンドシェイクが完了し、実際の接続記録が生成される
- 対象 Web サイトが失敗していた場合、証明書の issuer とドメインが想定どおりである
- 一時的に無効にした証明書検査またはセキュリティ設定を元に戻している
- 管理されたテストに明確な理由がない限り、skip-cert-verify は false のままにする
