feat(vault): add vault proxy mode for credential isolation #12839
feat(vault): add vault proxy mode for credential isolation #12839sfo2001 wants to merge 7 commits into
Conversation
07b1e91 to
6fa643a
Compare
6fa643a to
fe12ab8
Compare
|
This seems extremely limited. It handles only API keys in headers. The other one, #9271, handles any kind of secret, API keys, Oauth, bot tokens in the request body, and so on. |
@HenryLoenwind Valid concern -- the base PR is deliberately scoped to API key injection via HTTP headers, which covers all LLM and web search providers. Follow-up PRs extend the vault proxy to additional secret types:
On domain allowlisting: the nginx sidecar already has hardcoded proxy_pass targets with a return 403 default per server block, so unauthorized paths are blocked per-provider. Full outbound isolation (Docker --internal network + explicit allowlist) is a natural follow-up and doesn't require architectural changes. On OAuth: short-lived OAuth tokens (1h expiry) are a different risk profile than long-lived API keys. They can't be statically encrypted into a vault file. The channel token HTTP endpoint pattern (sidecar resolves tokens on demand) could be extended to OAuth in a future PR. On cross-platform: the nginx sidecar is fully containerized and works identically on Linux, macOS, and Windows (via Docker Desktop) with no platform-specific code paths. |
4d5d137 to
5b36096
Compare
bfc1ccb to
f92900f
Compare
|
This pull request has been automatically marked as stale due to inactivity. |
Again , another one I will now rebase. Note that the maintainers have already closed another PR in favour of this one. So it would be great if a real human has a look at it and decides either to merge or close. Status update: the vault proxy has been running in production (my own self-hosted Unraid deployment ) for ~2 weeks. I use it with Google, Brave, Nebius Token Factory in a Docker. This addresses several open security issues: #11829, #14411, #9627, #11202, #20912, #7916, #10173. Happy to split the PR if that helps review. |
fe2918b to
98c0084
Compare
98c0084 to
18db460
Compare
18db460 to
f269336
Compare
701593e to
e4d36d2
Compare
Add vault proxy infrastructure that keeps API keys out of the gateway process. When vault.enabled is true and vault.proxies maps a provider to a proxy URL, the gateway uses a placeholder key and rewrites the baseUrl to route through the vault sidecar (nginx), which injects the real credential. - Add VaultConfig type, zod schema, and config plumbing - Add resolveVaultProxyUrl() + VAULT_PROXY_PLACEHOLDER_KEY in model-auth - Rewrite baseUrl in normalizeProviders() when vault proxy is configured - Add tests for proxy resolution, baseUrl rewriting, schema validation Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add `openclaw vault` CLI subcommands for managing age-encrypted secrets and a provider registry mapping providers to proxy ports/secret names. Subcommands: init, status, add, remove, list, migrate. Includes hostname validation for --proxy-host, extracted helpers (buildDefaultProxyMap, findProviderBySecretName), and docs with --json --reveal security note. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Extend vault proxy support to web search providers (Brave, Perplexity, Grok/xAI) and add Groq, Mistral entries to the provider registry. When vault proxy is active, auth headers are omitted and requests are routed through the vault sidecar. SEARCH_VAULT_KEYS hoisted to module scope; import path corrected. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Adds the runtime counterpart to the vault CLI and TS operations: - Dockerfile: nginx:alpine base with age for secret decryption - entrypoint.sh: decrypts vault.age, renders nginx config, starts proxy - nginx.conf.template: reverse proxy blocks for all supported providers - secrets.example.txt: documentation example for secret file format Co-Authored-By: Claude Opus 4.6 <[email protected]>
Providers like ollama and autorouter have no vault proxy mapping. Migrating their keys strips them from config with no way to inject them back, breaking those providers at runtime. Guard the migrate loop with a providerProxyUrl() check to leave local keys in place. Co-Authored-By: Claude Opus 4.6 <[email protected]>
The Google SDK sets apiVersion="" when baseUrl is overridden, sending
requests to /models/{model}:method instead of /v1beta/models/{model}.
Add nginx location /models/ that rewrites to /v1beta/models/ upstream.
Also add dummy apiKey for local providers (ollama, autorouter) to
prevent SDK ModelRegistry validation from silently rejecting the
entire models.json — which dropped vault proxy baseUrl overrides.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add all 10 supported providers to the table. Document Google SDK's apiVersion="" behavior that requires a /models/ nginx location block, and the no-key-required fix for local providers that prevents SDK validation from silently dropping all models.json overrides. Co-Authored-By: Claude Opus 4.6 <[email protected]>
e4d36d2 to
91aca6a
Compare
|
Closing this in favor of the architectural direction that landed in #26155. The maintainers chose in-process secret resolution ( The vault-proxy approach offers stronger isolation (the gateway process never holds credential values at all; injection happens at the network layer), but that stronger guarantee requires a containerized sidecar and is inherently Docker-specific. Thanks for the review and the clear direction. |
Summary
openclaw vaultCLI (init, status, add, remove, list, migrate) for managing age-encrypted secrets with a provider registry mapping providers to proxy portsDetails
Architecture: Two Docker networks enforce isolation — the gateway has no route to the internet and no access to plaintext keys. The vault sidecar decrypts
vault.ageat startup, renders an nginx config with credentials, and proxiesrequests to upstream APIs.
Config:
vault.enabled+vault.proxiesinopenclaw.json. When active,normalizeProviders()rewritesbaseUrlto the proxy URL and substitutes a placeholder API key.CLI:
openclaw vault initgenerates an age keypair;vault add/removemanage individual secrets with automatic proxy configuration for known providers;vault migratemoves plaintextapiKeyvalues from config into the encryptedvault.
Security:
--proxy-hostvalidated against hostname regex to prevent SSRF--json --revealrisk documented in vault.mdTest plan
attacker.com/path#,-bad-host, empty)buildDefaultProxyMap()andfindProviderBySecretName()helper coverageUse of AI
Greptile Overview
Greptile Summary
This PR introduces a “vault proxy” mode intended to keep provider API keys out of the gateway process. It adds:
vaultconfig section (vault.enabled,vault.proxies,vault.file,vault.publicKey) and schema validation.baseUrlis rewritten to the proxy URL and auth resolution returns a fixed placeholder key.openclaw vaultCLI for initializing an age-encrypted vault file and managing secrets / migrating plaintextapiKeyvalues.The changes primarily touch the agent configuration pipeline (
ensureOpenClawModelsJson→normalizeProviders), auth resolution (resolveApiKeyForProvider,resolveModelAuthMode), and tool execution paths for web search. The CLI and vault operations live undersrc/cli/vault-cli.tsandsrc/vault/operations.tsand are registered via the sub-CLI registry.Confidence Score: 3/5
openclawCLI entrypoint when the vault subcommand is loaded.registerVaultCli()appears to register avaultcommand that is already declared in the sub-CLI registry. That duplication commonly causes Commander to throw or behave unexpectedly in real runs, and current tests don’t exercise the full CLI registration flow.