Skip to content

fix(security): migrate MCP private-key handoff to keyfile pattern - #3478

Closed
jewoos2921 wants to merge 1 commit into
block:mainfrom
jewoos2921:fix/mcp-keyfile-secret-isolation
Closed

fix(security): migrate MCP private-key handoff to keyfile pattern#3478
jewoos2921 wants to merge 1 commit into
block:mainfrom
jewoos2921:fix/mcp-keyfile-secret-isolation

Conversation

@jewoos2921

Copy link
Copy Markdown

Summary

Previously, BUZZ_PRIVATE_KEY and NOSTR_PRIVATE_KEY were passed to MCP subprocesses via environment variables (PASSTHROUGH_ENV allowlist in buzz-agent). Any configured MCP server could read the user's identity key from its own environment — a known trust-boundary tradeoff documented in commit fbce606fe.

This PR migrates to a keyfile pattern so raw private keys never appear in subprocess environments.

Changes

buzz-dev-mcp/src/shim.rs — core fix

  • Writes BUZZ_PRIVATE_KEY to a 0600 keyfile inside the shim's 0700 tempdir (same proven pattern already used for NOSTR_PRIVATE_KEY)
  • Exposes the path via BUZZ_KEYFILE env var to shell children
  • Removes BUZZ_PRIVATE_KEY from the process env immediately after writing the keyfile
  • New Shim.buzz_env field carries BUZZ_KEYFILE + BUZZ_RELAY_URL (relay URL is non-secret)

buzz-cli/src/lib.rs — keyfile support

  • New --keyfile flag / BUZZ_KEYFILE env var that reads the private key from a file
  • Takes precedence over --private-key / BUZZ_PRIVATE_KEY
  • Backward compatible — existing env-var usage still works for standalone CLI invocations

buzz-dev-mcp/src/view_image.rs — media auth

  • Relay media Blossom t=get auth now reads from BUZZ_KEYFILE first, falling back to BUZZ_PRIVATE_KEY for backward compatibility

buzz-dev-mcp/src/shell.rs

  • Passes buzz_env (BUZZ_KEYFILE, BUZZ_RELAY_URL) to shell children alongside git_env
  • Bootstrap hint checks BUZZ_KEYFILE instead of BUZZ_PRIVATE_KEY

buzz-agent/src/mcp.rs — allowlist lockdown

  • Removes BUZZ_PRIVATE_KEY and NOSTR_PRIVATE_KEY from PASSTHROUGH_ENV
  • Adds BUZZ_KEYFILE (a filesystem path, not a secret)
  • MCP subprocesses can no longer read identity keys from their environment

Security impact

Before After
Any MCP server can read BUZZ_PRIVATE_KEY from its env MCP servers only see BUZZ_KEYFILE (a path)
Key in env = readable via /proc/self/environ, env, etc. Key in 0600 file inside 0700 tempdir, cleaned up on exit
NOSTR_PRIVATE_KEY already keyfile-isolated BUZZ_PRIVATE_KEY now also keyfile-isolated

Verification

  • cargo check — all 3 crates compile
  • cargo clippy -- -D warnings — clean
  • cargo fmt --check — clean
  • cargo test -p buzz-cli -p buzz-dev-mcp -p buzz-agent --lib — 286 passed (2 pre-existing failures on main, unrelated to this change)

Previously, BUZZ_PRIVATE_KEY and NOSTR_PRIVATE_KEY were passed to MCP
subprocesses via environment variables (PASSTHROUGH_ENV allowlist in
buzz-agent). Any configured MCP server could read the user's identity key
from its own environment — a known trust-boundary tradeoff documented in
commit fbce606.

This migrates to a keyfile pattern so raw private keys never appear in
subprocess environments:

1. buzz-dev-mcp shim.rs: writes BUZZ_PRIVATE_KEY to a 0600 keyfile (same
   pattern already used for NOSTR_PRIVATE_KEY), exposes the path via
   BUZZ_KEYFILE, then removes the raw key from the process env. Shell
   children receive BUZZ_KEYFILE (a path, not a secret) instead.

2. buzz-cli: adds --keyfile flag / BUZZ_KEYFILE env var that reads the
   private key from a file. Takes precedence over --private-key /
   BUZZ_PRIVATE_KEY (backward compatible — existing env-var usage still
   works for standalone CLI invocations).

3. buzz-dev-mcp view_image.rs: relay media auth now reads from BUZZ_KEYFILE
   first, falling back to BUZZ_PRIVATE_KEY for backward compatibility.

4. buzz-agent mcp.rs: removes BUZZ_PRIVATE_KEY and NOSTR_PRIVATE_KEY from
   PASSTHROUGH_ENV. Adds BUZZ_KEYFILE (non-secret path). MCP subprocesses
   can no longer read identity keys from their environment.

The keyfile is written inside the shim's 0700 tempdir (cleaned up on drop),
with 0600 permissions set at creation time — the same proven pattern used
for the NOSTR_PRIVATE_KEY keyfile since it was introduced.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant