パッケージマネージャの HTTP プロキシ設定
Package Manager HTTP Proxy Configuration
このダイジェストはClaude Opus 4.7 / 4.8によって生成されたものです(License)。原文はこちら↗。
01 何が問題だったのか
企業ネットワークや官公庁のシステム、大学のキャンパス、ファイアウォールの内側にある CI 環境などでは、すべての HTTP 通信をプロキシサーバー経由に限定していることがよくあります。Unix では慣習として http_proxy / https_proxy といった環境変数でプロキシを指定し、ほとんどのコマンドラインツールがこれを尊重します。
ところが Swift Package Manager(SPM)のプロキシ対応は、操作の種類によって二分されていました。
- git 操作は動作します。 ソースパッケージの clone や fetch のために SPM が
gitを呼び出すとき、git のサブプロセスはプロセスの環境を引き継ぎ、http_proxy/https_proxyをネイティブに尊重します。そのため、プロキシ環境下でもこれらの操作は問題なく行えます。 - それ以外の HTTP 操作は機能しません。 バイナリアーティファクトのダウンロード、パッケージレジストリへのアクセス、パッケージコレクションの取得、署名検証のための OCSP チェック、Swift SDK のインストールなどでは、SPM は Foundation の
URLSessionを使います。このURLSessionはURLSessionConfiguration.defaultから生成され、プロキシ設定が一切適用されません。macOS ではシステムレベルのプロキシ設定が拾われることはありますが、環境変数は反映されません。Linux にはそもそもシステムプロキシの層がなく、これらの操作はプロキシに全く対応していませんでした。
つまり、プロキシの内側にいる開発者は、swift package resolve でソース依存は解決できても、同じサーバーからバイナリターゲットのアーティファクトをダウンロードできない、という状態でした。
さらに、macOS の GUI ワークフローでは環境変数そのものが扱いにくいという問題もあります。Xcode が SPM を起動するとき、Xcode は launchd から最小限の環境で起動するため、シェルの環境変数を引き継ぎません。そのため、Xcode 経由の SPM 操作に対して http_proxy を設定するには、launchctl setenv のような扱いにくい回避策に頼るしかありませんでした。
SPM には依存ミラーの設定(SE-0219)もあり、外部 URL を社内ホストへミラーリングすることで特定の依存についてはこの問題を回避できます。しかしミラーは一般的な解決策にはなりません。依存ごとに 1:1 のマッピングが必要で、多数の依存を持つプロジェクトでは現実的でなく、そもそもミラーは URL を書き換える仕組みであってネットワーク経路を制御する仕組みではないためです。「外部通信はすべてプロキシ経由」という方針の組織には、URL 単位の書き換えではなく、トランスポート層でのプロキシ対応が必要でした。
02 どのように解決されるのか
SPM の組み込み HTTP クライアントが行うすべてのネットワーク操作に対して、HTTP / HTTPS プロキシのサポートを追加します。設定方法は、JSON の設定ファイル proxy.json と、標準的なプロキシ環境変数(http_proxy / https_proxy / no_proxy)という 2 つの相補的な仕組みです。設定ファイルは起動のされ方(ターミナル、Xcode、CI)によらず確実に効くのに対し、環境変数は CI システムや既存の Unix ワークフローとの自然な統合手段になります。
設定ファイル proxy.json
SPM の設定ディレクトリに proxy.json を置きます。
- ユーザーレベル(共有):
~/.swiftpm/configuration/proxy.json - プロジェクトレベル(ローカル):
<project>/.swiftpm/configuration/proxy.json
{
"version": 1,
"http": {
"proxy": "http://proxy.corp.example.com:8080"
},
"https": {
"proxy": "http://proxy.corp.example.com:8080"
},
"noProxy": ["localhost", "127.0.0.1", "::1", ".internal.corp"]
}
version(必須・整数): スキーマのバージョン。1を指定します。http/https(任意・オブジェクト): それぞれ HTTP / HTTPS リクエスト向けのプロキシ設定。proxy(必須・文字列)にプロキシ URL を書きます。httpだけを指定した場合、HTTPS リクエストはそのプロキシを使わず直接通信します(逆も同様)。これはcurlの挙動に倣ったもので、HTTPS トラフィックを誤って HTTP 専用プロキシに流さないための設計です。noProxy(任意・文字列の配列): プロキシを経由せず直接通信するホストやパターン。
http / https の下がオブジェクトとして入れ子になっているのは意図的で、将来の認証付きプロキシ対応("authentication": "basic" など)をスキーマのバージョンを上げずに追加できるようにするためです。
CLI コマンド
swift package config の下に、プロキシ設定用のサブコマンドを追加します。
# プロジェクトの HTTP プロキシを設定
swift package config set-proxy --http http://proxy:8080
# グローバル(ユーザーレベル、全プロジェクト)に設定
swift package config set-proxy --global --http http://proxy:8080
# HTTP と HTTPS を同時に設定
swift package config set-proxy --http http://proxy:8080 --https http://proxy:8080
# 既存の設定に除外ホストを追加
swift package config set-proxy --no-proxy "localhost,.internal.corp"
# 現在の実効設定を表示
swift package config get-proxy
# HTTPS プロキシ設定だけを削除
swift package config unset-proxy --https
# すべてのプロキシ設定を削除(プロジェクトレベル)
swift package config unset-proxy
set-proxy は --http / --https / --no-proxy の少なくとも 1 つを要求し、追加的(additive) に動作します。つまり、指定したフィールドだけを更新し、既存の設定は残します。unset-proxy はフラグを指定するとその設定だけを、フラグなしではすべてのプロキシ設定を削除します。
--global フラグはユーザーレベルの設定を対象とし、カレントディレクトリに Package.swift がなくても任意のディレクトリで実行できます。これは SE-0535 でミラーコマンドに追加された --global フラグと一貫した挙動です。
get-proxy は優先順位を解決した後の実効設定を表示し、各値がどのソース(プロジェクト設定・ユーザー設定・システム・環境変数)に由来するかも示します。
$ swift package config get-proxy
HTTP proxy: http://proxy:8080 (user: ~/.swiftpm/configuration/proxy.json)
HTTPS proxy: http://corpproxy:3128 (system)
No proxy: localhost, .internal.corp (user: ~/.swiftpm/configuration/proxy.json)
優先順位
プロキシ設定を決めるとき、SPM は次の順で最初に値を提供したソースを採用します。フィールドごとに独立して解決されるため、たとえば HTTP はユーザー設定、HTTPS はシステムプロキシ、というように別々のソースから来ても構いません。
- 環境変数(
http_proxy/HTTP_PROXYなど。小文字が大文字より優先) — 最優先 - プロジェクトのローカル設定(
<project>/.swiftpm/configuration/proxy.json) - ユーザーレベル(グローバル)設定(
~/.swiftpm/configuration/proxy.json) - macOS のシステムプロキシ(システム設定のネットワーク → プロキシ。macOS のみ)
- プロキシなし(直接接続) — デフォルト
環境変数を最優先にしているのは、これがコマンドラインツールの標準的な慣習であり、呼び出し側が他の設定によらずプロキシ設定を確実に強制できるようにするためです。macOS では URLSession がシステムプロキシ設定を自動的に引き継ぐため、システムプロキシが設定済みであれば環境変数も proxy.json もなしで通信がプロキシ経由になります。Linux にはシステムプロキシの層がないため、設定手段は環境変数と proxy.json になります。
noProxy のマッチング
noProxy は次のパターンをサポートします(マッチングは大文字小文字を区別しません)。
| パターン | マッチ対象 |
|---|---|
* |
すべてのホスト(実質的にプロキシを無効化) |
example.com |
example.com とそのすべてのサブドメイン(sub.example.com など) |
.example.com |
example.com のサブドメインのみ(example.com 自身は含まない) |
192.168.1.1 |
完全一致の IP アドレス |
localhost |
ホスト名 localhost |
プロキシ URL の形式
プロキシ URL は scheme://host[:port] の標準形式で、http(最も一般的で、CONNECT により HTTP / HTTPS 両方のターゲットに使われる)、https、socks5 のスキームに対応します。URL に資格情報(user:password@)を含めることはできません。含まれていた場合、SPM は認証付きプロキシがまだサポートされていない旨のエラーを出します。
適用範囲
プロキシ設定は、SPM の組み込み HTTP クライアントが行うすべての HTTP 操作に適用されます。
- バイナリアーティファクトのダウンロード
- パッケージレジストリの API リクエスト
- パッケージコレクションの取得
- OCSP 証明書検証のリクエスト
- Swift SDK のダウンロード
- ビルド済みバイナリのダウンロード
一方、git 操作には影響しません。git は従来どおり git 自身のプロキシ設定(gitconfig の http.proxy、またはサブプロセスに渡される環境変数)を使います。
実装上は、SPM のすべての HTTP クライアント抽象(HTTPClient / LegacyHTTPClient)が共通して使う唯一の具体実装である URLSessionHTTPClient の層でプロキシ設定を適用します。URLSession を生成する前に URLSessionConfiguration の connectionProxyDictionary を設定するため、既存の利用側コードは変更なしでプロキシ対応を得られます。プラットフォーム差は条件付きコンパイルで吸収します。
なお、この提案は完全にオプトインで、プロキシを設定しないパッケージは従来どおり直接接続を行います。Package.swift のマニフェスト形式や依存解決の挙動、tools-version による制御には一切変更がありません。
03 今後の見通し
この提案では認証を伴わないプロキシに絞っていますが、その先にいくつかの方向性が示されています。いずれも将来の構想であり、実現を約束するものではありません。
認証付きプロキシのサポート
ユーザー名とパスワード、あるいはトークンによる認証を必要とするプロキシへの対応が、将来の提案として検討されています。swift package-registry login と同じパターンに倣い、次のような形が想定されています。
swift package config proxy-login <proxy-url>コマンドを追加し、--username/--passwordまたは--tokenを受け付ける- 資格情報は OS の安全な資格情報ストア(macOS では Keychain、それ以外では
.netrc)に保存する proxy.jsonには認証の種類("authentication": "basic"など)だけを記録し、資格情報そのものは書かない- パスワードがシェル履歴に残らないよう、対話的なプロンプトにも対応する
これにより、平文の設定ファイルに資格情報を置かずに済み、レジストリログインのワークフローとの一貫性も保てます。
noProxy での CIDR レンジ指定
初期の実装では、noProxy に書いた IP アドレスは完全一致として扱われます。将来的には CIDR 記法(192.168.0.0/16 など)による IP レンジのマッチングをサポートすることも考えられます。
より広いネットワーク設定への統合
将来 SPM がカスタム CA 証明書や接続タイムアウト、TLS 設定といったネットワークレベルの設定を必要とするようになった場合、proxy.json をより広い network.json に統合することも考えられます。スキーマの version フィールドは、そうした移行のための余地を残すものです。