Antigravity CLI ネットワーク問題の整理
Antigravity CLI ネットワーク問題の整理
1. 背景
最初の Docker 開発環境では、主に Codex を利用していた。
この初期構成には、すでに Squid コンテナが含まれていた。
app-editor
Codex / 開発作業用コンテナ
squid
Docker network 内にある proxy コンテナ
git-bare
restricted network 上の内部 Git サーバー
app-editor は複数の Docker network に接続されていた。
services:
app-editor:
networks:
- restricted
- ingress
- outbound
git-bare:
networks:
restricted:
aliases:
- git
内部 Git サーバーは restricted network 上にあり、app-editor から以下のように参照していた。
git://git:9418/app.git
この段階では、Antigravity CLI 固有の通信要件はまだ問題になっていなかった。
2. 構成図ビフォーアフター
2.1 Before: 初期構成
flowchart LR
subgraph Docker["Docker Compose"]
subgraph Restricted["restricted network"]
App["app-editor<br/>Codex / 開発作業 / 後から agy 導入"]
Git["git-bare<br/>git://git:9418/app.git"]
end
subgraph ProxyNet["proxy / outbound network"]
SquidC["Squid container<br/>http://squid:3128"]
end
App -->|internal git| Git
App -.->|HTTP_PROXY / HTTPS_PROXY| SquidC
SquidC --> Internet["Internet"]
end
App -.->|Antigravity CLI が一部直行| Internet
classDef problem fill:#ffe6e6,stroke:#cc0000,color:#000;
class Internet,App problem;
初期構成では、Codex 利用を前提とした Docker 開発環境に、すでに Squid コンテナが含まれていた。
後から Antigravity CLI を導入したところ、Squid 経由ではなく外部へ直行しようとする通信があり、DNS / proxy / route の問題が表面化した。
2.2 After: 最終構成
flowchart LR
subgraph Docker["Docker Compose"]
subgraph Internal["internal / private networks"]
App2["app-editor<br/>Codex / Antigravity / npm / Go"]
Git2["git-bare<br/>git://git:9418/app.git"]
end
end
App2 -->|private IP 宛ては許可| Git2
App2 --> Bridge["Docker bridge<br/>docker0 / br-*"]
Bridge --> DU["DOCKER-USER"]
DU --> Filter["DOCKER-EGRESS-FILTER"]
Filter --> Private["private IP<br/>10/8, 172.16/12, 192.168/16<br/>RETURN"]
Filter --> IPSet["ipset docker_allowed_v4<br/>DNS解決結果の許可IP"]
Filter --> Drop["LOG + DROP<br/>未許可通信"]
IPSet -->|TCP 80/443 only| Internet2["Internet"]
Drop -.-> Blocked["example.com など<br/>未許可宛先"]
DockerNAT["Docker NAT / MASQUERADE<br/>Docker に任せる"] -.-> Bridge
classDef ok fill:#e6ffed,stroke:#008000,color:#000;
classDef block fill:#ffe6e6,stroke:#cc0000,color:#000;
classDef control fill:#e6f0ff,stroke:#005fcc,color:#000;
class Private,IPSet,Internet2 ok;
class Drop,Blocked block;
class DU,Filter,DockerNAT control;
最終構成では、Squid / transparent proxy / nftables 単体制御は使わず、Docker の DOCKER-USER chain と ipset を使って外向き通信を制御する。
Docker の NAT / MASQUERADE は Docker に任せ、自前では許可IPへの TCP 80/443 と private IP 宛てだけを通す。
3. 問題の発生
後から Antigravity CLI (agy) を導入しようとしたところ、外部通信まわりの問題が表面化した。
代表的なエラーは以下。
Eligibility check failed:
Post "https://daily-cloudcode-pa.googleapis.com/v1internal:loadCodeAssist":
dial tcp: lookup daily-cloudcode-pa.googleapis.com on 127.0.0.11:53:
server misbehaving
別の状況では以下のようなエラーも出た。
dial tcp 142.251.24.95:443: connect: network is unreachable
つまり、Codex 利用時には問題になっていなかった以下の要素が、Antigravity CLI の導入によって問題化した。
Docker DNS
外部 API への HTTPS 通信
proxy 設定
Docker network の default route
Antigravity CLI が proxy を尊重するかどうか
4. 試したこと 1: 既存の Squid コンテナを使う
4.1 目的
初期構成にすでに存在していた Squid コンテナを使って、Antigravity CLI の外部通信を proxy 経由にできないか確認した。
4.2 試した構成
app-editor から Docker network 内の Squid コンテナを参照した。
http://squid:3128
環境変数として以下を設定した。
HTTP_PROXY=http://squid:3128
HTTPS_PROXY=http://squid:3128
http_proxy=http://squid:3128
https_proxy=http://squid:3128
Python から Squid への TCP 接続や CONNECT は確認できた。
oauth2.googleapis.com:443
daily-cloudcode-pa.googleapis.com:443
への CONNECT が通ることも確認した。
4.3 結果
Squid コンテナ自体は動いていた。
しかし、Antigravity CLI は一部通信で proxy を完全には使っていないように見えた。
そのため、単純な HTTP_PROXY / HTTPS_PROXY 方式では解決しなかった。
4.4 分かったこと
Squid コンテナに到達できることと、
Antigravity CLI が常に Squid を使うことは別問題
5. 試したこと 2: Squid の hostname / IP 固定
5.1 目的
proxyconnect tcp: lookup squid ... no such host のようなエラーを避けたかった。
5.2 試したこと
squid という hostname の名前解決が不安定に見えたため、Compose 上で IP を固定したり、proxy の指定を hostname ではなく IP にしたりした。
例:
HTTPS_PROXY=http://172.30.0.52:3128
5.3 結果
Squid コンテナへの名前解決や到達性の問題はある程度整理できた。
しかし、Antigravity CLI の eligibility check で発生する直行通信の問題は残った。
5.4 分かったこと
Squid の名前解決問題を直しても、
Antigravity CLI が proxy を無視する通信は解決しない
6. 試したこと 3: CoreDNS を試す
6.1 位置づけ
CoreDNS は初期構成には存在しなかった。
Claude 関連の URL や、外部サービスの名前解決を制御・確認する流れの中で、試験的に試した。
これは本格導入ではなく、名前解決制御の検証だった。
6.2 目的
特定ドメインを任意の IP に向ければ、通信を制御できるのではないかと考えた。
例:
daily-cloudcode-pa.googleapis.com -> Squid の IP
6.3 結果
この方法は根本的な解決にならなかった。
理由は、DNS で Squid の IP に向けても、HTTPS クライアントは以下へ接続するだけだからである。
SquidのIP:443
しかし Squid は通常、HTTP proxy として 3128 で待ち受けている。
Squid:
3128 で HTTP proxy として待ち受け
Antigravity CLI:
443 に HTTPS 接続しようとする
そのため、DNS を書き換えても 443 -> 3128 の変換にはならなかった。
6.4 分かったこと
DNS は宛先IPを変えられるだけ
宛先ポートやプロトコルまでは proxy 用に変換しない
7. 試したこと 4: コンテナ内 iptables + redsocks
7.1 目的
Antigravity CLI が proxy を無視するなら、コンテナ内で TCP 80/443 を強制的に proxy へ流したかった。
7.2 試した構成
app-editor
↓ iptables OUTPUT
redsocks
↓
Squid コンテナ
7.3 結果
app-editor が default route を持たない状態では、外部 IP への接続が routing 段階で失敗した。
network is unreachable
この場合、iptables の NAT OUTPUT に到達する前に失敗する。
つまり、redsocks に流す以前に、カーネルが「その宛先へ行く経路がない」と判断していた。
7.4 分かったこと
iptables OUTPUT で横取りするには、
まず対象宛先が route 可能である必要がある
default route が無い internal network だけでは、
NAT OUTPUT に到達する前に失敗する
8. 試したこと 5: ホスト側 Squid + transparent proxy
8.1 目的
コンテナ内で制御するのではなく、ホスト側で Docker bridge から出る通信を強制的に proxy へ流したかった。
8.2 試した構成
途中で、Squid を Docker コンテナ内ではなくホスト側に導入する構成を試した。
Docker container
↓
Docker bridge
↓ DNAT
redsocks / sing-box
↓
host Squid
↓
Internet
redsocks は古くメンテナンスが不安だったため、sing-box も試した。
8.3 結果
Docker bridge から出る TCP 80/443 を DNAT し、ホスト側 Squid まで流すこと自体はできた。
しかし、新しい問題が分かった。
Squid access.log には以下のように出た。
CONNECT 172.66.147.243:443
CONNECT 104.20.23.154:443
期待していた以下のようなログではなかった。
CONNECT daily-cloudcode-pa.googleapis.com:443
CONNECT api.openai.com:443
8.4 原因
transparent proxy では、アプリが先に DNS 解決して IP に接続しようとする。
その TCP 接続を後から横取りするため、Squid から見ると宛先は IP だけになる。
アプリ
↓ DNS 解決
domain -> IP
↓
IP:443 に接続
↓
transparent proxy が横取り
↓
Squid には IP:443 として届く
8.5 分かったこと
明示 proxy:
Squid にドメイン名が残る
dstdomain ACL が使える
transparent proxy:
Squid には IP:port しか見えない
dstdomain ACL は期待通り使えない
9. 試したこと 6: Squid で IP allowlist 制御
9.1 目的
transparent proxy でドメインが見えないなら、DNS 解決結果の IP で制御しようとした。
9.2 試した構成
allowed domains
↓ DNS 解決
allowed IPs
↓
Squid dst ACL
Squid 側では dstdomain ではなく dst ACL を使う方向にした。
9.3 結果
IP ベースでの許可/拒否はできる見込みが立った。
しかし、ここで設計上の疑問が出た。
IP でしか制御しないなら、
Squid を挟む必要が薄いのではないか
9.4 分かったこと
Squid を proxy として使う意味は、
ドメイン名 CONNECT が見える場合に大きい
transparent proxy で IP しか見えないなら、
Squid は単なる IP フィルタに近くなる
10. 試したこと 7: nftables による IP allowlist 制御
10.1 目的
Squid / sing-box を外し、Docker の外向き通信を nftables で直接制御したかった。
10.2 試した構成
Docker container
↓
Docker bridge
↓
nftables forward hook
↓
allowed IP の TCP 80/443 だけ許可
10.3 結果
未許可の example.com は drop できた。
一方で、許可済みの api.openai.com は drop されていないにもかかわらず timeout した。
状況は以下。
example.com:
drop ログあり
通信失敗
api.openai.com:
drop ログなし
allowed_v4 に IP あり
しかし timeout
原因は Docker の NAT / MASQUERADE が効いていなかったことだった。
手動で以下を入れると通信できた。
iptables -t nat -A POSTROUTING -s 172.18.0.0/16 -j MASQUERADE
10.4 分かったこと
nftables の filter で accept しても、
Docker の送信元NATが無ければ外部から戻り通信が返らない
Docker の iptables / NAT と、
自前 nftables を直接混ぜると事故りやすい
11. 最終案: DOCKER-USER + ipset
11.1 方針
Docker の NAT / MASQUERADE は Docker に任せる。
自前の通信制限は、Docker が用意している DOCKER-USER chain に入れる。
11.2 最終構成
Docker container
↓
Docker bridge
↓
DOCKER-USER
↓
DOCKER-EGRESS-FILTER
↓
ipset docker_allowed_v4
11.3 制御内容
private IP 宛て
→ RETURN
allowed IP + TCP 80/443
→ RETURN
UDP 443
→ DROP
その他
→ LOG + DROP
11.4 結果
Docker の NAT と競合しにくくなり、外向き通信制限を実現できた。
example.com のような未許可宛先は drop される。
api.openai.com など許可済みドメインの DNS 解決結果に含まれる IP は通る。
12. strict / relaxed モード
運用上、許可ドメインを2段階に分けた。
12.1 strict
Antigravity / OpenAI / Anthropic / Google API など、最低限に近い許可。
api.openai.com
auth.openai.com
chatgpt.com
ab.chatgpt.com
platform.openai.com
oauth2.googleapis.com
antigravity-unleash.goog
www.googleapis.com
playwright.azureedge.net
antigravity-cli-auto-updater-974169037036.us-central1.run.app
storage.googleapis.com
api.anthropic.com
platform.claude.com
daily-cloudcode-pa.googleapis.com
12.2 relaxed
npm / GitHub / Go module / Playwright など、開発作業向けに許可を広げたもの。
api.openai.com
auth.openai.com
chatgpt.com
ab.chatgpt.com
platform.openai.com
oauth2.googleapis.com
antigravity-unleash.goog
www.googleapis.com
playwright.azureedge.net
api.github.com
github.com
codeload.github.com
objects.githubusercontent.com
raw.githubusercontent.com
proxy.golang.org
sum.golang.org
storage.googleapis.com
registry.npmjs.org
npmjs.com
www.npmjs.com
cdn.playwright.dev
api.anthropic.com
platform.claude.com
切り替え用に以下を用意した。
docker-egress-strict
docker-egress-relaxed
13. 内部 Docker 通信への対応
Compose 内には git-bare もあり、app-editor から以下でアクセスしていた。
git://git:9418/app.git
外向き制限を Docker bridge 全体にかけると、内部通信も巻き込む可能性がある。
そのため、private IP 宛ては制限対象から外した。
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
これにより、Docker 内部の Git サーバーなどへの通信は許可し、外部向け通信だけを allowlist 制御する形にした。
14. Docker restart 後の再適用
iptables / ipset は永続化しない。
また、Docker daemon restart 時には Docker の iptables ルールが再構成される可能性がある。
そのため、Docker 起動後に自前ルールを再適用するため、systemd drop-in を使った。
[Service]
ExecStartPost=/usr/local/sbin/update-docker-egress-ipset.sh
これにより、Docker daemon 起動後・再起動後に DOCKER-USER + ipset ルールを再適用する。
15. 最終的な妥協点
最終的には、厳密な FQDN 制御は諦めた。
理由は以下。
Antigravity CLI が proxy を完全には尊重しない
transparent proxy では Squid にドメイン名が残らない
SNI 制御は偽装や実装コストの問題がある
nftables を直接使うと Docker NAT と競合しやすい
そのため、以下に妥協した。
DNS 解決結果ベースの IP allowlist 制御
Docker NAT は Docker に任せる
通信制限は DOCKER-USER + ipset で行う
用途ごとに strict / relaxed を切り替える
この方式は、厳密なドメイン制御ではない。
CDN や共有 IP の場合、許可した IP 上の別ドメインにも到達できる可能性がある。
ただし、今回の目的には実用上かなり合っていた。
16. 最終構成
app-editor container
↓
Docker bridge
↓
DOCKER-USER
↓
DOCKER-EGRESS-FILTER
↓
ipset docker_allowed_v4
├─ private IP → RETURN
├─ allowed IP + TCP 80/443 → RETURN
├─ UDP 443 → DROP
└─ others → LOG + DROP
Docker NAT / MASQUERADE は Docker 管理に任せる。
Docker NAT:
Docker 管理
Egress 制限:
DOCKER-USER + ipset 管理
17. 結論
最初は、Codex 利用を前提とした Docker 開発環境だった。
この初期構成には Squid コンテナも含まれていたが、Antigravity CLI 固有の通信問題はまだ表面化していなかった。
後から Antigravity CLI を導入したことで、外部通信、DNS、proxy、Docker network の問題が表面化した。
既存の Squid コンテナ、CoreDNS の試行、コンテナ内 redsocks、ホスト側 Squid + transparent proxy、Squid の IP allowlist、nftables などを順に試した。
しかし、最終的には Squid も transparent proxy も不要と判断し、Docker と競合しにくい DOCKER-USER + ipset 方式に落ち着いた。
これは FQDN 単位の厳密な制御ではなく、DNS 解決結果ベースの IP 制御という妥協である。
ただし、Antigravity CLI のように proxy を無視する可能性があるアプリに対しても、Docker コンテナ単位で外向き通信を制限できる、現実的な解決策になった。