You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
#4033 以降に claude / codex から commit できなくなったので修正をしましょう #4059
Activity
KATO-Hiro commentedon Sep 19, 2026
Plan: devcontainer からsecretsを排除し、入れ子のサンドボックスを撤去する
経緯(深刻だったもの)
#4034 で導入したエージェントのサンドボックスの構成で、次の問題が分かった。
CONFIRM_API_URLは、compose がコンテナの環境変数として必須で注入していた。printenvを実行すれば誰でも読め、サンドボックスでは防げていなかった。git credential fillで取り出せた。nodeユーザーにパスワードなしのsudo(管理者権限)を与えていた。エージェントも設定を書き換えたり、ファイアウォールを外したりできた。方針: コンテナに秘密を置かず、外向きの通信は許可リストで絞る
コンテナ全体を境界とし、コンテナ内ではエージェントのサンドボックスを使わない。
本番の値はローカルに置かない
CONFIRM_API_URL(AtCoder アカウント連携の確認にだけ使う)は、既定でコンテナに入れない。連携確認のボタンが失敗するだけで、ほかの機能には影響しない。pnpm db:seed)で作る(admin、guest)。本物の値での確認が必要なときだけ、エージェントを使わずに行う。エージェントのサンドボックスはコンテナ内だけ無効にする
コンテナに残る秘密は、エージェント自身のログイン情報だけ
staging/mainはブランチ保護で守る。人間の push の手間は増やさない。ルールだけではエージェントによる push を技術的には防げない。外部通信の許可リスト
init-firewall.shを元にし、コンテナの起動のたびに適用する。[1]api.openai.com(Codex を API キーで使うときだけ必要)[5] は入れない。init-firewall.sh再実行は、既存のOUTPUT DROP規則を消した後に GitHub から IP 一覧を取得できず、通信不能になるため行わない。chatgpt.comで、ファイアウォールでは止められない。設定でだけ止めている。[3][7]却下した代替案
Rebuild 後の確認(2026-09-19)
ユーザーの確認結果:
curlによる許可先・非許可先への接続とsudo -n trueは、それぞれ期待どおりに成功・失敗した。pnpm installとpnpm test:e2eは成功した。実際の
git commit/git pushは、このレビュー前の確認項目から外した。coderabbit review --plainも今回の動作確認には含めない。残るリスク
docker compose upでは、ファイアウォールが効かない。この起動時の例外は、ローカル開発の運用として受け入れる。CONFIRM_API_URLが残っていると再注入される(警告は出る)。GitHub のトークン共有の停止も各自のホスト設定に依存する。新しいメンバーにも確認を依頼する。教訓
出典
KATO-Hiro commentedon Sep 19, 2026
追加
CodeRabbit のレビューを受けて、DNS の許可を Docker の組み込み DNS(
127.0.0.11)宛てに限定し、Claude の mount 元を~/.claude-devcontainer/AtCoderNoviStepsに分離した。ホストの
~/.claudeの memory・会話履歴は引き継がない。KATO-Hiro commentedon Sep 19, 2026
ファイアウォール再実行の安全化
概要
devcontainer の起動待ちが長時間続き、コンテナ破棄後は起動できた。
確認できた欠陥は、再実行時の
iptables -Fが許可規則を消してOUTPUT DROPだけを残し、GitHub メタデータ取得を妨げることである。ログがなく、実際の停止箇所は未特定。専用チェーン・一時 ipset の swap・全外部取得への期限・双方向の自己検証を導入した。
判断
curl/digに期限がある」ことだけを静的に検査する。-N || -Fで直し、Rebuild で戻る不具合(host_networkの固定、ジャンプ規則の重複、IPv4 ジャンプ追加後・IPv6 ジャンプ追加前の中断で再実行が完了済みと判定し IPv6 が素通しになること)は見送る。最後の件は該当区間が ip6tables 成功後の-I/-Pだけで発生率が低い。コンテナ再起動で netns が作り直され規則も消えると想定しているが未検証。却下
OUTPUT ACCEPTにする。一時的に許可範囲が広がる。waitFor: postStartCommandを外す。設定失敗を隠す。iptables-restoreによる宣言化。書き直し量に対し、要件(実態が分かること)に効かない。補足: チェーン
NOVISTEPS_*は変数ではなく iptables のチェーン名である。チェーンは上から照合される規則のリストで、-Nでカーネル内に作られスクリプト終了後も残る。組み込みのINPUT/OUTPUTからのジャンプで呼ばれる。中身はホスト側から
docker compose exec -u root web iptables -L NOVISTEPS_OUTPUT -n -v --line-numbersで確認できる(node の sudo は init-firewall.sh のみ許可)。未解決
postStartCommandまでの窓は無防備のまま。Rebuild 後の確認(2026-09-19 実施・合格)
Firewall check OK4 行とFirewall configuredが出る。diff /usr/local/bin/init-firewall.sh .devcontainer/init-firewall.shが差分なし。curl -sS -o /dev/null --max-time 8 https://example.com; echo $?が7。sudo /usr/local/bin/init-firewall.shの再実行がFirewall configuredまで進む。ip -6 routeにデフォルト経路がなければ、IPv6 素通しの影響は実質ない。docker compose exec -u root web ...):iptables -S OUTPUTに-P OUTPUT DROPと、再実行後も 1 行だけの-j NOVISTEPS_OUTPUT。ip6tables -S OUTPUTに-P OUTPUT DROPと-j NOVISTEPS_IPV6。残る任意確認
iptables -N NETNS_MARKER後にコンテナを再起動し、iptables -L NETNS_MARKERが失敗すれば netns 再作成の想定を確認済みとして「判断」の「未検証」を外す。残っていれば IPv6 素通しの見送り判断を見直す。KATO-Hiro commentedon Sep 20, 2026
Devcontainer ファイアウォールの許可範囲と再実行の修正
概要
Compose の
webからdb:5432への通信を残し、Docker ブリッジ全体への許可を削除する。IPv4 と IPv6 の設定状態を確認し、途中で止まった初期化を再実行できるようにする。設計
ファイアウォール設定は
.devcontainer/init-firewall.shの単一責務とする。既存のユーティリティやサービスに同等の処理はない。Docker DNS でdbの IPv4 アドレスを取得し、TCP 5432 だけ許可する。公開ポート 5173 と 5555 への着信のみ許可する。初期化済み判定は設けず、再実行時も両系統のチェーンを作り直して欠落を補う。再構築前に DROP ポリシーを設定し、途中失敗で許可的な IPv6 OUTPUT を残さない。完全な
iptables-save/restoreによる状態保存は、ローカル開発コンテナには複雑すぎるため採用しない。ホスト側の宛先を許可する必要は Compose の通信契約から確認できないため採用しない。フェーズ
init-firewall.shの許可範囲と再実行条件を検証するテストを先に追加する。CONTRIBUTING.mdの通信範囲、Rebuild、公開ポートの説明を現行設定に合わせる。pnpm test:unit、変更ファイルの Prettier、pnpm lint、pnpm check、git diff --checkを実行する。Rebuild 後の確認
以下のコマンドはホスト側のリポジトリで実行する。
webとdbが起動してから確認する。全項目を 2026-09-20 に確認した。手順 1・2 の再実行・5 の DB 接続・6 は
webコンテナ内(nodeユーザー)から、手順 2 のpostStartCommand・3・4・5 のホストからの 5173 番ポートはホスト側から確認した。nodeユーザーのsudoは/usr/local/bin/init-firewall.shだけが NOPASSWD で許可されており、iptablesの参照はできないため、残りはホスト側で実行する。docker compose exec web cmp -s .devcontainer/init-firewall.sh /usr/local/bin/init-firewall.shが成功すること。イメージ内の実行ファイルが修正版であることを確かめる。postStartCommandがFirewall configuredで終了すること。[x] 続けて
docker compose exec web sudo /usr/local/bin/init-firewall.shをもう一度実行し、再実行でも成功することを確かめる(自己チェック 4 件が通りFirewall configuredで終了)。docker compose exec -u root web iptables -S NOVISTEPS_OUTPUTで、dbの IPv4 アドレス宛て TCP 5432 の許可があり、Docker サブネット全体またはホスト側への無条件の許可がないことを確かめる。docker compose exec -u root web iptables -S NOVISTEPS_INPUTでは、着信の許可が公開ポート 5173 と 5555、および既存接続とループバックに限られることを確かめる。docker compose exec -u root web iptables -S OUTPUTとdocker compose exec -u root web ip6tables -S OUTPUTで、それぞれのNOVISTEPS_*チェーンへのジャンプが1つずつあり、両方の OUTPUT ポリシーが DROP であることを確かめる。再実行後もジャンプが増えないことを確認する。手順 2 の再実行を済ませてあるので、ここでジャンプが重複していなければ冪等性の確認を兼ねる。docker compose exec web node -e "const socket = require('node:net').connect(5432, 'db'); socket.on('connect', () => { console.log('DB TCP OK'); socket.destroy(); }); socket.on('error', (error) => { console.error(error); process.exitCode = 1; });"がDB TCP OKと表示することを確かめる(手順 2 の再実行の前後どちらも接続できた)。[x] アプリを起動している場合はホストから 5173 番ポートにアクセスできることも確認する。
docker compose exec web curl -I --connect-timeout 5 --max-time 8 https://api.github.comが通信でき、docker compose exec web curl -sS --connect-timeout 5 --max-time 8 https://example.comは接続拒否で終了することを確かめる。これは初期化スクリプト自身の許可先・遮断先チェックの再確認である。Merge pull request #4061 from AtCoder-NoviSteps/#4059