OpenCode コンテナ内で Docker を直接使えるようにする(docker-mcp の廃止) #4

Closed
opened 2026-08-26 01:03:42 +09:00 by bot-genji1024 · 4 comments
Collaborator

目的・背景

現状、OpenCode コンテナから Docker を操作する手段は docker-mcp(MCP サーバー経由)のみです。docker-mcp はビルドコンテキストが /workspace 配下に限定される制約があり、OpenCode の作業ディレクトリ(${HOME}/repos/...)とパスが一致しないため、docker build が resolves outside of /workspace; refusing to build で失敗します。これが Docker 動作確認の不十分さ(private-opencode-server PR #4 のレビュー指摘)の根本原因です。

本イシューでは、docker-mcp を廃止し、OpenCode コンテナに docker CLI を導入してホストの Docker daemon を直接利用できるようにします。

現状(調査結果)

  • OpenCode コンテナ: node:26.3.0-trixie-slim ベース。docker CLI なし。
  • docker-mcp: docker:27-dind ベース + privileged: true。コンテナ内 dockerd を起動し、Python MCP サーバー(server.py)が docker CLI を subprocess 実行。
  • OpenCode は DOCKER_MCP_URL=http://docker-mcp:3004/mcp を環境変数で保持し、opencode.jsonc の docker MCP(remote)経由で操作。
  • /var/run/docker.sock のマウントは全サービスで無し。privileged は docker-mcp のみ。

実装方針(推奨)

Docker outside of Docker(DooD)方式 + docker-mcp 廃止

  • ホストの /var/run/docker.sock を OpenCode コンテナへ bind マウントし、OpenCode に docker CLI を追加する。
  • OpenCode は ${HOME}/repos/ をホストと同一パスでマウント済みのため、コンテナ内で実行する docker build のビルドコンテキストをホスト daemon が同一パスで解決でき、旧 docker-mcp の /workspace パス不一致が解消される。

(代替案: OpenCode を privileged にして DinD を持ち込む — 却下。重く、ビルドコンテキストのパス問題も残るため。)

変更内容(予定)

  1. dockerfiles/opencode/Dockerfile
    • docker CLI を追加(例: apt-get install -y docker.io、または Docker 公式リポジトリから docker-ce-cli)。
    • docker compose plugin の追加も検討(ホスト側で compose を使用しない場合は不要)。
  2. compose.yaml
    • docker-mcp サービス定義を削除。
    • opencode サービスの depends_on から docker-mcp を削除。
    • opencode サービスに /var/run/docker.sock の bind マウントを追加。
    • DOCKER_MCP_URL 環境変数を削除。
  3. .config/opencode/opencode.jsonc
    • docker MCP 定義を削除。
  4. .config/opencode/agents/verifier.md(および docker MCP を参照するエージェント定義)
    • docker_* MCP ツール参照を、bash での docker CLI 実行(許可リスト更新含む)に変更。
  5. dockerfiles/docker-mcp/ ディレクトリを削除。
  6. README.md
    • サービス一覧・説明から docker-mcp を削除し、Docker 直接利用の記述へ更新。

セキュリティ上の注意

  • /var/run/docker.sock のマウントはホスト root 相当の権限をコンテナに与えます。一方、現在の docker-mcp の privileged: true を廃止できるため、スタック全体の privileged 面は縮小します。
  • OpenCode コンテナは既にホストの広範なディレクトリ(~/.agents / ~/.config/opencode / ~/repos 等)をマウント済みであり、同等の信頼を前提とした構成です。ホスト daemon 上の他サービス(forgejo-mcp 等)を誤って操作しないよう、運用上の注意を README に明記する予定です。

検証方法

  1. docker compose -f compose.yaml build opencode(CLI 追加を反映)。
  2. docker compose -f compose.yaml run --rm --service-ports opencode で起動。
  3. コンテナ内で docker version / docker ps が成功し、ホスト daemon が見えることを確認。
  4. コンテナ内で docker run --rm hello-world が成功することを確認。
  5. ${HOME}/repos/... 配下の Dockerfile をビルドコンテキストに指定した docker build が成功すること(旧 docker-mcp のパス制約の解消確認)。
  6. docker compose ps に docker-mcp が現れないことを確認。

関連

  • 起因: private-opencode-server PR #4 のレビュー指摘(コメント c368)
  • 本イシューの方針を承認いただいた後に実装着手します。
## 目的・背景 現状、OpenCode コンテナから Docker を操作する手段は docker-mcp(MCP サーバー経由)のみです。docker-mcp はビルドコンテキストが `/workspace` 配下に限定される制約があり、OpenCode の作業ディレクトリ(`${HOME}/repos/...`)とパスが一致しないため、`docker build` が `resolves outside of /workspace; refusing to build` で失敗します。これが Docker 動作確認の不十分さ(private-opencode-server PR #4 のレビュー指摘)の根本原因です。 本イシューでは、docker-mcp を廃止し、OpenCode コンテナに docker CLI を導入してホストの Docker daemon を直接利用できるようにします。 ## 現状(調査結果) - OpenCode コンテナ: `node:26.3.0-trixie-slim` ベース。docker CLI なし。 - docker-mcp: `docker:27-dind` ベース + `privileged: true`。コンテナ内 dockerd を起動し、Python MCP サーバー(`server.py`)が docker CLI を subprocess 実行。 - OpenCode は `DOCKER_MCP_URL=http://docker-mcp:3004/mcp` を環境変数で保持し、`opencode.jsonc` の `docker` MCP(remote)経由で操作。 - `/var/run/docker.sock` のマウントは全サービスで無し。`privileged` は docker-mcp のみ。 ## 実装方針(推奨) **Docker outside of Docker(DooD)方式 + docker-mcp 廃止** - ホストの `/var/run/docker.sock` を OpenCode コンテナへ bind マウントし、OpenCode に docker CLI を追加する。 - OpenCode は `${HOME}/repos/` をホストと同一パスでマウント済みのため、コンテナ内で実行する `docker build` のビルドコンテキストをホスト daemon が同一パスで解決でき、旧 docker-mcp の `/workspace` パス不一致が解消される。 (代替案: OpenCode を privileged にして DinD を持ち込む — 却下。重く、ビルドコンテキストのパス問題も残るため。) ## 変更内容(予定) 1. `dockerfiles/opencode/Dockerfile` - docker CLI を追加(例: `apt-get install -y docker.io`、または Docker 公式リポジトリから docker-ce-cli)。 - docker compose plugin の追加も検討(ホスト側で compose を使用しない場合は不要)。 2. `compose.yaml` - `docker-mcp` サービス定義を削除。 - `opencode` サービスの `depends_on` から `docker-mcp` を削除。 - `opencode` サービスに `/var/run/docker.sock` の bind マウントを追加。 - `DOCKER_MCP_URL` 環境変数を削除。 3. `.config/opencode/opencode.jsonc` - `docker` MCP 定義を削除。 4. `.config/opencode/agents/verifier.md`(および docker MCP を参照するエージェント定義) - `docker_*` MCP ツール参照を、bash での docker CLI 実行(許可リスト更新含む)に変更。 5. `dockerfiles/docker-mcp/` ディレクトリを削除。 6. `README.md` - サービス一覧・説明から docker-mcp を削除し、Docker 直接利用の記述へ更新。 ## セキュリティ上の注意 - `/var/run/docker.sock` のマウントはホスト root 相当の権限をコンテナに与えます。一方、現在の docker-mcp の `privileged: true` を廃止できるため、スタック全体の privileged 面は縮小します。 - OpenCode コンテナは既にホストの広範なディレクトリ(`~/.agents` / `~/.config/opencode` / `~/repos` 等)をマウント済みであり、同等の信頼を前提とした構成です。ホスト daemon 上の他サービス(forgejo-mcp 等)を誤って操作しないよう、運用上の注意を README に明記する予定です。 ## 検証方法 1. `docker compose -f compose.yaml build opencode`(CLI 追加を反映)。 2. `docker compose -f compose.yaml run --rm --service-ports opencode` で起動。 3. コンテナ内で `docker version` / `docker ps` が成功し、ホスト daemon が見えることを確認。 4. コンテナ内で `docker run --rm hello-world` が成功することを確認。 5. `${HOME}/repos/...` 配下の Dockerfile をビルドコンテキストに指定した `docker build` が成功すること(旧 docker-mcp のパス制約の解消確認)。 6. `docker compose ps` に docker-mcp が現れないことを確認。 ## 関連 - 起因: private-opencode-server PR #4 のレビュー指摘(コメント c368) - 本イシューの方針を承認いただいた後に実装着手します。
Owner

@bot-genji1024

(代替案: OpenCode を privileged にして DinD を持ち込む — 却下。重く、ビルドコンテキストのパス問題も残るため。)

これについて詳しく説明してください。安全性の観点を重視して、DinD の選択肢を排除せずに再検討してください。

@bot-genji1024 > (代替案: OpenCode を privileged にして DinD を持ち込む — 却下。重く、ビルドコンテキストのパス問題も残るため。) これについて詳しく説明してください。安全性の観点を重視して、DinD の選択肢を排除せずに再検討してください。
Author
Collaborator

@genji1024 ご要請いただいた「DinD の選択肢を排除せずに再検討」について、安全性の観点を中心に詳しく説明します。

現状の整理

まず重要な前提として、現在の docker-mcp は既に DinD で動いています。compose.yaml で docker-mcp サービスは docker:27-dind イメージ + privileged: true で起動し、コンテナ内で dockerd を実行しています。つまり現状のスタックには既に privileged コンテナが1つ常駐しています。

Issue #4 で検討していたのは「この docker-mcp(DinD)を廃止して何に置き換えるか」であり、選択肢は以下の2つです:

  • 案A(DooD): docker-mcp を廃止し、OpenCode コンテナに docker CLI を追加 + ホストの /var/run/docker.sock をマウント
  • 案B(DinD): docker-mcp を廃止し、OpenCode コンテナ自体を privileged: true にしてコンテナ内で dockerd を起動

案A(DooD)と案B(DinD)の詳細比較

セキュリティ面

項目 案A(DooD: socket マウント) 案B(DinD: OpenCode privileged)
privileged モード 不要。コンテナは通常権限で動作 必要。カーネル機能・デバイス・マウント等へのフルアクセス
ホストアクセスの経路 Docker socket 経由でホスト root 相当の操作が可能 privileged 経由でカーネル直接アクセスが可能
他サービスへの影響リスク 高い。ホスト daemon を共有するため、誤って forgejo/postgres/minio 等のコンテナを操作・停止しうる 低い。ネストされた独立 daemon で動くため、ホスト上の他コンテナとは隔離される
攻撃面の性質 Docker API へのアクセス権 = 実質 host root カーネル機能へのフルアクセス = 実質 host root
privileged 廃止 docker-mcp の privileged: true を廃止でき、スタック全体の privileged 面はゼロになる docker-mcp の privileged は廃止できるが、OpenCode が privileged になるため privileged コンテナは1つ残る(現状と同じ)

両者とも「コンテナ内プロセスが host root 相当の権限を得る」という意味で最悪ケースの深刻度は同等ですが、攻撃経路と爆発半径が異なります:

  • 案Aは privileged 不要という利点がある一方、ホスト daemon 共有による他サービスへの誤操作リスク(blast radius が広い)がある
  • 案Bは privileged が必要という欠点がある一方、独立 daemon による隔離(他サービスへの影響なし)という利点がある

ビルドコンテキストのパス問題

Issue 本文では案Bについて「ビルドコンテキストのパス問題も残る」と却下しましたが、この論拠は正確ではありませんでした。検証の結果:

  • 案A: OpenCode の ${HOME}/repos がホストと同一パスでマウントされているため、ホスト daemon が同一パスでビルドコンテキストを解決できる → パス問題は解消する
  • 案B: OpenCode の ${HOME}/repos をネストコンテナにも同一パスでマウントすれば、ネスト daemon が同一パスで解決できる → パス問題は同様に解消しうる

つまりパス問題は案A・案Bいずれでも解決可能であり、却下の根拠としては成立しませんでした。訂正してお詫びします。

運用面

項目 案A(DooD) 案B(DinD)
リソース消費 軽い(docker CLI のみ追加) 重い(ネスト dockerd の起動・ストレージ層の二重化)
イメージサイズ docker CLI 追加のみ(+数MB) dockerd + CLI(+数百MB)
起動時間 変化なし dockerd 起動分だけ遅延
デバッグ ホスト daemon の状態をそのまま見える ネスト daemon の状態を別途確認する必要あり
Docker API バージョン差異 ホスト daemon に追従 ネスト dockerd のバージョンに依存

安全性重視での再検討結果

安全性を第一軸として再検討した結果、両者一長一短であり、単一の優位性では決定できません。以下の判断基準を提示します:

案A(DooD)を選ぶべき場合

  • 「privileged コンテナをスタックから完全に排除したい」が最優先
  • OpenCode が他サービスのコンテナを誤操作しないよう運用で担保できる(README 明記・運用ルール徹底)
  • リソース消費を最小化したい

案B(DinD)を選ぶべき場合

  • 「他サービス(forgejo/postgres/minio 等)への誤操作リスクを技術的に排除したい」が最優先
  • privileged コンテナが1つ残ることを許容できる(現状も docker-mcp が privileged なので、実害は増えない)
  • リソース消費の増加を許容できる

私の見解

現状既に docker-mcp が privileged で常駐しているため、「privileged をゼロにできる」案Aの利点は実利的です。他サービス誤操作リスクについては、OpenCode が既にホストの広範なディレクトリをマウントしており、同等の信頼を前提としているため、運用上の注意で担保可能と判断します。

ただし、genji1024 さんが隔離性(他サービスへの誤操作排除)を優先される場合は案Bも妥当な選択です。その場合、パス問題は解消可能であるため、Issue 本文の却下理由は撤回します。

どちらの案を採用するかご指示ください。承認いただいた案で実装を進めます。

@genji1024 ご要請いただいた「DinD の選択肢を排除せずに再検討」について、安全性の観点を中心に詳しく説明します。 ## 現状の整理 まず重要な前提として、**現在の docker-mcp は既に DinD で動いています**。`compose.yaml` で `docker-mcp` サービスは `docker:27-dind` イメージ + `privileged: true` で起動し、コンテナ内で `dockerd` を実行しています。つまり現状のスタックには既に privileged コンテナが1つ常駐しています。 Issue #4 で検討していたのは「この docker-mcp(DinD)を廃止して何に置き換えるか」であり、選択肢は以下の2つです: - **案A(DooD)**: docker-mcp を廃止し、OpenCode コンテナに docker CLI を追加 + ホストの `/var/run/docker.sock` をマウント - **案B(DinD)**: docker-mcp を廃止し、OpenCode コンテナ自体を `privileged: true` にしてコンテナ内で `dockerd` を起動 ## 案A(DooD)と案B(DinD)の詳細比較 ### セキュリティ面 | 項目 | 案A(DooD: socket マウント) | 案B(DinD: OpenCode privileged) | |---|---|---| | privileged モード | **不要**。コンテナは通常権限で動作 | **必要**。カーネル機能・デバイス・マウント等へのフルアクセス | | ホストアクセスの経路 | Docker socket 経由でホスト root 相当の操作が可能 | privileged 経由でカーネル直接アクセスが可能 | | 他サービスへの影響リスク | **高い**。ホスト daemon を共有するため、誤って forgejo/postgres/minio 等のコンテナを操作・停止しうる | **低い**。ネストされた独立 daemon で動くため、ホスト上の他コンテナとは隔離される | | 攻撃面の性質 | Docker API へのアクセス権 = 実質 host root | カーネル機能へのフルアクセス = 実質 host root | | privileged 廃止 | docker-mcp の `privileged: true` を廃止でき、スタック全体の privileged 面は**ゼロ**になる | docker-mcp の privileged は廃止できるが、OpenCode が privileged になるため privileged コンテナは**1つ残る**(現状と同じ) | 両者とも「コンテナ内プロセスが host root 相当の権限を得る」という意味で最悪ケースの深刻度は同等ですが、**攻撃経路と爆発半径が異なります**: - 案Aは privileged 不要という利点がある一方、ホスト daemon 共有による他サービスへの誤操作リスク(blast radius が広い)がある - 案Bは privileged が必要という欠点がある一方、独立 daemon による隔離(他サービスへの影響なし)という利点がある ### ビルドコンテキストのパス問題 Issue 本文では案Bについて「ビルドコンテキストのパス問題も残る」と却下しましたが、この論拠は**正確ではありませんでした**。検証の結果: - 案A: OpenCode の `${HOME}/repos` がホストと同一パスでマウントされているため、ホスト daemon が同一パスでビルドコンテキストを解決できる → **パス問題は解消する** - 案B: OpenCode の `${HOME}/repos` をネストコンテナにも同一パスでマウントすれば、ネスト daemon が同一パスで解決できる → **パス問題は同様に解消しうる** つまりパス問題は案A・案Bいずれでも解決可能であり、却下の根拠としては成立しませんでした。訂正してお詫びします。 ### 運用面 | 項目 | 案A(DooD) | 案B(DinD) | |---|---|---| | リソース消費 | 軽い(docker CLI のみ追加) | 重い(ネスト dockerd の起動・ストレージ層の二重化) | | イメージサイズ | docker CLI 追加のみ(+数MB) | dockerd + CLI(+数百MB) | | 起動時間 | 変化なし | dockerd 起動分だけ遅延 | | デバッグ | ホスト daemon の状態をそのまま見える | ネスト daemon の状態を別途確認する必要あり | | Docker API バージョン差異 | ホスト daemon に追従 | ネスト dockerd のバージョンに依存 | ## 安全性重視での再検討結果 安全性を第一軸として再検討した結果、**両者一長一短であり、単一の優位性では決定できません**。以下の判断基準を提示します: ### 案A(DooD)を選ぶべき場合 - 「privileged コンテナをスタックから完全に排除したい」が最優先 - OpenCode が他サービスのコンテナを誤操作しないよう運用で担保できる(README 明記・運用ルール徹底) - リソース消費を最小化したい ### 案B(DinD)を選ぶべき場合 - 「他サービス(forgejo/postgres/minio 等)への誤操作リスクを技術的に排除したい」が最優先 - privileged コンテナが1つ残ることを許容できる(現状も docker-mcp が privileged なので、実害は増えない) - リソース消費の増加を許容できる ### 私の見解 現状既に docker-mcp が privileged で常駐しているため、「privileged をゼロにできる」案Aの利点は実利的です。他サービス誤操作リスクについては、OpenCode が既にホストの広範なディレクトリをマウントしており、同等の信頼を前提としているため、運用上の注意で担保可能と判断します。 ただし、**genji1024 さんが隔離性(他サービスへの誤操作排除)を優先される場合は案Bも妥当な選択です**。その場合、パス問題は解消可能であるため、Issue 本文の却下理由は撤回します。 どちらの案を採用するかご指示ください。承認いただいた案で実装を進めます。
Owner

@bot-genji1024 隔離性を優先して案Bを採用します。実装と PR 作成をお願いします。実環境では Rootless Docker で運用していることに気を付けてください。 docker-mcp は問題なく動いているので、そちらと同じ構成にしておくのが安全だと思います。

@bot-genji1024 隔離性を優先して案Bを採用します。実装と PR 作成をお願いします。実環境では Rootless Docker で運用していることに気を付けてください。 `docker-mcp` は問題なく動いているので、そちらと同じ構成にしておくのが安全だと思います。
Author
Collaborator

進捗報告

Issue #4 の実装を完了し、PR を作成しました。

  • PR: #5 #5
  • 案B(DinD)を実装: OpenCode コンテナのベースを docker:27-dind に変更し、docker-mcp を廃止しました
  • 旧 docker-mcp と同じ構成(docker:27-dind + privileged: true)を踏襲しています
  • 静的検証は PASS、Docker 実動作確認は環境制約により未実施(実環境での確認をお願いします)

@genji1024 レビューをお願いいたします

## 進捗報告 Issue #4 の実装を完了し、PR を作成しました。 - PR: #5 https://forgejo.genji1024.com/genji1024/containerized-agents/pulls/5 - 案B(DinD)を実装: OpenCode コンテナのベースを `docker:27-dind` に変更し、docker-mcp を廃止しました - 旧 docker-mcp と同じ構成(`docker:27-dind` + `privileged: true`)を踏襲しています - 静的検証は PASS、Docker 実動作確認は環境制約により未実施(実環境での確認をお願いします) @genji1024 レビューをお願いいたします
Sign in to join this conversation.
No labels
enhancement
No milestone
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
genji1024/containerized-agents#4
No description provided.