Repository navigation
fix(devcontainer): 許可外を即座に拒否し、ホスト allowlist を定期再解決する - #1170
Conversation
コンテナ内のブラウザで 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>
|
🤖 敵対的レビュー(自己レビュー): Must 1 件(修正済み)/ Question 3 件 / FYI 2 件 観点は正確性・セキュリティ・再利用/簡潔性・ [Must] 再解決ループの孤児が掃除されない(修正済み 最新コミット)多重起動を pidfile だけで防いでいたので、pidfile を失うと孤児のループが残り、適用のたびに 検証: なお最初の観測で「3 本走っている」と見えたのは私の手動起動の残骸と 実機で確認したこと(DoD 照合)
TTL の付き方も確認: ホスト由来 = Question(人間の判断が要る)[Question] [Question] Monaco 自前ホストを別 Issue に切るか。 これは開発環境だけの話ではなく、 [Question] TTL と再解決間隔の既定値(1 時間 / 5 分)。短くすれば追随は速いが DNS 問い合わせが FYI[FYI] REJECT 化で遮断は緩んでいない。 拒否応答(TCP RST / ICMP)はカーネルがローカルで [FYI] 副次的な改善
|
|
🚀 Preview deployed: https://smalruby.jp/smalruby3-editor/topic/autopilot-1169/ |
…lot-1169 fix(devcontainer): 許可外を即座に拒否し、ホスト allowlist を定期再解決する
概要
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)。外に出るデータは無いので
遮断の強度は変わらないまま、即座に失敗するようになる。
https://example.com-P OUTPUT DROPは fail-closed の保険として残す(REJECT ルールの構築に失敗しても閉じる)。原因 2: 起動時 1 回の DNS 解決では CDN に追随できない
emit_host()はdigを 1 回引いて ipset に入れるだけ。A レコードが変わる宛先は数十分で stale になる。実測(allowlist に追加した約 30 分後):
「追加した直後は動くのに、しばらくすると再発する」という厄介な壊れ方で、
deb.debian.orgでは「再適用直後に apt を実行すること」という運用でのごまかしが入っていた(本 PR で不要になる)。
→ ホスト名由来の IP は期限付き(既定 1 時間)で入れ、背景ループが既定 5 分ごとに
引き直す。CIDR レンジ(GitHub / AWS)は従来どおり無期限。消えた IP は期限切れで自然に
落ちるので、許可が広がりっぱなしにならない。
変更
.devcontainer/init-firewall.sh--refresh-loopモードと background 起動 /EXTRA_HOSTS追加 / ローカル allowlist 解析を helper に集約 / 検証に「即座に失敗するか」を追加.devcontainer/README.md追加した宛先:
cdn.jsdelivr.net(Monaco 本体)、accounts.google.com/apis.google.com(Google ログイン)、
deb.debian.org/security.debian.org(従来ローカルに当てていたもの)。www.googletagmanager.comは入れない(解析用・開発に不要。REJECT で即失敗して先に進む)。実機で確認したこと
適用してから実際に叩いて確認した。
目的の宛先が通る:
既存の宛先が壊れていない:
api.github.com200 /registry.npmjs.org200 /api.anthropic.com404(到達はしている)/ AWS SSO も解決済み。許可外は即座に失敗する:
example.com→ 000 (0.012s)、googletagmanager.com→ 000 (0.012s)。スクリプトの検証ステップにもこの観点を追加した(遅ければ warn を出す)。
再解決が stale を治す(今回の不具合そのものを人工的に再現して回復させた):
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 に切り出す(レビューで合意できれば起票する)。
レビュー観点