プロキシを有効にしたら社内 API が落ちた
目標
顧客の社内プロキシの設定で、curl・Python urllib・requestsが、それぞれどの経路に行くのかを、記録するダミーのプロキシで直接測り、3つのツールがすべて同じ経路に行く設定・連携ツール・判定ツールを作ります。
なぜ重要なのか
プロキシの環境変数は、標準ではなく慣習なので、ツールごとに解釈が違います。そのため、顧客の設定を入れたあと、curlのヘルスチェックは緑なのに、Pythonの収集ツールだけが、社内APIから403を受け取ることが起きます。推測で設定を直すと、1つのツールを生かして別のツールを殺しやすくなります。このラボは、「そのリクエストがプロキシに届いたか」を、記録で確認する習慣を作ります。
用意するもの: /opt/lab/p1a-proxy/。corpnet.py(ダミーのプロキシ・社内API)、customer.env(顧客の設定)、cases.tsv(ケース18個)、connector.py(問題のある連携ツール)です。インターネットはありません。127.0.0.2・127.0.0.3も、このPod自身(ループバック)で、corp.example・saascorp.exampleのような名前は解決されません。直接行こうとしたリクエストは、名前解決で失敗しますが、プロキシの記録にないこと自体が、「直接行った」という証拠です。
想定所要時間は60分です。セッションが終わると、/root/proxyは消えるので、必要なファイルは別に保管してください。
ステップ
python3 /opt/lab/p1a-proxy/corpnet.py upで、社内ネットワークの模擬を立ち上げ、customer.envを適用した状態で、http://127.0.0.2:8080/healthを、curl・urllib・requestsで1回ずつリクエストします。応答本文のserved_byとidを見て、도구<TAB>PROXY|DIRECT<TAB>idの形で、3行を記録します(プレースホルダーはツール名です。ファイル: /root/proxy/repro.tsv)。- cases.tsvのステップ2のケース(c01..c05、大文字・小文字、スキームごとの変数)を、3つのツールで実行して、
case<TAB>curl<TAB>urllib<TAB>requestsの形で記録します(ファイル: /root/proxy/matrix.tsv)。 - ステップ3のケース(c06..c10、サフィックス・先頭のドット・アスタリスク付きのドメイン)を測って、matrix.tsvに追加します。
- ステップ4のケース(c11..c14、IP・CIDR・ポート)を測って、matrix.tsvに追加します。
- ステップ5のケース(c15..c18、アスタリスク1文字と、小文字・大文字の優先順位)を測って、matrix.tsvに追加します。
- 3つのツールがすべて、社内の4か所(127.0.0.2:8080、127.0.0.3:8080、localhost:8081、api.corp.example:8080)は直接、外部の3か所(httpとhttpsのsaascorp.example、updates.vendor.example)は、プロキシ
http://127.0.0.1:3128経由で行くようにする設定を作ります(ファイル: /root/proxy/fixed.env)。 - connector.pyをコピーして直します(コピー先: /root/proxy/connector.py)。CONNECTOR_PROXYは引き続き使いますが、NO_PROXYに該当するURLは直接行き、環境のHTTP_PROXY・HTTPS_PROXYは使ってはいけません。
- 探針を作ります(ファイル: /root/proxy/route_probe.py)。
python3 route_probe.py <env파일> URL...が、設定ファイルのプロキシのアドレスを、ローカルのセンチネルに差し替えて、3つのツールを実際に実行し(プレースホルダーはenvファイルのパスです)、URLごとにurl<TAB>curl<TAB>urllib<TAB>requestsを出力し、判定が分かれたら終了コード3、すべて同じなら0で終わる必要があります。
参考
- 1回だけ環境を与えて実行:
env -i PATH=$PATH http_proxy=http://127.0.0.1:3128 curl -s http://127.0.0.2:8080/health。env -iで、シェルに残った変数を消します。 - 記録を見る:
tail -n 3 /root/proxy/logs/proxy.jsonl、tail -n 3 /root/proxy/logs/api.jsonl - urllibは、403を例外(HTTPError)として投げます。本文は、例外オブジェクトの
read()で読みます。 - httpsのケースは、プロキシにCONNECTとして届きます。本文がないので、記録の行数で判定します。
- 採点ツールは、あなたが立ち上げたcorpnetを使いません。自分のポートに同じ模擬を立ち上げて、同じ条件でもう一度測ります。
- よくある間違い: シェルにexportしておいたプロキシの変数が、次のケースに混ざる、
*.corp.exampleがワイルドカードとして動作すると信じる。
顧客の設定で障害を再現して、証拠を残す
corpnetを立ち上げ、customer.envで、3つのツールのリクエストを1回ずつ送ったあと、ツール・経路・idを記録してください(ファイル: /root/proxy/repro.tsv)。
応答本文のserved_byがcorp-proxyなら、プロキシを通ったということです。idは、logsの記録の行と対応している必要があり、記録のUser-Agentで、どのツールが送ったかを、クロスチェックされます。customer.envは、set -aで、サブシェルでだけexportしてください。
大文字のHTTP_PROXYとALL_PROXYを測る
cases.tsvのc01..c05を、3つのツールで実行して、PROXY/DIRECTで記録してください(ファイル: /root/proxy/matrix.tsv)。
ケースごとに、env -iで、きれいな環境を作り、リクエストの前後で、proxy.jsonlの行数が増えたかを見てください。curlのマニュアルのENVIRONMENTの節で、http_proxyにだけ付いた例外を探してみてください。
サフィックス・先頭のドット・アスタリスク付きのドメイン
c06..c10を測って、追加してください(ファイル: /root/proxy/matrix.tsv。前のケースの行は消さないでください)。
直接行こうとしたリクエストは、名前解決で失敗します。失敗したかどうかではなく、プロキシの記録に届いたかどうかで判定してください。saascorp.exampleは、corp.exampleで終わる、別の会社の名前です。
CIDRとポートを書いたNO_PROXY
c11..c14を測って、追加してください(ファイル: /root/proxy/matrix.tsv)。
CIDRのサポートは、curlのマニュアルの--noproxyの説明に、バージョンとともに出てきます。urllibのドキュメントは、no_proxyの項目にポートを付けられると書いています。ドキュメントが何も言っていないツールは、測ってみないとわかりません。
アスタリスク1文字と、小文字の優先
c15..c18を測って、追加してください(ファイル: /root/proxy/matrix.tsv)。
アスタリスクが、リストの中の1つの項目のときと、リスト全体のときを、比べてください。空文字列の小文字の変数が、「設定されていない」と読まれるのか、「空のリスト」と読まれるのかも、ツールごとに違います。
3つのツールの共通項で設定を書き直す
社内の4か所は直接、外部の3か所はプロキシ(http://127.0.0.1:3128)経由になる設定を作ってください(ファイル: /root/proxy/fixed.env)。
前に埋めた表で、3つのツールがすべて同じ答えを出した表記だけを選んでください。大文字・小文字、ドメインの前のドット、CIDRの代わりに使うもの、ポートを付けるかどうか、の4つを決める必要があります。
proxies引数を使う連携ツールを直す
/opt/lab/p1a-proxy/connector.pyをコピーして、NO_PROXYを守るように直してください(コピー先: /root/proxy/connector.py)。
requests.utilsに、URLとno_proxyの文字列で、迂回するかどうかを教えてくれる関数があります。セッションのtrust_envは、環境変数のプロキシを使うかどうかを決めます。社内のIPをコードに埋め込むと、別のNO_PROXYで採点するときに不合格になります。
どんな設定でも、ツールごとの経路を測る探針
探針を作ってください(ファイル: /root/proxy/route_probe.py)。envファイルとURLを受け取って、3つのツールの経路を実際の実行で判定してTSVで出力し、分かれたら3、同じなら0で終わります。
顧客のプロキシのアドレスは、このPodから届きません。判断の規則(no_proxy)はそのままにして、プロキシの値だけを、ローカルのセンチネルのアドレスに差し替えれば、センチネルが接続を受けたかどうかで判定できます。採点ツールは、ランダムなドメインとプロキシのアドレスで試験します。