1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Redmine と MCP ツールの権限管理設計 ― 実装編

1
Posted at

はじめに

前編では、Redmine と MCP を組み合わせる場合、利用者ごとではなく、

権限・責務・Trust Boundary ごとに MCP Server を分離する

という設計を整理した。

実装編では、この設計を Linux サーバー上へ実際に配置する方法を考える。

想定する最終構成は次のようなものだ。

ChatGPT / AI Agent
        │
        ├─ Reader MCP
        │    └─ Reader API Key
        │
        ├─ Writer MCP
        │    └─ Writer API Key
        │
        ├─ Agent MCP
        │    └─ Agent API Key
        │
        └─ Admin MCP
             └─ Admin API Key
                  │
                  ↓
               Redmine

ただし、最初から4系統すべてを作る必要はない。

少人数運用では、

Reader
Writer

の2系統から始め、

AI Agent Runner を導入した段階で、

Agent

を追加する方が運用しやすい。


1. 実装上の基本原則

今回の構成では、認可を一箇所だけに依存させない。

次の複数層で制御する。

ChatGPT / Agent
      ↓
接続可能な MCP
      ↓
MCP Server の Tool / mode
      ↓
Redmine API Key
      ↓
Redmine Role
      ↓
Project Permission

このうち重要なのは、Redmine を最終的な認可境界として残すことだ。

MCP Server 側で操作を制限していても、Redmine API Key に不要な権限を与えない。

逆に Redmine 側が read-only でも、MCP 側で不要な write Tool を公開しない。

つまり、

MCP は操作面を制限し、Redmine は権限面を制限する。


2. MCP Server 専用 OS ユーザーを作る

Redmine と同じ Linux サーバーで MCP Tunnel Client を動かす場合でも、Redmine 本体と同じ OS ユーザーでは起動しない方がよい。

例えば、

redmine
redmine-mcp

を分離する。

MCP Server は Redmine REST API を経由して操作するため、通常は Redmine の Rails アプリケーションディレクトリや DB ファイルへ直接アクセスする必要はない。

理想的な境界は次の通りだ。

redmine-mcp user
├─ Tunnel Client 実行
├─ MCP Server 実行
├─ MCP用設定ファイル読取
└─ Redmine REST API 接続

アクセス不要
├─ Redmine DB
├─ Redmine secret
├─ Rails application files
└─ Redmine OS service

MCP Server が侵害された場合でも、Redmine 本体へ OS レベルで横移動しにくくなる。


3. ディレクトリを分離する

具体的なパスは環境依存なので固定する必要はないが、責務としては次のように分離するとよい。

MCP runtime
├─ executable
├─ runtime working directory
└─ logs / temporary files

MCP secrets
├─ control plane credential
├─ tunnel identifier
└─ Redmine API credential

systemd
└─ service definitions

例えば一例として、

/opt/redmine-mcp/
/etc/redmine-mcp/
/etc/systemd/system/

のように分けられる。

ここで重要なのはディレクトリ名ではなく、

実行物と秘密情報を分離すること

である。


4. API Key を権限ごとに分離する

最も重要なのが API Key の分離だ。

悪い例は、

Reader MCP ─┐
Writer MCP ─┼─ 同じ管理者API Key
Agent MCP ──┘

である。

これでは MCP Server を分けても、Redmine 側の権限境界が存在しない。

代わりに、

Reader MCP
└─ Reader用API Key

Writer MCP
└─ Writer用API Key

Agent MCP
└─ Agent用API Key

とする。

可能であれば、それぞれ Redmine 上でも別ユーザーとする。

mcp-reader
mcp-writer
mcp-agent

こうすると Redmine の journal や監査情報から、

どの権限主体が変更したか

を区別しやすくなる。


5. Reader Role を作る

Reader は最も単純である。

成立させる責務は、

ChatGPT が Redmine の情報を取得できるが、状態変更はできない

ことである。

Reader の Redmine Role には、運用上必要な閲覧権限だけを与える。

概念的には、

Reader Role

Allowed
├─ Project 閲覧
├─ Ticket 閲覧
├─ Journal 閲覧
├─ Version 閲覧
└─ Wiki 閲覧

Denied
├─ Ticket 作成
├─ Ticket 更新
├─ コメント追加
├─ Status変更
├─ Version変更
├─ Member変更
└─ Project設定変更

となる。

さらに MCP Server 自体も read-only mode で起動する。

MCP Layer
    read-only

+

Redmine Layer
    read-only role

この二重化が重要である。


6. Writer Role を作る

Writer は、人間が ChatGPT を通して通常のチケット操作を行うための権限である。

例えば、

Allowed
├─ Ticket 閲覧
├─ Ticket 作成
├─ Ticket 更新
├─ コメント追加
├─ Status変更
├─ 関連Ticket操作
└─ 必要なVersion参照

Denied
├─ Project削除
├─ Member管理
├─ Role管理
├─ System設定
└─ 不要な管理操作

のような境界を作る。

ここで重要なのは、

Writer = Administrator

にしないことだ。

ChatGPT がチケット作成・更新を行うだけなら、Redmine 全体を管理する権限は不要である。


7. Agent Role は Writer と分離する

AI Agent Runner を導入するときは、Writer の API Key を流用しない。

Agent 専用 Role を作る。

例えば Agent Runner の責務が、

Ready for Agent の Ticket を取得
↓
Agent Brief を取得
↓
実装開始を記録
↓
結果を Journal へ記録
↓
定義された状態へ遷移

であるなら、それ以外の権限は基本的に不要である。

概念的には、

Agent Role

Allowed
├─ 指定ProjectのTicket閲覧
├─ Journal追加
├─ Agent workflowで必要なTicket更新
└─ 許可された状態遷移

Denied
├─ 任意Ticket削除
├─ Project管理
├─ Member管理
├─ Version削除
├─ Role管理
└─ System設定

となる。


8. Workflow の状態遷移も権限として扱う

AI Agent の権限管理では、

「Ticket を編集できるか」

だけでは不十分である。

誰がどの状態へ遷移できるかも重要だ。

例えば Agent Brief Workflow が、

Brief Draft
    ↓ Human Review
Brief Ready
    ↓ Handler Validation
Ready for Agent
    ↓ Agent
Running
    ↓
Verification

なら、

Human Writer
Brief Draft → Brief Ready

Handler
Brief Ready → Ready for Agent

Agent Runner
Ready for Agent → Running

CI / Handler
Running → Verification / Result

のように責務を分離できる。

理想的には、Redmine の Workflow 設定と Role を利用して、

Agent Runner が Human Review を飛ばして Ready for Agent に変更できない

状態にする。

これは単なる UI 制御ではなく、AI Agent に対する security boundary になる。


9. systemd service を権限ごとに分ける

Linux 上では、MCP Server を権限別に別サービスとして管理する。

例えば、

redmine-mcp-reader.service
redmine-mcp-writer.service
redmine-mcp-agent.service

という構成にする。

Reader と Writer を同じプロセスへまとめて、リクエストごとに API Key を切り替える設計も技術的には可能だが、小規模な環境ではサービス自体を分離した方が理解しやすい。

理由は、

  • credential の混在を防ぎやすい
  • systemd 単位で停止できる
  • ログを分離できる
  • 障害範囲を限定できる
  • 設定ミスを発見しやすい

ためである。


10. Reader service の構成例

以下は構造を示す例である。

実際の executable path、オプション名、環境変数名は、導入している Tunnel Client と MCP Server の現行仕様に合わせる必要がある。

[Unit]
Description=Redmine MCP Reader
After=network-online.target
Wants=network-online.target

[Service]
Type=simple

User=redmine-mcp
Group=redmine-mcp

EnvironmentFile=/etc/redmine-mcp/reader.env

ExecStart=/path/to/tunnel-client run \
  --control-plane.api-key env:CONTROL_PLANE_API_KEY \
  --control-plane.tunnel-id ${CONTROL_PLANE_TUNNEL_ID} \
  --mcp.command channel=main,command=<reader-mcp-command>

Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Reader 用 environment file には Reader 用 credential だけを配置する。

CONTROL_PLANE_API_KEY=...
CONTROL_PLANE_TUNNEL_ID=...
REDMINE_URL=...
REDMINE_API_KEY=<reader credential>
REDMINE_MCP_READ_ONLY=true

11. Writer service の構成例

Writer も別 service とする。

[Unit]
Description=Redmine MCP Writer
After=network-online.target
Wants=network-online.target

[Service]
Type=simple

User=redmine-mcp
Group=redmine-mcp

EnvironmentFile=/etc/redmine-mcp/writer.env

ExecStart=/path/to/tunnel-client run \
  --control-plane.api-key env:CONTROL_PLANE_API_KEY \
  --control-plane.tunnel-id ${CONTROL_PLANE_TUNNEL_ID} \
  --mcp.command channel=main,command=<writer-mcp-command>

Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Writer 側は、

REDMINE_API_KEY=<writer credential>
REDMINE_MCP_READ_ONLY=false

となる。

Reader と Writer で秘密情報ファイルを共用しない。


12. Agent service ではさらに境界を強くする

Agent Runner 用 MCP では、人間用 Writer より強い制限を考える。

例えば、

Agent MCP
├─ Agent専用 Redmine user
├─ Agent専用 API Key
├─ Agent専用 Role
├─ Agent対象 Project のみ参加
└─ Agent Workflowで必要な権限のみ

とする。

Agent が何らかの理由で誤った Tool を呼んでも、

MCP Tool
↓
Redmine Role
↓
Workflow
↓
Project membership

のどこかで拒否できる設計が望ましい。


13. EnvironmentFile の権限を絞る

API Key や Tunnel credential を unit ファイルへ直接書かない。

秘密情報は別ファイルへ配置する。

例えば、

/etc/redmine-mcp/reader.env
/etc/redmine-mcp/writer.env
/etc/redmine-mcp/agent.env

とする。

ファイル自体も、

root
または
専用 service account

以外から読めないようにする。

重要なのは、

一般ユーザー
↓
service definition は読める

秘密情報
↓
読めない

という分離である。


14. MCP Server のバージョンを固定する

常駐サービスでは、起動するたびに最新バージョンを取得する構成は避けた方がよい。

例えば、

npx package

だけでは、その時点の最新版へ更新される可能性がある。

常駐環境では、

特定version

へ固定するか、

事前にインストールした executable

を使う。

理由は、

server reboot
↓
MCP Server が暗黙アップデート
↓
Tool contract が変化
↓
ChatGPT側の挙動が変化

という状態を防ぐためである。

Infrastructure restart と Application upgrade は分離した方がよい。


15. Tunnel と MCP Server のアップグレードも分離する

同様に、

Tunnel Client
MCP Server
Redmine

を一度にアップグレードしない方がよい。

例えば、

1. Reader MCP で新バージョン確認
2. 接続確認
3. read operation確認
4. Writerへ展開
5. write operation確認
6. Agentへ展開

のように段階的に更新する。

Reader を最初の Canary として使える。


16. 起動確認

systemd へ登録した後は、

service enabled
service active
tunnel connected
MCP initialized
Redmine reachable

をそれぞれ確認する。

ここで、

プロセスが起動していること

と、

MCP が利用可能であること

は別条件として扱う。

例えば Tunnel Client のプロセスが active でも、

  • Redmine API Key が無効
  • MCP subprocess 起動失敗
  • Tunnel 接続失敗

などはあり得る。

したがって service status だけで運用正常とは判断しない。


17. Reader の検証

Reader の検証では、

成功ケースだけでなく拒否ケースを確認する。

成功すべき操作

Project取得
Ticket取得
Roadmap取得
Journal取得
Wiki取得

失敗すべき操作

Ticket作成
Ticket本文変更
コメント追加
Status変更
Ticket削除

重要なのは、

read が成功することと write が失敗することの両方

を確認することである。


18. Writer の検証

Writer では、

Ticket取得
Ticket作成
Ticket更新
コメント追加
許可されたStatus変更

が成功することを確認する。

同時に、

Member管理
Role変更
Project削除
System設定変更

など、Writer の責務外操作が拒否されることも確認する。


19. Agent の検証

Agent では通常の CRUD だけでなく Workflow Contract を確認する。

例えば、

Ready for Agent
↓
Running

は成功する。

しかし、

Brief Draft
↓
Running

は拒否される。

また、

Agent対象Project

では操作できるが、

別Project

にはアクセスできない、あるいは変更できないことも確認する。

Agent の Acceptance Criteria は、

できること

だけでなく、

できてはいけないこと

を含める必要がある。


20. ログを service 単位で分離する

systemd を使う利点の一つが journal である。

例えば、

Reader service logs
Writer service logs
Agent service logs

がそれぞれ分離される。

障害時には、

誰向けのMCPだったか
どのcredentialだったか
どのserviceが失敗したか

を切り分けやすい。

複数の権限を1プロセスへまとめると、この境界が曖昧になりやすい。


21. Redmine の Journal も監査ログとして利用する

MCP 側のログだけでなく、Redmine 自身の Journal も重要である。

例えば API Key を、

mcp-reader
mcp-writer
mcp-agent

という別ユーザーへ割り当てていれば、

誰が変更したか

を Redmine 側でも区別できる。

AI Agent が更新した Ticket を人間が更新したように見せないことが重要だ。

監査上、

Human
Agent
Automation

は別 principal として扱う方がよい。


22. Admin MCP は常用しない

Admin MCP を作る場合でも、通常の ChatGPT 接続先として常時利用する必要はない。

管理操作には、

  • Member変更
  • Project設定
  • Role変更
  • Version管理
  • 削除系操作

など、通常開発より影響の大きい操作が含まれる。

そのため、

Reader
Writer
Agent

は常駐、

Admin

は必要時のみ有効化する設計も考えられる。

例えば systemd 上で、

disabled by default

にしておき、必要な管理作業時だけ起動する。

これにより、高権限 credential の exposed time を短くできる。


23. ChatGPT 側の権限と混同しない

重要なのは、

ChatGPT から MCP が見えることと、その操作が許可されていることは別問題

という点である。

仮に ChatGPT が Writer MCP を呼び出せたとしても、

MCP server
↓
Redmine API
↓
Role / Workflow

で最終的な認可を行える。

逆に Redmine 側が強権限であれば、

「ChatGPT が適切に判断してくれる」

ことだけを security boundary としてはいけない。

AI の判断と認可は分離する。


24. MCP Tool Contract も必要最小限にする

Redmine Role が最終防御になるとしても、Agent 向け MCP へ不要な Tool を公開する必要はない。

例えば Agent Runner が、

get issue
add journal
update limited workflow state

しか必要としないなら、

Project削除や Member変更といった Tool はそもそも見せない方がよい。

理想的には、

Tool exposure
↓
API permission
↓
Workflow permission

の3段階で絞る。


25. 利用者が増えた場合の拡張

最初の2人程度なら、

Writer MCP

だけから始める運用も可能である。

その後、

Step 1

Writer

から、

Step 2

Reader
Writer

へ分離する。

AI Agent 導入後、

Step 3

Reader
Writer
Agent

とする。

必要になった場合のみ、

Step 4

Reader
Writer
Agent
Admin

へ拡張する。

人数が増えたからプロセスを増やすのではなく、

新しい Trust Boundary が生まれたときだけ増やす。


26. さらに大規模になった場合

利用者が数十人・数百人になった場合には、単純な共有 API Key 方式だけでは監査や個人単位の認可が不足する可能性がある。

その段階では、

User Identity
↓
MCP Gateway / Authorization Layer
↓
credential selection / delegated identity
↓
Redmine

のような追加認可レイヤーを検討することになる。

ただし、2〜10人程度のチームで最初からこの仕組みを作るのは過剰になりやすい。

まずは、

権限別MCP
+
権限別API Key
+
Redmine Role

で十分なケースが多い。


27. AI Agent Runner との接続

最終的に Agent Runner を導入すると、

Redmine
   │
Ready for Agent Ticket
   ↓
Agent Handler
   ↓
Agent Runner
   ↓
Git
   ↓
CI

という流れになる。

Agent Handler が Redmine を操作するときには、人間用 Writer MCP を使わせず、

Agent MCP

を使わせる。

これにより、

Human permission boundary

と、

Machine permission boundary

を分離できる。


28. GitHub 側も同じ考え方で分離する

この考え方は Redmine だけで終わらない。

Agent Runner が GitHub へアクセスする場合も、

Redmine Agent Credential

と、

GitHub Agent Credential

を分離する。

Agent Runner に必要なのは一般的には、

対象repository取得
branch作成
commit / push
PR操作
CI結果取得

など、Agent Workflow に必要な範囲である。

Organization 管理権限まで持たせる必要はない。

つまり Agent Runner 全体では、

Agent
├─ Redmine: workflow最小権限
└─ GitHub: source最小権限

という構成になる。


29. Redmine と GitHub の責務境界

最終的な構成は次のようになる。

                Human
                  │
              ChatGPT
                  │
            Writer MCP
                  │
               Redmine
                  │
           Ready for Agent
                  ↓
             Agent Runner
              /        \
             /          \
       Agent MCP       Git credential
           │                 │
        Redmine            GitHub
                              │
                              ↓
                              CI

責務としては、

Redmine
├─ Roadmap
├─ Ticket
├─ Agent Brief
├─ Approval
├─ Workflow State
└─ Execution metadata

GitHub
├─ Source
├─ Branch
├─ Commit
├─ Pull Request
└─ CI

となる。


30. 実装時の完了条件

この権限管理基盤を導入するときは、単に service が起動しただけで完了にしない。

少なくとも次を確認する。

[ ] MCP runtime が専用OSユーザーで動作する

[ ] credential が service definition と分離されている

[ ] Reader と Writer が別credentialになっている

[ ] Reader の write 操作が Redmine 側でも拒否される

[ ] Writer が必要な Ticket 操作を実行できる

[ ] Writer が管理操作を実行できない

[ ] systemd restart 後に自動復旧する

[ ] OS reboot 後に自動起動する

[ ] service ごとにログを確認できる

[ ] Redmine Journal 上で操作主体を識別できる

Agent を導入する場合はさらに、

[ ] Agent専用credentialが存在する

[ ] AgentがHuman用credentialを使用しない

[ ] Agent対象Projectが限定されている

[ ] Agentが許可されたWorkflow遷移のみ実行できる

[ ] Human ReviewをAgentが迂回できない

[ ] Agentの操作がRedmine上で識別できる

ことを確認する。


31. 最初に構築する現実的な構成

現在の利用者が2人程度なら、最初から複雑にしすぎる必要はない。

現実的には、

Redmine Server

systemd
├─ redmine-mcp-reader
└─ redmine-mcp-writer

から始めればよい。

それぞれ、

Reader
├─ read-only MCP
└─ read-only Redmine credential

Writer
├─ write-enabled MCP
└─ limited writer Redmine credential

とする。

AI Agent Runner が実際に導入された段階で、

redmine-mcp-agent

を追加する。

Admin は必要性が出るまで作らなくてもよい。


まとめ

Redmine と MCP の権限管理では、大掛かりな独自認可基盤を最初から作る必要はない。

基本構造は、

systemd service
      ↓
権限別 MCP Server
      ↓
権限別 API Key
      ↓
Redmine User
      ↓
Redmine Role
      ↓
Workflow / Project Permission

で成立する。

特に重要なのは、

  • MCP を専用 OS ユーザーで動かす
  • Reader / Writer / Agent を credential レベルで分離する
  • MCP 側だけでなく Redmine 側でも制限する
  • Agent と Human を別 principal として扱う
  • Agent が変更可能な Workflow State を限定する
  • 高権限 Admin MCP を常用しない
  • 常駐サービスでは MCP Server のバージョンを固定する
  • systemd の起動確認だけでなく、許可・拒否の双方を検証する
  • Redmine Journal を監査情報として利用する

という点である。

小規模な段階では、

Reader
Writer

だけで十分であり、

開発基盤が Agent 化された時点で、

Agent

を追加すればよい。

この方式なら、現在の数人規模の ChatGPT + Redmine MCP 環境から、将来の Agent Handler / Agent Runner を含む開発基盤まで、同じ認可モデルのまま段階的に拡張できる。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?