CRL を作り直した直後に接続拒否が起きたとクライアントが言うので、CRL が犯人に見える。そうではない。拒否はトランスポート層の応答であり、証明書の検証はその後のトンネルのハンドシェイク内で行われる。タイミングは状況証拠であり、証明ではない。

二つの確認で、一分未満のうちに両者を切り分けられる。どちらも信頼設定には触れない。

エラーを発生させた層で読む

connect からの ECONNREFUSED は、トランスポートの試行が拒否されたことを意味する。何かが reset で応答したか、スタックが到達不能なポートを報告した。証明書の問題ではこのエラーは出ない。トランスポート接続がなければ TLS ハンドシェイクは始まらないからだ。拒否が見えたなら、クライアントは証明書を提示する段階にすら達していない。この一点だけで、失効が原因ではないと分かる。

逆も成り立つ。失効した証明書は TCP 接続を完了させたうえでハンドシェイクに失敗し、サーバ側には検証エラーとして記録される。

TCP の拒否:誰も待ち受けていない

TCP の場合は、意図したインスタンスが動作し、プロファイルのアドレスとポートにバインドされているかを確認する。ss -lntp はリスナーを表示するので、無いことが通常の説明になる。接続を落とすのではなく拒否するファイアウォールもクライアント側に同じメッセージを出すため、両方を確認する。

アドレスファミリーも確認する。127.0.0.1 や IPv6 だけにバインドされたサービスは、もう一方に解決するクライアントを拒否する。

次に、推測せずにサービスのログを読む。CA メンテナンス後の再起動は、読めないファイルや欠けたファイルで失敗し、待ち受け状態に達しないことがあり、その場合はログにファイル名が出る。これは起動の失敗であり、証明書の拒否とは別の不具合だ。

UDP には拒否できるハンドシェイクがない

UDP 上の OpenVPN は TCP 的な意味での接続を確立しないため、connect の時点で拒否するものはない。Linux では、UDP ソケットが示すエラーは、以前のデータグラムが生成した ICMP port-unreachable に由来することがある。udp(7) の man ページがその非同期な挙動を説明している。

そのため UDP クライアントは、サーバプロセスが動作していても想定アドレスにバインドされていないときに拒否を表示しうる。

CRL が実際に確認される場所

CRL は TLS ハンドシェイクの途中、相手が証明書を提示した後に参照され、しかもサーバ設定が有効にしている場合に限られる。OpenVPN の crl-verify オプションがファイルを指し、失敗はサーバログに証明書名を伴う検証エラーとして現れる。

したがって失効したクライアントは、トランスポート層では接続でき、その後の認証で失敗する。同じサーバ、同じポートで、まったく別のメッセージになる。

その段階に達したら、CRL の発行者と有効期限を確認する。誤った CA が署名した CRL、あるいはクライアントの接続前に期限切れになった CRL は、クライアント自身の証明書とは無関係な検証エラーを生む。

次回のための短い手順書

まずトランスポートを確認する。リスナーがあり、アドレスファミリーが正しく、ファイアウォールが拒否していないこと。次に起動ログを読んで、サービスが待ち受け状態に達したことを確認する。そして最後に、検証メッセージを求めてハンドシェイクのエラーを見る。この順序を固定する。各段階が原因の一つの種類を丸ごと除外するからだ。

トランスポート、解決された宛先、ポート、エラーの正確な文言を書き留める。クライアントの再インストールはその証拠を壊し、たいてい何も変えない。

関連記事