すべてのリクエストが 407 Proxy Authentication Required で返ってきて、ターゲットには何も届かず、パネル上の認証情報は正しく見える。これはこの製品カテゴリで最もよくあるサポートチケットの一つであり、同時に最も速く解決できるものでもある。なぜなら407を引き起こす要因の数は少なく、決まった順序で切り分けられるからだ。
このステータスコードが実際に何を意味するのか、確認すべき原因、確認する順序、そして認証情報が正しい場合でも407を引き起こすクライアント側の誤りについて説明する。
407が実際に意味するもの
407はターゲットサイトからではなく、プロキシから返ってくる。これはプロキシが「リクエストは届いたが、認証情報が受け入れられなかった」と伝える方法であり、オリジンサーバーからの401に相当するプロキシ層版のエラーである。この区別はデバッグにおいて重要だ。407が返ってきたということは、トラフィックはゲートウェイに到達し、ゲートウェイがそれを拒否したことを意味する。接続自体には問題はない。認証に問題があるのだ。
これはまた、ターゲットサイトは一切関与していないことも意味する。407が返ってきている場合、ヘッダー、ユーザーエージェント、レンダリング、リクエスト間隔などを変更しても何の助けにもならない。なぜならリクエストはそもそもプロキシを出ていないからだ。
まず確認すべき3つの原因
ターゲティングをユーザー名で指定するゲートウェイでは、407のほとんどは次の3つのいずれかである。
認証情報が間違っている。 タイプミス、ダッシュボードからコピーした際に紛れ込んだ余分な空白文字、あるいは別の製品の認証情報を使っている場合などだ。プロキシの認証情報は通常、アカウントのログイン情報とは異なる。これは驚くほどよくある混同である。
拡張ユーザー名内のフラグが不正な形式になっている。 これは見落とされがちな原因であり、ターゲティングをユーザー名にエンコードするゲートウェイに特有のものだ。ユーザー名に国、都市、セッション、TTLのフラグが含まれている場合、認識できない値があるとユーザー名全体がパースできなくなり、ゲートウェイはターゲティングエラーとしてではなく認証エラーとしてそれを拒否する。誤った国コード、フォーマットの間違った都市スラッグ、不正な文字を含むセッション識別子、sidを伴わないttlなどが該当する。認証情報自体は完璧だが、ユーザー名文字列がそうではないのだ。
パスワードがローテーションされた。 誰かがパネルで再生成した場合、古い値を保持しているすべてのクライアントは更新されるまで407を返す。あるマシンでは動くのに別のマシンでは失敗する、という典型的なケースだ。
診断の順序
このリストを上から順に実行し、うまくいったところで止める。なぜなら、問題を解決したステップこそが原因を特定するからだ。
1. すべてのフラグを外し、素の認証情報でテストする。 このステップ一つで、認証情報の問題なのかフラグの問題なのかを切り分けられる。常に最初に行うべきだ。
# bare username, no targeting flags at all
curl -x customer-USERNAME:PASSWORD@p.shifter.io:443 https://ipinfo.io/json
これが成功すれば、認証情報自体は問題なく、原因はフラグにある。それでも407が返る場合は、認証情報自体が間違っており、フラグを修正しても意味がない。
2. フラグを一つずつ戻していく。 まず国、次に都市またはASN、その後セッションとTTLの順だ。407が再発したフラグが不正な形式のものであり、これでどの値を確認すべきかが正確にわかる。
curl -x customer-USERNAME-country-us:PASSWORD@p.shifter.io:443 https://ipinfo.io/json
curl -x customer-USERNAME-country-us-city-new_york:PASSWORD@p.shifter.io:443 https://ipinfo.io/json
curl -x customer-USERNAME-country-us-city-new_york-sid-abc123:PASSWORD@p.shifter.io:443 https://ipinfo.io/json
フォーマットに注意すること。国コードは2文字のISOコードであるため、ukはよくある間違いで、正しくはgbだ。都市スラッグはスペースやハイフンではなくアンダースコアを使う。例えばnew_yorkのようにだ。またttlはsidと併用する場合のみ有効で、単体のTTLは無効である。完全な構文はgateway and authentication docsにあり、ターゲティングオプションについてはcity-level targetingとASN targetingで扱っている。
3. パネルからパスワードを再コピーする。 ステップ1で素の認証情報が失敗した場合、メモや設定ファイルではなく、パネルから直接パスワードを取得し、末尾のスペース、文書からコピーした際のスマートクォート、途中で切れたペーストがないか特に確認する。
4. 値をすべての箇所に反映させる。 ローカルでは動くが本番環境では動かない場合、環境変数、シークレットマネージャー、コンテナイメージ、CI設定のいずれかに古いコピーが残っている。これはプロキシの問題ではなくデプロイの問題だ。
407に似ているが実際には異なるエラー
これらを区別することで多くの無駄な労力を省ける。それぞれ対処法が異なるからだ。
このゲートウェイにおける502は、その時点でフィルタに一致するアドレスがなかったことを意味する。これは認証の問題ではなくターゲティングの問題だ。認証情報は受け入れられたが、指定した組み合わせに対して利用可能なものがなかったということである。フィルタを広げる、都市指定から国指定に落とす、あるいは厳密一致のフラグを緩める、といった対処が必要だ。
509はプランの帯域幅が枯渇し、超過利用が無効になっていることを意味する。認証は成功しており、クォータが不足しているだけだ。関連するサイジングのガイダンスはestimating monthly bandwidthにある。
接続拒否はゲートウェイに到達すらしていないことを意味し、通常は現在のエンドポイントではなく従来のポートベースのホストを指定しているか、ネットワークからのアウトバウンド接続がブロックされていることが原因だ。認証の問題だと決めつける前に、nc -vz p.shifter.io 443 で生の接続性をテストすること。
ターゲットからの403やチャレンジページは、認証が完全に成功し、サイト側が拒否したことを意味する。これはまったく別の問題であり、avoiding blocksで扱っている。
クライアント側の誤りが407を引き起こすケース
時には認証情報もフラグも正しく、クライアント側に問題があることもある。
パスワードに含まれる特殊文字。 パスワードにURL上で意味を持つ文字、つまり @、:、#、/、% などが含まれている場合、それをプロキシURLに直接埋め込むとパースが壊れ、認証情報が破損した状態で届く。パーセントエンコードすること。
import requests
from urllib.parse import quote
user = "customer-USERNAME-country-us"
pwd = quote("p@ss:word/123", safe="") # encode before embedding
PROXY = f"http://{user}:{pwd}@p.shifter.io:443"
r = requests.get("https://ipinfo.io/json",
proxies={"http": PROXY, "https": PROXY}, timeout=15)
print(r.status_code, r.text[:120])
プロキシ認証とターゲット認証の混同。 curl -U はプロキシの認証情報を設定し、-u はターゲットサイトの認証情報を設定する。プロキシの認証情報を Authorization ヘッダーとして送っても何の効果もない。プロキシ認証は Proxy-Authorization で送られるものであり、認証情報がプロキシURLに含まれている場合、ほとんどのクライアントはそれを自動的に設定してくれる。
HTTPSで認証情報が失われる。 クライアントの設定によっては、プロキシをHTTPのみに対して設定していることがあり、プレーンなリクエストは認証されるがHTTPSリクエストはされないことがある。上記のPythonの例のように両方の項目を設定すること。
想定と異なる環境変数。 シェルのプロファイル、Dockerfile、CIランナーで設定された HTTP_PROXY や HTTPS_PROXY は、コードが渡している値を黙って上書きすることがあり、アプリケーションがソースコード内のものとはまったく異なるプロキシ文字列に対して認証しようとすることがある。デバッグの際はコードを信用せず、実際に有効になっているプロキシ設定を出力して確認すること。
プリエンプティブなプロキシ認証を送らないライブラリ。 一部のHTTPクライアントは認証情報を送る前にチャレンジを待ち、特定のトンネリング設定においてそのチャレンジをうまく処理できないことがある。素の curl は動くのにアプリケーションでは動かない場合、違いはゲートウェイではなくクライアント側にある。一般的なスタックでの動作する設定についてはusing residential proxies with Pythonにまとめている。
本番コードでの407の扱い方
運用上の注意点として、407は一時的なエラーではなく終端的なエラーである。リトライしても意味がない。なぜなら次の試行でも認証情報は同様に間違っているからであり、認証失敗に対するリトライループは単に帯域幅を浪費するだけでなく、外部からはクレデンシャルスタッフィングのパターンに見えかねない。これを終端エラーとして分類し、大きく失敗させ、アラートを出すこと。これはretry and backoffで述べられている分類の原則である。
認証情報のローテーションは、パネルでのワンクリックではなくデプロイ計画を必要とする。なぜなら古い値を保持しているすべてのクライアントは、ローテーションの瞬間から失敗し始めるからだ。まずシークレットストアを更新し、クライアントをロールし、その後でローテーションすること。
まとめ
407は、リクエストがゲートウェイに到達し、ゲートウェイが認証情報を拒否したことを意味する。したがってターゲットサイトは無関係であり、接続性は証明済みである。まず素の認証情報をフラグなしでテストすること。この一つのステップで問題を半分に切り分けられる。次にフラグを一つずつ戻して、不正な形式のものを見つける。誤った国コードや、セッションを伴わないTTLはユーザー名全体をパース不能にすることを覚えておくこと。素のテストが失敗した場合はパネルからパスワードを再コピーし、一方の環境では動くが他方では動かない場合は環境変数やシークレットストア内の古い値を確認すること。似て非なるものを除外すること。502は空のフィルタ、509は帯域幅、接続拒否は誤ったホスト、サイトからの403はそもそも認証の問題ではない。そして407を決してリトライしないこと。
完全な構文リファレンスはgateway and authentication docsにあり、製品自体はresidential proxiesにある。同じ認証情報がすべての国、都市、セッションモードで動作し、per-GB pricingが適用される。