社内 Wi-Fi では動き、スマートフォンのテザリングでは失敗する。どちらも VPN に接続している。Git は 403 を返し、エラーは三行にわたる。テザリングを原因とみなし、何かが動くまでネットワーク設定を変えたくなるが、HTTP 403 はサーバーがリクエストを理解して拒否したという意味なので、有用な問いはどのサーバーかであり、有用な次の一手は一度に一つだけ変える比較である。

ネットワークより先にステータスを読む

403 は名前解決の失敗でも、TCP のタイムアウトでも、完了しなかった TLS ハンドシェイクでもない。それは、あなたのリクエストを解析した何かが下した判断だ。

その判断を下したものが何かを知るまでは、試すネットワーク変更はどれも、もっともらしい筋書きを伴った推測にすぎない。

403 で応答できるのは誰か

リポジトリのアプリケーションは候補の一つにすぎない。プロキシ、ゲートウェイ、フィルタリングサービスがその前に位置して先に応答することがあり、どちらの場合もクライアントが見るステータスは同じだ。ステータス、関連するヘッダー、秘匿情報を除いたエラー本文を保存し、認可されたサービスのログと突き合わせる。リモート URL と実際に到達したエンドポイントも DNS やプロキシ設定によって異なりうるので、設定されたアドレスとリクエストを行っているプロセスを確認する。

拒否がリポジトリに届いていなければ、そのログは沈黙し、その沈黙が証拠になる。

一度に一つの変数だけを比較する

テザリングとの比較が役立つのは問題を絞り込むからであり、原因を証明するからではない。二つのネットワークは、アドレス指定、IP ファミリー、プロキシ、TLS ネゴシエーション、外向きポリシーにおいて同時に異なりうる。

試行ごとに一つだけ変え、残りを記録する。下の表は、これを進めながら私がメモに残す最小限だ。

Keep constantRecord when it changes
Repository, operation and credentialsResolved address and IP family
Git process and configurationProxy use and the responding component
Intended VPN profileActual route and exit seen by the service

動く実行と失敗する実行の間で異なる一行こそ、次の一時間を注ぐ場所である。

回避策は証拠であり、説明ではない

別のアドレスファミリーを強制すると宛先、経路、または選択される中間装置を変えうるため、IPv6 がリポジトリと互換でないと証明せずに結果を変えうる。ネゴシエートされた TLS のバージョンが違うことも、同じ範疇の観察だ。どちらも、証明書の検証を無効にしたり接続を弱めたりする根拠にはならない。最初にうまくいった変更で止まるのではなく、拒否した構成要素のポリシーとログを実際の判断までたどる。

説明できない回避策は、別の場所でまた失敗する。

修正される報告書を書く

応答した構成要素の所有者に、同じ操作、二つの経路、そして拒否した構成要素を渡す。何かを共有する前に、認可ヘッダー、埋め込まれた資格情報、非公開リポジトリの詳細を削除する。

有用な報告書は短く、再現でき、症状ではなく層を名指しする。

私が順に行う確認

リモート URL と解決されたエンドポイントを確認し、失敗する実行からステータスとヘッダーを取得する。同じ操作を二つ目のネットワークで繰り返し、二つの記録を比較する。

そのうえで、その差分を応答した構成要素へ持っていく:

  • クライアント側とネットワーク側のプロキシ設定。
  • リポジトリの手前にある外向きポリシーまたはフィルタリング。
  • トークンまたはアカウントの範囲(有効期限と SSO の条件を含む)。
  • トークンが表す身元に対するリポジトリの権限。

ステータスの意味論そのものはRFC 9110にあり、報告書を書くときには引用する価値がある。

関連記事