Skip to content

aaronckj/vaultproxy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

293 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

vaultproxy

crates.io docker CI License: MIT MSRV: 1.88 transparent: opt-in

Secure credential sidecar for MCP servers — backed by Vaultwarden.

Stop pasting API keys into .env files. vaultproxy reads credentials from your self-hosted Vaultwarden, injects the right auth header for each downstream service, and never exposes plaintext secrets to the MCP layer or the AI agent.

Claude Code → MCP Server → vaultproxy (127.0.0.1:3201) → Your Service
                                     ↑
                               Vaultwarden (credentials stay here)

Why vaultproxy

Most MCP servers store credentials in env vars or .env files. These are readable by any process running as the same user, leak into shell history and ps auxe, and end up duplicated across every server you run. The AI agent driving your MCP server can also see them in tool responses.

vaultproxy fixes this with a small loopback HTTP service that:

  • Reads every credential from one place — your Vaultwarden folder.
  • Injects the correct auth pattern per service (X-Api-Key, Bearer, Basic, session cookie, UniFi dual-mode, query param).
  • Keeps secrets out of the MCP server's address space entirely (native /proxy mode), or at worst out of disk (launcher mode).
  • Detects weak / reused credentials via HMAC-SHA256 fingerprints (all local — no password hashes ever leave your LAN; HIBP is explicitly out of scope by design), and (optionally) rotates some of them via a Playwright + vision-LLM agent.

How it compares

vaultproxy agent-vault wirken OneCLI warden-mcp etc.
Backend Vaultwarden Infisical own vault + OS keychain own encrypted file Vaultwarden (as vault tool)
Hides credentials from AI ❌ exposes vault to AI
Auth patterns built-in bearer, header, basic, session, query, UniFi dual bearer bearer bearer n/a
Hot-reload (SIGHUP + HTTP)
Credential audit (weak/reused, local HMAC — no HIBP)
Auto credential rotation 🚧 partial (browser-vision, experimental)
TPM sealing ✅ optional
Dual integration: smart /proxy + dumb --launch proxy only proxy only proxy only n/a
Transparent HTTPS_PROXY (zero-mod agents) --features transparent (v1.1.0 beta) n/a
Language / binary Rust, single static binary Go Rust Rust TS / shells bw

vaultproxy is opinionated for the selfhost / homelab crowd — *arr stack, Home Assistant, OPNsense, UniFi, Plex, Tautulli, Nginx Proxy Manager. Other brokers assume HashiCorp Vault / Infisical / Conjur backends and target generic API providers (Anthropic, GitHub, Stripe).


Quickstart (Docker Compose)

1. Create a vault-proxy folder in Vaultwarden and add one item per downstream service. The password field holds the credential (API key, Bearer token, etc.).

2. Create services.toml in a local ./config/ directory:

[[service]]
name = "ha_home"
base_url = "http://homeassistant.local:8123"
auth = "bearer"
vault_item = "vault-proxy - Home Assistant"

[[service]]
name = "sonarr"
base_url = "http://sonarr.local:8989/api/v3"
auth = "header"
header_name = "X-Api-Key"
vault_item = "vault-proxy - Sonarr"

Full example: services.example.toml.

3. Run the setup wizard once to unlock Vaultwarden:

# docker-compose.yml
services:
  vaultproxy:
    image: ghcr.io/aaronckj/vaultproxy:latest   # or pin to a tag, e.g. :1.0.4
    restart: unless-stopped
    network_mode: host
    volumes:
      - ./config:/config
    environment:
      VAULT_FOLDER: vault-proxy
    command: ["--setup"]   # remove after first run
docker compose up                 # interactive setup
# remove `command: ["--setup"]`
docker compose up -d              # run for real
curl http://127.0.0.1:3201/vault/health

4. Use it from an MCP server:

curl -X POST http://127.0.0.1:3201/proxy \
  -H 'Content-Type: application/json' \
  -d '{"service":"ha_home","method":"GET","path":"/api/states"}'

The credential never appears in your shell, the MCP server, or the response.

Working MCP server examples (TypeScript, Python, Rust): examples/.


Auth patterns

auth value Required fields Example service
bearer Home Assistant
header header_name Sonarr / Radarr / Plex
query_param param_name Tautulli
basic key_field, secret_field OPNsense
session login_path, token_field Nginx Proxy Manager, Duplicati
unifi_dual login_path UniFi OS (API key → session fallback)

Add insecure_tls = true for LAN services with self-signed certs (logs a startup warning).


Two integration modes

  1. Native /proxy (Tier 1, recommended). Your MCP server detects vault-proxy via VAULT_PROXY_URL env and calls POST $VAULT_PROXY_URL/proxy. The credential resolves inside vault-proxy and is injected into the outbound header. The MCP server never holds the secret. See examples/smart-mcp-server-*.

  2. Launcher mode --launch (Tier 2). For unmodified upstream MCP servers, vault-proxy resolves credentials from Vaultwarden and execs the MCP server with credentials injected as env vars. No .env files on disk. Weaker than Tier 1 (env vars are readable from /proc/<pid>/environ by the same OS user) but stronger than file-based storage. Configure in mcp-servers.example.toml.


Do you actually need the MITM? (most people don't)

The two modes above cover almost everyone. Reach for the explicit POST /proxy path first — it never touches your TLS trust store and is the recommended way to use vaultproxy.

The transparent HTTPS-MITM listener is opt-in (since v1.12.0) and exists only for unmodified third-party agents that can't be taught to call /proxy (and can't be launched via --launch either). Enable it at build time with --features transparent. If your agent can call /proxy, you do not need this.

The transparent CA is trusted only by the agent process you explicitly configure to trust it — never system-wide.


Feature highlights

Feature Status Where
6 built-in auth patterns ✅ stable this README
SIGHUP + POST /vault/reload-services hot-reload with atomic swap and rollback-to-empty guard ✅ stable docs/operator/RELOAD.md
Per-caller rate limiting via X-Caller-Id / VAULT_PROXY_CALLER_ID ✅ stable SECURITY.md
Credential audit — weak/reused detection via HMAC-SHA256 fingerprints (all local — no password hashes ever leave your LAN; HIBP is explicitly out of scope by design) ✅ stable docs/operator/CRED-AUDIT.md
Credential rotation: browser-vision (Playwright + vision LLM, experimental, off by default) + UniFi key-bootstrap; generic *arr API rotation not supported (POST /rotate returns a typed unsupported) 🚧 partial, --features browser docs/operator/BROWSER-ROTATION.md
TPM-sealed keystore --features tpm SECURITY.md
Web dashboard --features dashboard (127.0.0.1:3202) docs/operator/DASHBOARD.md
Audit log (JSON, sensitive fields masked, 1000-entry cap) ✅ stable docs/operator/AUDIT-LOG.md
SMB mount provisioning via vault items ✅ stable docs/SMB.md
Transparent HTTPS_PROXY listener (zero-mod agents, host_inject + placeholder) --features transparent (v1.1.0 beta) docs/operator/TRANSPARENT.md

Planned for v1.2+: SIGHUP rebuild of transparent registry, audit log entries for transparent traffic, mTLS / Unix-socket listener auth, OAuth flows, response prompt-injection sanitisation, SIEM audit sinks, generic /rotate strategies. See docs/ROADMAP.md.


Security stance (one-paragraph version)

vaultproxy binds to 127.0.0.1:3201 only. The trust model is OS-level process isolation — any local process running as the same user can call /proxy. There is no mTLS or caller authentication on the proxy endpoint by design (network isolation is the primary defence; a startup warning fires on any non-loopback bind). Internal endpoints (/vault/connecterr-secrets, /vault/reload-services, /browser/*, /vault/notes) require an Authorization: Bearer <internal-token> from $CONFIG_DIR/internal-token (mode 0600; generated on first start, persisted at 0600, and regenerated only if the file is deleted). Credentials are decrypted in-process from an encrypted keystore; plaintext never appears in logs. Optional TPM sealing binds the keystore to the host machine. SSRF, log-injection, path-traversal, and arbitrary-command launcher guards are enforced at config load. Full threat model: SECURITY.md. Report vulnerabilities privately via GitHub Security Advisories.


Project status

vaultproxy is a young project maintained by one person, and it has not had a third-party security audit yet. Set expectations accordingly.

The thing that makes that easy to live with: vaultproxy stores no secrets of its own. Every credential lives in your Vaultwarden — vaultproxy just reads, injects, and forwards. If the project is ever abandoned, you revert to plain env vars or Vault Agent in minutes, with zero lock-in.


Install

Docker (recommended)

docker build -t vaultproxy:latest .                                    # headless, default
docker build --build-arg FEATURES=dashboard -t vaultproxy:dashboard .  # + web UI
docker build --build-arg FEATURES=dashboard,browser,engine -t vaultproxy:full .

A pre-built image is published to ghcr.io/aaronckj/vaultproxy via the GitHub Actions workflow on every tagged release.

Cargo (bare metal)

cargo install vaultproxy                        # from crates.io (headless)

Or build from source:

cargo build --release                          # headless
cargo build --release --features tpm           # + TPM sealing (needs libtss2-dev)
cargo build --release --features dashboard     # + web UI on 127.0.0.1:3202
cargo build --release --features browser       # + Playwright rotation (needs LiteLLM + Playwright)

Deeper docs


License

MIT. See LICENSE.

About

Secure credential sidecar for MCP servers — injects auth from Vaultwarden without exposing secrets to AI agents

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages