# How the CubeSandbox Credential Vault Prevents Secrets from Entering Sandbox Memory

> Learn how the CubeSandbox credential vault safeguards secrets by injecting them at the L7 proxy layer, preventing sandbox memory exposure. Secure your sensitive data.

- Repository: [Tencent Cloud/CubeSandbox](https://github.com/TencentCloud/CubeSandbox)
- Tags: internals
- Published: 2026-07-12

---

**The CubeSandbox credential vault injects secrets at the L7 proxy layer (CubeEgress) rather than inside the micro-VM, ensuring sensitive tokens never reside in sandbox memory.**

CubeSandbox isolates untrusted user code inside micro-VMs. When that code needs to authenticate with external APIs, the **credential vault** architecture guarantees that secrets remain outside the sandbox boundary, eliminating the risk of memory dumps or side-channel leaks exposing sensitive credentials.

## The L7 Injection Architecture

The credential vault operates by intercepting outbound HTTPS traffic at the host level and injecting authorization headers only after the request has exited the sandbox. This creates a strict security boundary where the micro-VM never possesses the raw secret value.

The request flow works as follows:

1. **Sandbox user code** issues an HTTP request *without* any `Authorization` header
2. **CubeVS** (the kernel eBPF layer) allows the outbound connection and tags the flow for L7 processing
3. **CubeEgress** (the OpenResty/Lua proxy) receives the request and matches it against per-sandbox policies containing `action.inject` rules
4. **Credential Vault** (host-side storage, typically under `/root/.pi/agent` or integrated with a secret manager) supplies the real secret via the `vault:get` lookup
5. **Injection** occurs as CubeEgress substitutes the secret into the header (e.g., `Authorization: {{SECRET}}`) before forwarding to the external service
6. **Response** returns through the proxy unchanged to the sandbox

In [`CubeEgress/lua/policy.lua`](https://github.com/TencentCloud/CubeSandbox/blob/main/CubeEgress/lua/policy.lua), the injection logic validates the rule before performing the lookup:

```lua
-- CubeEgress Lua policy (simplified)
if r.action.inject then
    for _, inj in ipairs(r.action.inject) do
        -- `inj.secret` is the identifier; the proxy looks it up in the vault
        local secret = vault:get(inj.secret)   -- never exposed to sandbox
        ngx.req.set_header(inj.header, secret)
    end
end

```

## Configuring Secret Injection Policies

Policies are serialized via [`network-agent/internal/cubeegress/wire.go`](https://github.com/TencentCloud/CubeSandbox/blob/main/network-agent/internal/cubeegress/wire.go) and translated by the SDK into `action.inject` configurations. The Python SDK ([`sdk/python/cubesandbox/_policy.py`](https://github.com/TencentCloud/CubeSandbox/blob/main/sdk/python/cubesandbox/_policy.py)) converts high-level declarations into the wire format expected by CubeEgress.

**Python SDK Example:**

```python
from cubesandbox import CubeEgress

policy = CubeEgress(
    rules=[
        {
            "match": {"host": "api.openai.com"},
            "action": {"allow": True, "inject": [{"header": "Authorization", "secret": "OPENAI_API_KEY"}]},
        }
    ]
)

sandbox = CubeSandbox()
sandbox.run(
    code="import requests; requests.get('https://api.openai.com/v1/models')",
    egress_policy=policy
)   # No key in the sandbox code

```

**Go SDK Example:**

```go
policy := []EgressRule{
    {
        Match: Match{Host: "api.openai.com"},
        Action: Action{
            Allow:  true,
            Inject: []Inject{{Header: "Authorization", Secret: "OPENAI_API_KEY"}},
        },
    },
}
sandbox := cubesandbox.New()
sandbox.Run(context.Background(), "curl https://api.openai.com/v1/models", policy)

```

The [`examples/pi-agent-integration/run_pi_agent.py`](https://github.com/TencentCloud/CubeSandbox/blob/main/examples/pi-agent-integration/run_pi_agent.py) file demonstrates this pattern explicitly, noting that *"the key never enters the VM"* while still allowing authenticated outbound requests.

## Security Guarantees and Observability

The architecture provides three critical security guarantees:

* **Zero-copy of secrets** – The secret value exists only in the CubeEgress host process; the sandbox’s memory and network buffers never contain the raw credential
* **Policy-driven validation** – Injection only occurs for explicitly allowed destinations validated by [`CubeEgress/lua/policy.lua`](https://github.com/TencentCloud/CubeSandbox/blob/main/CubeEgress/lua/policy.lua) against the per-sandbox policy
* **Automatic log redaction** – CubeMaster sanitizes all logs to prevent accidental exposure. In [`cubelog/logger.go`](https://github.com/TencentCloud/CubeSandbox/blob/main/cubelog/logger.go), secret values are replaced with `***REDACTED***` before any output reaches operators or logging systems

This design ensures that even if an attacker gains full control of the sandbox micro-VM, they cannot extract secrets from memory or network traffic because the credentials materialize only at the host-side proxy layer.

## Summary

* Secrets are injected at the CubeEgress L7 proxy layer, never reaching sandbox RAM or storage
* Policy-driven injection via `action.inject` rules validated in [`CubeEgress/lua/policy.lua`](https://github.com/TencentCloud/CubeSandbox/blob/main/CubeEgress/lua/policy.lua) restricts secrets to specific HTTPS destinations
* Host-side credential vault lookups occur outside the micro-VM boundary, maintaining strict isolation
* Automatic redaction in [`cubelog/logger.go`](https://github.com/TencentCloud/CubeSandbox/blob/main/cubelog/logger.go) prevents accidental secret exposure in operational logs
* The [`examples/pi-agent-integration/run_pi_agent.py`](https://github.com/TencentCloud/CubeSandbox/blob/main/examples/pi-agent-integration/run_pi_agent.py) reference implementation demonstrates production-ready secret isolation

## Frequently Asked Questions

### Where does the credential vault store the actual secret values?

The credential vault operates as a host-side store, typically located at `/root/.pi/agent` or integrated with external secret managers like AWS Secrets Manager or HashiCorp Vault. When CubeEgress evaluates an `action.inject` rule, it calls `vault:get(inj.secret)` to retrieve the credential from this host-side storage, ensuring the lookup never crosses into the sandbox boundary.

### How does the system prevent a compromised sandbox from extracting arbitrary secrets?

The [`CubeEgress/lua/policy.lua`](https://github.com/TencentCloud/CubeSandbox/blob/main/CubeEgress/lua/policy.lua) enforces strict destination matching before injection occurs. Secrets are only injected for HTTPS requests matching specific `host` patterns defined in the per-sandbox policy. A sandbox cannot request injection for arbitrary domains because the proxy validates the rule against the whitelist before performing the vault lookup, preventing credential exfiltration to attacker-controlled endpoints.

### Can sandbox code detect that secret injection is occurring?

No. From the sandbox's perspective, it sends unauthenticated HTTP requests and receives authenticated responses transparently. The injection happens at the network layer (CubeEgress) after the request leaves the micro-VM. The sandbox process sees no `Authorization` headers in its memory, no credential files on disk, and no environment variables containing secrets, making the injection mechanism completely opaque to the running code.

### What happens if the credential vault is temporarily unavailable?

If the vault lookup fails or the secret is missing, CubeEgress will not substitute the placeholder (e.g., `{{SECRET}}`) with a real value. Depending on the policy configuration, the request will either proceed without the header (likely causing the external service to reject it) or be dropped by the proxy, but in neither case will an invalid or empty secret leak into the sandbox memory.