Skip to content

Latest commit

 

History

14 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

10_broker — secrets-injection proxy

mitmproxy (regular forward-proxy) + a Python addon that injects secrets pulled from Bitwarden Secrets Manager (bws) into allowlisted outbound requests. Deny-by-default: unlisted hosts get a 403. Secret values are never logged.

Portable how-it-works / connect-a-tool docs live in the secrets-injection-broker skill (~/.claude/skills/secrets-injection-broker/). This README is the machine-specific runbook.

Topology (single machine)

Broker and the dev container both run rootless on podman-machine-default (default connection), sharing a user-defined network broker-net:

  • Host Claude Code → localhost:8080 (broker published -p 127.0.0.1:8080:8080).
  • dev container → broker:8080 (container-to-container over broker-net; no host port, not LAN-exposed).

Engine-separation note: broker + dev share one engine, so a dev with the docker socket ON could exec the broker and read BWS_ACCESS_TOKEN. The socket is OFF by default — keep it off while co-located. (Co-location was chosen over a separate machine because cross-machine networking forced LAN/Tailscale/VPN exposure.)

Layout

Containerfile  python:3.12-slim + bws 2.1.0 (sha-pinned) + mitmproxy + PyYAML
addon.py       deny-by-default injection (header/bearer/basic/query, name->id map, TTL cache)
routes.yaml    allowlist + injection config (secret = bws KEY NAME)
ca/            exported PUBLIC CA cert (gitignored)
.env*          NOT used — token is a podman secret; BWS_SERVER_URL is a plain -e (EU region)

Build & run (default connection, rootless)

podman build -t broker:latest C:\projects\2026\10_broker
podman network create broker-net    # once

# one-time: store the BWS token as a podman secret (byte-exact temp file; no Git-Bash stdin — MSYS corrupts it)
$t = "$env:TEMP\bwstok"; [IO.File]::WriteAllText($t, '<TOKEN>', (New-Object Text.UTF8Encoding($false)))
podman secret create BWS_ACCESS_TOKEN $t; Remove-Item $t -Force

# run: shared network + loopback publish + hardened
podman run -d --name broker --init --network broker-net `
  -p 127.0.0.1:8080:8080 `
  --secret BWS_ACCESS_TOKEN,type=env `
  -e BWS_SERVER_URL=https://vault.bitwarden.eu `   # EU account; REQUIRED or bws -> invalid_client
  -v broker-ca:/home/broker/.mitmproxy:U `
  --read-only --tmpfs /tmp --tmpfs /home/broker/.config `
  --cap-drop=ALL --security-opt=no-new-privileges `
  --restart=on-failure:5 `
  --memory=512m --memory-swap=512m --pids-limit=512 --ulimit core=0 `
  broker:latest

# export the PUBLIC ca cert for clients to trust
podman cp broker:/home/broker/.mitmproxy/mitmproxy-ca-cert.pem C:\projects\2026\10_broker\ca\

Changing routes = rebuild + recreate. routes.yaml is baked into the image and the rootfs is --read-only, so podman cp into /app fails (500). Edit routes.yaml → podman build → recreate. After any CA regen, reconnect host MCP servers (they load the CA at launch).

Routes (all verified live)

host inject secret
api.search.brave.com header X-Subscription-Token BRAVE_API_KEY
api.mem0.ai header Authorization = Token {value} MEM0_API_KEY
api.notion.com bearer NOTION_TOKEN
github.com basic (x-access-token) — git push/pull (write-capable) GITHUB_TOKEN

Host Claude Code MCP wiring (C:\Users\mmtom\.claude.json)

brave-search, mem0, notion launch under Bun with a placeholder token + broker proxy + CA, e.g.:

"mem0": {
  "command": "C:\\Users\\mmtom\\.bun\\bin\\bun.exe",
  "args": ["C:\\Users\\mmtom\\.bun\\install\\global\\node_modules\\@mem0\\mcp-server\\dist\\index.js"],
  "env": {
    "MEM0_API_KEY": "broker-injects-real-value", "MEM0_DEFAULT_USER_ID": "mem0-mcp",
    "HTTPS_PROXY": "http://localhost:8080", "HTTP_PROXY": "http://localhost:8080",
    "NODE_EXTRA_CA_CERTS": "C:\\projects\\2026\\10_broker\\ca\\mitmproxy-ca-cert.pem"
  }, "type": "stdio"
}

(notion: bin/cli.mjs --transport stdio, env NOTION_TOKEN. brave: server-brave-search/dist/index.js, env BRAVE_API_KEY.)

Runtime gotcha: these servers honor HTTPS_PROXY only under Bun — the ~/.bun/bin/*.exe shims run under Node, which ignores it (sends the placeholder → upstream auth error, no broker hit). Always launch via bun.exe <dist/index.js>. curl.exe (Schannel) needs --ssl-no-revoke to honor --cacert. Scoped per-server — Claude's own Anthropic/Max traffic is NOT proxied. github MCP currently runs direct (mcp-server-github.exe, no token) — unbrokered; could be brokered via the github.com/api.github.com route + a Bun launch.

dev git wiring (done)

dev is on broker-net; broker CA at /home/dev/broker-ca.pem. Git scoped to github.com only:

git config --global http.https://github.com/.proxy     http://broker:8080
git config --global http.https://github.com/.sslCAInfo  /home/dev/broker-ca.pem

Broker injects Basic auth (PAT) → git push/pull works without the token in dev. Host stays on gh CLI.

Smoke test (from Windows host)

curl.exe -x http://localhost:8080 --ssl-no-revoke --cacert C:\projects\2026\10_broker\ca\mitmproxy-ca-cert.pem `
  -H "Accept: application/json" "https://api.search.brave.com/res/v1/web/search?q=test"   # 200 (injected)
curl.exe -x http://localhost:8080 --ssl-no-revoke https://example.com/                    # 403 (not allowlisted)

Notes

  • routes.yaml secrets are bws key names — must exist in the project or the broker won't start (fail-loud).
  • Secret fetches are TTL-cached (BROKER_CACHE_TTL, default 300s).
  • Anthropic/model traffic must NOT be routed here (keeps Max-plan native).
  • :8080 is unauthenticated but only reachable via host loopback + the internal broker-net (no LAN). If exposure ever broadens, add Proxy-Authorization (esp. given the write-capable github.com route).

About

mitmproxy + Bitwarden Secrets Manager forward proxy that injects API credentials into allowlisted outbound HTTPS, so tools/agents never hold the secret value (deny-by-default).

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages