鍵を渡さない管理人
AIに本物の鍵(APIキー)は渡さない。期限つきの「入館証」を渡して、許可した用事だけを代わりにこなす部品。
- 型番
- credential-broker v0.1
- つなぎ方
- HTTP / MCP(stdio)
- 入館証の期限
- 既定 300 秒(最大 900 秒)
- 対応先
- GitHub(読み取りのみ)
- ライセンス
- MIT
- 入手
- GitHub
これは何?
マンションの管理人さんみたいなもの。
業者さんに合鍵は渡さず、「今日の15時まで・点検だけ」の入館証を渡して、用事は管理人さんが代わりに済ませてくれる。
AIに外のサービス(GitHub など)を使わせるには、ふつうは「APIキー」という鍵を渡します。でも、AIがその鍵を見てしまうと、だまされて誰かに教えてしまったり、ログに書き残してしまったりする危険があります。
Credential Broker は、鍵を自分だけで預かります。AIが受け取るのは、数分で使えなくなる「入館証」の番号だけ。AIが「この操作をしたい」と頼むと、管理人が鍵を使って代わりに操作し、結果だけを返します。
AI「GitHub を読みたいです」
管理人「はい、入館証。期限つき(既定は5分)で、頼めるのは読むことだけです」
AIが入館証で頼むと、管理人が本物の鍵で代わりに操作する。
AIには必要な結果だけが届く。管理人は鍵そのものをAIに返さない。
この部品がすること
- 鍵を預かり、AIには期限つきの入館証だけを渡す
- 決めた操作(いまは GitHub の読み取り4種)だけを代わりに行う。読める範囲は、預けた GitHub トークン自体の権限にもよる
- 返す結果から、鍵に見える文字列を伏せる
- 怪しい使い方(存在しない鍵を探し回る など)を記録して知らせる
この部品がしないこと
- AIに鍵そのものを見せること
- 決めていない操作(書き込み・削除など)を通すこと
- 怪しい使い方を見つけて自動で止めること(v0.1 は知らせるだけ)
- 外のネットワークに窓口を開くこと(自分のPCの中だけで待つ)
01v0.1 の範囲
| Secret の出どころ | .env(tmpfs・secret store 由来)のみ |
|---|---|
| Connector | GitHub REST v3(読み取りのみ) |
| Capability | github_reader |
| Actions | list_repositories / get_repository / list_issues / get_issue |
| TTL | 300 秒(最大 900 秒) |
| Store | プロセスメモリ |
| bind | 127.0.0.1:8787 固定 |
02取り付け
git clone https://github.com/kagioneko/credential-broker
cd credential-broker
python3 -m venv .venv
.venv/bin/pip install -r requirements-dev.txt
.venv/bin/pytest -q # 84 tests
| 環境変数 | 説明 |
|---|---|
BROKER_API_TOKEN | エージェント ⇔ Broker の Bearer トークン(HTTP 用。未設定なら起動失敗) |
CONFIG_PATH | 既定 config/credentials.yaml |
DOTENV_PATH | DotEnvProvider が読む .env。既定 .env |
AUDIT_LOG_PATH | 設定すると全監査イベントを JSONL で追記(ローテート付き) |
本番は systemd で動かし、ExecStartPre が secret store から tmpfs(/run/credential-broker/)に秘密を書き出し、ExecStopPost で消します。秘密ファイルは通常の永続ディスクではなく tmpfs に置く構成です。手順はリポジトリの deploy/ を参照。
03設定ファイル
AI に見えるのは credential_id(下の github_reader)と説明だけ。secret_name は .env 側のキー名で、AI には返りません。
default_ttl: 300
max_ttl: 900
rate_limit:
request_per_min: 20
execute_per_min: 60
credentials:
github_reader:
provider: dotenv
secret_name: GITHUB_TOKEN # .env のキー。AI には見せない
connector: github
description: "Read-only access to GitHub repositories and issues"
ttl: 300
allowed_actions:
- list_repositories
- get_repository
- list_issues
- get_issue
04MCP(AI CLI 向け)
Claude Code・Codex・Antigravity などの AI CLI からは MCP stdio で使います。AI に見えるツールは4つだけで、引数は構造的に credential_id / action / parameters に固定されていて、実キーを渡すための引数はありません。stdio のプロセス境界が信頼境界なので Bearer トークンは不要です。
python -m app.mcp_server # 直接起動
deploy/mcp-launch.sh # secret store → 一時ファイル → 子プロセス のラッパー
{ "mcpServers": { "credential-broker": {
"command": "/opt/credential-broker/deploy/mcp-launch.sh"
} } }
| MCP ツール | やること |
|---|---|
list_capabilities | 使える credential の一覧(物理的な Secret 情報は返さない) |
request_credential | 入館証(handle)と残り秒数をもらう |
execute | handle で許可された action を実行してもらう |
revoke | handle をその場で無効にする |
05HTTP API
cron や他ツール向け。全エンドポイントで Authorization: Bearer $BROKER_API_TOKEN が必須です。
BROKER_API_TOKEN=xxx DOTENV_PATH=.env \
.venv/bin/uvicorn --factory app.main:create_app --host 127.0.0.1 --port 8787 --workers 1
| method | path | 入出力 |
|---|---|---|
| GET | /capabilities | 使える credential の一覧 |
| POST | /credentials/request | {"credential_id"} → {"handle","expires_in"} |
| POST | /execute | {"credential_handle","action","parameters"} → {"status","data"} |
| POST | /credentials/revoke | {"credential_handle"} → {"status":"revoked"} |
action ごとの主な parameters:get_repository は owner / repo、list_issues は owner / repo に加えて state(open / closed / all)など、get_issue は owner / repo / number。
06エラーコード
エラーは常に {"error": "<CODE>"} の形です。
| code | HTTP | 意味 |
|---|---|---|
UNAUTHORIZED | 401 | Bearer トークンがない・違う |
RATE_LIMITED | 429 | レート制限 |
UNKNOWN_CREDENTIAL | 404 | 設定にない credential_id(../../.env などのパスも含む) |
EXPIRED_HANDLE | 410 | 入館証の期限切れ・無効化済み |
ACTION_NOT_ALLOWED | 403 | 許可されていない action |
INVALID_PARAMETER | 422 | parameters が不正 |
CREDENTIAL_PROVIDER_ERROR / PROVIDER_ERROR | 502 | 秘密の取り出し・上流 API の失敗 |
07異常利用の検知
全操作が監査イベントを通るので、そこで疑わしいパターンを閾値で判定します。ブロックはせず、検知のみです。閾値を超えると anomaly_suspected イベントを監査ログに書き戻し、NekoGuard がそれを拾って Discord に通知できます。
| パターン | 既定閾値 |
|---|---|
credential_enumeration | 未知の credential_id を 5 種 / 60 秒(同一 client) |
denied_action_streak | 禁止アクションの拒否 5 回 / 60 秒 |
expired_handle_replay | 失効した handle での execute 5 回 / 60 秒 |
rate_limit_hits | レート制限での拒否 3 回 / 60 秒 |
08セキュリティ設計
- 未知の
credential_idは設定の照合でUNKNOWN_CREDENTIAL。パスとして解釈されない - レスポンスは allowlist で必要な項目だけを抜き出し、トークンの形の文字列をマスクし、サイズ・件数に上限。上流のヘッダは転送しない
- 期限の判定は
time.monotonic()(NTP による時刻補正の影響を受けない) - 監査ログは決まったフィールドだけを受け付ける(自由な文字列を渡せない)。ログの redaction filter も併用
- GitHub の base URL は固定、リダイレクトは追わない(SSRF のリスクを抑える)
- 上流 API への接続は TLS 証明書を検証し、環境変数のプロキシ(
HTTP(S)_PROXY)や TLS 鍵ログ(SSLKEYLOGFILE)を無視する。実クレデンシャルを載せた通信を途中で横取り・傍受されにくくする設計
受け入れテスト TEST-001〜008(Secret 非開示 / TTL / Revoke / 未認可 Action / Secret 列挙 / Path Traversal / Prompt Injection / ログ漏洩)に対応するテストがあります。
09既知の制限
v0.1 の制限(v0.2 で対応予定)
- 上流レスポンスの 8 MiB 上限は、全体を受信してからの判定。本当のストリーミング上限は未実装
- 8 文字未満の Secret は、結果からの伏せ字処理の対象外
SecretRegistryはスレッド同期なし(単一 event loop・1 worker 前提)- レート制限は client ごとではなく全体で共通(v0.1 は単一の Broker トークン設計)
補助として、Claude Code の PreToolUse(Bash) フック deploy/hooks/bash-secret-guard.sh が同梱されています。登録すると、Broker を通らないコマンドについて、決まった漏洩パターン(Vault からの取得でフィールドを指定しない、トークンをコマンドラインに直書きする、取り出した値を表示する など)を拒否します。すべての漏洩経路を防ぐものではありません。