How to Implement a Credential Vault in CubeSandbox to Prevent API Key Exposure
CubeSandbox isolates API keys outside the sandbox by routing traffic through CubeEgress, a user-space proxy that injects credentials at runtime while keeping secrets out of the sandbox filesystem, environment variables, and process memory.
TencentCloud/CubeSandbox is an open-source isolation platform that executes user code inside lightweight micro-VM sandboxes called Cubelet. To implement a credential vault that prevents API keys from entering these restricted environments, the platform stores secrets centrally in CubeMaster and injects them dynamically via the CubeEgress L7 proxy. This design ensures that sensitive credentials never ship inside the sandbox image or appear in the sandbox's memory space.
How the Credential Vault Works
The vault architecture relies on a strict separation between the secret store and the execution environment. When sandbox code initiates an outbound HTTP request, the traffic flows through a controlled pipeline:
Sandbox code --> Cubelet (network namespace) --> CubeVS (kernel eBPF) --> allow_out → CubeEgress (user-space proxy)
│ │
└─> request matches EgressRule.inject ──► CubeEgress adds header ──► target service
Because CubeEgress operates in the host's user space, it can modify requests after they leave the sandbox but before they reach the external network. This allows the proxy to inject Authorization: Bearer <secret> headers on the fly while the sandbox itself transmits only placeholder tokens or empty headers.
Injection Rule Definitions
Operators define injection rules in the CubeNetworkConfig protobuf message (formerly CubeVSContext) located in network-agent/api/v1/network_agent.proto. Each sandbox declares an EgressRule.inject field that maps request patterns—such as host, path, or method—to a secret placeholder like ${SECRET}.
As documented in docs/changelog/v0.4.0.md, this feature supports fine-grained scoping per-sandbox, per-domain, or per-path, ensuring that credentials are injected only for specific intended destinations.
Configuring Central Secret Storage
Secrets reside in CubeMaster’s configuration or an external secret manager, never inside the sandbox image. Operators add keys to the vault using a YAML configuration file (e.g., cube-master.yaml):
secrets:
openai-key: sk-xxxxxxxxxxxxxxxxxxxx
CubeMaster reads this section at startup and holds the credentials in memory. Because the secret is never written to the sandbox filesystem or passed as an environment variable during container creation, the isolated code cannot read the value via /proc/self/environ or filesystem traversal.
Defining Injection Rules with the Go SDK
Programmatic configuration uses the types defined in sdk/go/policy.go. The following example creates an egress rule that injects the openai-key secret into requests targeting api.openai.com:
import "github.com/TencentCloud/CubeSandbox/sdk/go"
policy := cubesandbox.Policy{
Egress: &cubesandbox.Egress{
Rules: []cubesandbox.EgressRule{
{
// Apply to any request to api.openai.com
Match: cubesandbox.EgressRuleMatch{
Host: "api.openai.com",
},
// Inject an Authorization header. The secret is stored in the vault
// under the name "openai-key".
Inject: []cubesandbox.EgressRuleInject{
{
Header: "Authorization",
// The placeholder will be replaced by the secret at runtime.
Secret: "${SECRET}",
// Reference the vault entry by name.
SecretName: "openai-key",
},
},
// Allow the request to proceed.
Allow: true,
},
},
},
}
The SecretName field references the key stored in CubeMaster’s vault, while the Secret field specifies the placeholder string that CubeEgress replaces with the actual value.
Runtime Injection and Request Rewriting
When a request matches an injection rule, CubeEgress performs the substitution. The proxy—which is built on OpenResty/Lua—looks up the stored secret by name, substitutes the ${SECRET} placeholder (or the raw value) into the specified header, and forwards the request to the target service. The original request that left the sandbox contains no credential.
This runtime rewriting happens entirely outside the sandbox network namespace. Consequently, even if the sandbox process is compromised, the attacker cannot sniff the credential because it is added only after the traffic exits the micro-VM.
Log Redaction for Audit Security
CubeMaster’s safe-log pipeline automatically masks secret fields as ***REDACTED*** in all audit records. As noted in docs/changelog/v0.4.0.md, this redaction prevents accidental exfiltration of credentials through log files while preserving the audit trail of injection events.
Verifying Zero-Exposure in Sandboxes
To confirm that the credential vault is functioning correctly, you can inspect the sandbox environment at runtime. The following command demonstrates that the secret is absent from the process environment:
# Inside the sandbox (e.g., via `cubecli exec`)
cat /proc/self/environ | grep OPENAI
# Output: (empty)
Because the secret is never placed in the environment, the sandbox cannot leak it through standard introspection.
Using the Python SDK (sdk/python/cubesandbox/sandbox.py), sandbox code sends requests without authentication headers:
import cubesandbox
sandbox = cubesandbox.Sandbox()
# The request is sent without any Authorization header.
resp = sandbox.http_get("https://api.openai.com/v1/models")
print(resp.status_code) # 200 – the key was injected by CubeEgress
The Python SDK automatically forwards the request to the egress proxy, which handles the credential injection transparently.
Delivery Mode Selection
The CubeSandbox web interface, specifically web/src/components/agents/AgentSettingsDialog.tsx, allows operators to choose between two credential delivery modes:
- Egress mode: The vault-style approach described above, where CubeEgress injects headers at the proxy layer.
- Environment mode: Injects secrets as environment variables during sandbox initialization (not recommended for high-security scenarios).
The egress mode is the recommended implementation for a true credential vault, as it guarantees the sandbox never has direct access to the secret in any form.
Summary
- Zero-exposure architecture: Secrets are stored centrally in CubeMaster and injected by CubeEgress after traffic leaves the sandbox, preventing access via filesystem, environment, or memory.
- Fine-grained control: Injection rules in
network-agent/api/v1/network_agent.protoallow scoping credentials by host, path, and method. - Audit safety: Log redaction automatically masks secret values as
***REDACTED***to prevent accidental leakage. - Extensible proxy logic: Built on OpenResty/Lua, CubeEgress supports custom rotation or temporary credential logic without modifying sandbox code.
- Verification: Inspection of
/proc/self/environinside the sandbox confirms the absence of credentials.
Frequently Asked Questions
How does CubeSandbox prevent secrets from leaking via environment variables?
In egress mode, CubeSandbox deliberately omits secrets from the sandbox initialization process. The secrets are stored only in CubeMaster’s memory and injected by CubeEgress at the network edge. Because the sandbox’s /proc/self/environ and filesystem never contain the credential, introspection attacks cannot retrieve the value.
Can I use regex patterns to match API endpoints for credential injection?
Yes. The EgressRuleMatch struct in sdk/go/policy.go supports pattern matching on host, path, and method fields. You can define specific injection rules that apply only to particular API endpoints, ensuring that credentials are sent exclusively to authorized services.
What happens if CubeEgress cannot find the requested secret in the vault?
If the SecretName referenced in an injection rule does not exist in CubeMaster’s vault, CubeEgress logs a substitution error and typically forwards the request without the injected header (depending on the configured fail-closed or fail-open policy). The sandbox receives no indication that a secret was missing, preventing information leakage about the vault’s contents.
Is the credential vault compatible with external secret managers like Vault or AWS Secrets Manager?
Yes. While the examples show YAML-based storage in cube-master.yaml, CubeMaster’s configuration layer can integrate with external secret managers. The secrets are loaded into CubeMaster’s memory at startup or fetched dynamically, but they are still injected via CubeEgress rather than passed into the sandbox, maintaining the zero-exposure guarantee.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →