Skip to content

fix(devcontainer): 許可外を即座に拒否し、ホスト allowlist を定期再解決する - #1170

Merged
takaokouji merged 2 commits into
developfrom
topic/autopilot-1169
Sep 1, 2026
Merged

takaokouji merged 2 commits into
developfrom
topic/autopilot-1169

Conversation

@smalruby3-editor-bot

Copy link
Copy Markdown

概要

devcontainer からコンテナ内のブラウザで GUI を開くと読み込みが完了せず、ブラウザ検証が
成立しなかった(loadingState が LOADING_VM_NEW_DEFAULT のまま loader が消えない)。
egress allowlist が原因だったが、単に宛先を足すだけでは直らない問題が 2 つあったので両方直す。

Closes #1169

原因 1: 許可外が DROP なので「失敗」ではなく「ハング」になる

許可外への接続は無応答のままなので、ブラウザはタイムアウトまで待つ。実測 8 秒以上。
GUI は Monaco / Google ログイン / 解析タグを並行して待ち続けるため、本来なら「その機能だけ
壊れて他は動く」で済むところがページ全体の読み込みが終わらない。

→ REJECT に変更(TCP は RST、他は ICMP port-unreachable)。外に出るデータは無いので
遮断の強度は変わらない
まま、即座に失敗するようになる。

DROP(以前) REJECT(本 PR)
https://example.com 8.00s(タイムアウト張り付き) 0.012s
GUI を開いたとき 読み込みが完了しない その機能だけ失敗、エディタは使える

-P OUTPUT DROP は fail-closed の保険として残す(REJECT ルールの構築に失敗しても閉じる)。

原因 2: 起動時 1 回の DNS 解決では CDN に追随できない

emit_host() は dig を 1 回引いて ipset に入れるだけ。A レコードが変わる宛先は
数十分で stale になる
。実測(allowlist に追加した約 30 分後):

accounts.google.com: 000 (6.00s)   ← 追加済みなのにタイムアウト
apis.google.com:     000 (6.00s)
cdn.jsdelivr.net:    200 (0.07s)   ← Cloudflare の安定 IP なので生きている

「追加した直後は動くのに、しばらくすると再発する」という厄介な壊れ方で、deb.debian.org では
「再適用直後に apt を実行すること」という運用でのごまかしが入っていた(本 PR で不要になる)。

→ ホスト名由来の IP は期限付き(既定 1 時間)で入れ、背景ループが既定 5 分ごとに
引き直す
。CIDR レンジ(GitHub / AWS)は従来どおり無期限。消えた IP は期限切れで自然に
落ちるので、許可が広がりっぱなしにならない。

変更

ファイル 内容
.devcontainer/init-firewall.sh REJECT ルール追加 / ホスト由来エントリの TTL 化 / --refresh-loop モードと background 起動 / EXTRA_HOSTS 追加 / ローカル allowlist 解析を helper に集約 / 検証に「即座に失敗するか」を追加
.devcontainer/README.md REJECT の理由(比較表)・定期再解決・追加した宛先を記載

追加した宛先: cdn.jsdelivr.net(Monaco 本体)、accounts.google.com / apis.google.com
(Google ログイン)、deb.debian.org / security.debian.org(従来ローカルに当てていたもの)。
www.googletagmanager.com は入れない(解析用・開発に不要。REJECT で即失敗して先に進む)。

実機で確認したこと

適用してから実際に叩いて確認した。

目的の宛先が通る:

cdn.jsdelivr.net:    301 (0.076s)
accounts.google.com: 302 (0.131s)
apis.google.com:     301 (0.067s)
deb.debian.org:      200 (0.058s)

既存の宛先が壊れていない: api.github.com 200 / registry.npmjs.org 200 /
api.anthropic.com 404(到達はしている)/ AWS SSO も解決済み。

許可外は即座に失敗する: example.com → 000 (0.012s)、googletagmanager.com → 000 (0.012s)。
スクリプトの検証ステップにもこの観点を追加した(遅ければ warn を出す)。

再解決が stale を治す(今回の不具合そのものを人工的に再現して回復させた):

1. accounts.google.com の IP を ipset から削除 → 000 (0.003s)  ← 遮断された
2. 5 秒間隔の再解決ループを起動して 12 秒待つ  → 302 (0.135s)  ← 自動で回復

TTL の付き方: ホスト由来 = timeout 3580、AWS/GitHub の CIDR = timeout 0(無期限)。

IPv6 遮断・fail-closed は維持: ip6tables の OUTPUT は policy DROP のまま、
-P OUTPUT DROP も残置。

セキュリティ上の判断

cdn.jsdelivr.net は汎用 CDN なので allowlist としては広い部類に入る。それでも入れるのは
製品自身がランタイムで Monaco を取りに行っているためで、塞ぐとルビータブをコンテナ内で
一切検証できない。

より望ましいのは Monaco を自前ホストして jsdelivr 依存そのものを無くすことで、これは
学校ネットワークで CDN が塞がれるとルビータブが動かないという利用者側の問題にも効く。
本 PR のスコープ外なので別 Issue に切り出す(レビューで合意できれば起票する)。

レビュー観点

  • REJECT 化で遮断が緩んでいないか(出ていくデータは無い、という理解で合っているか)
  • 背景ループのライフサイクル(多重起動の防止・ipset 消失時の自動終了)
  • TTL の既定値(1 時間 / 再解決 5 分)が妥当か

smalruby3-editor-bot Bot and others added 2 commits September 1, 2026 06:06
コンテナ内のブラウザで GUI を開くと読み込みが完了せず、検証ができなかった。
原因は 2 つある。

1. 許可外が DROP なので「失敗」ではなく「ハング」になる。アプリは Monaco や
   Google ログインを待ち続け、ページ全体の読み込みが終わらない。REJECT に変えて
   即座に失敗させる(出ていくデータは無いので遮断の強度は変わらない)。
2. ホスト名を起動時に 1 回だけ解決していたので、CDN の A レコード変更に追随
   できない。実測で accounts.google.com は追加から数十分で切れていた。期限付きで
   入れて背景ループが定期的に引き直す。

あわせて製品がランタイムで必要とする cdn.jsdelivr.net(Monaco)と
accounts/apis.google.com(Google ログイン)、および運用でローカルに当てていた
Debian リポジトリを EXTRA_HOSTS に入れる。解析用の GTM は開発に不要なので入れない。

Refs #1169

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
pidfile だけを頼りにしていたので、pidfile を失うと孤児のループが残り、適用の
たびに増えていく。パターンでも掃除する(適用側のプロセスは --refresh-loop を
持たないので自分は殺さない)。

Refs #1169

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@smalruby3-editor-bot

Copy link
Copy Markdown
Author

🤖 敵対的レビュー(自己レビュー): Must 1 件(修正済み)/ Question 3 件 / FYI 2 件

観点は正確性・セキュリティ・再利用/簡潔性・.claude/rules 準拠・DoD 整合。変更が
.devcontainer/ なので .claude/rules/devpod-workflow.md(egress allowlist の必須要件・
「認証をホストに rw マウントしない」原則)を読んで照合した。

[Must] 再解決ループの孤児が掃除されない(修正済み 最新コミット)

多重起動を pidfile だけで防いでいたので、pidfile を失うと孤児のループが残り、適用のたびに
増えていく
。/tmp が消えた場合や手動起動した場合に起きる。パターンでも掃除するようにした
(適用側のプロセスは --refresh-loop を持たないので自分自身は殺さない)。

検証:

pidfile を失った状態:                    1 本
再適用後(孤児が掃除されていれば 1 本): 1 本   ← 増えない

なお最初の観測で「3 本走っている」と見えたのは私の手動起動の残骸と
pgrep が自分自身のコマンドラインにマッチしたためで、スクリプトの多重起動防止そのものは
(クリーンな状態で 3 回適用 → 1 本のまま)正しく動いていた。誤検知だった分は取り消す。

実機で確認したこと(DoD 照合)

DoD 結果
許可外が即座に失敗する ✅ example.com 000 (0.012s)、以前は 8.00s
jsdelivr / Google が通る ✅ 301 / 302 / 301(いずれも 0.1s 未満)
時間が経っても通り続ける ✅ IP を ipset から削除 → 遮断 → 再解決ループで自動回復(302)
example.com は通らない ✅ 000
既存の宛先が壊れていない ✅ github 200 / npm 200 / anthropic 到達 / AWS SSO 解決
IPv6 遮断・fail-closed 維持 ✅ ip6tables OUTPUT は policy DROP、-P OUTPUT DROP も残置
README 更新 ✅ REJECT の比較表・定期再解決・追加した宛先

TTL の付き方も確認: ホスト由来 = timeout 3580、GitHub/AWS の CIDR = timeout 0(無期限)。

Question(人間の判断が要る)

[Question] cdn.jsdelivr.net(汎用 CDN)を allowlist に入れる是非。 製品がランタイムで
Monaco を取りに行っている以上、塞ぐとルビータブをコンテナ内で検証できない。ただし汎用 CDN は
allowlist としては広く、理屈の上では任意の npm パッケージを取得できる。Monaco を自前ホスト
すれば依存ごと消せる
ので、そちらを本命にしたい(下記)。

[Question] Monaco 自前ホストを別 Issue に切るか。 これは開発環境だけの話ではなく、
学校ネットワークで CDN が塞がれているとルビータブが動かないという利用者側の問題でもある。
合意いただければ起票する。

[Question] TTL と再解決間隔の既定値(1 時間 / 5 分)。短くすれば追随は速いが DNS 問い合わせが
増える。実測では Google の A レコードが数十分で変わっていたので 5 分にしたが、もっと長くても
実害は無いかもしれない。

FYI

[FYI] REJECT 化で遮断は緩んでいない。 拒否応答(TCP RST / ICMP)はカーネルがローカルで
返すだけ
で、パケットは外に出ない。DROP との違いは「相手を待たせるか、即座に諦めさせるか」だけ。

[FYI] www.googletagmanager.com は意図的に入れていない。 解析用で開発には不要。REJECT に
したことで、落としてもページの読み込みは進む(これが今回の設計変更の効果そのもの)。

副次的な改善

deb.debian.org を EXTRA_HOSTS に入れたことで、これまで運用でごまかしていた
「firewall 再適用の直後に apt を実行すること」という制約が不要になった(定期再解決が
stale を防ぐため)。.claude/rules 側にこの回避策の記述は無いが、memory には残っているので
運用メモとしては更新が要る。

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

🚀 Preview deployed: https://smalruby.jp/smalruby3-editor/topic/autopilot-1169/

@takaokouji
takaokouji merged commit 31eba88 into develop Sep 1, 2026
9 checks passed
@takaokouji
takaokouji deleted the topic/autopilot-1169 branch September 1, 2026 12:43
github-actions Bot pushed a commit that referenced this pull request Sep 1, 2026
…lot-1169

fix(devcontainer): 許可外を即座に拒否し、ホスト allowlist を定期再解決する
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(devcontainer): egress allowlist で Monaco / Google が落ちてブラウザ検証ができない

1 participant