From 7eacd21760cf13cf69ff703eddb17faab2b22835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B4=BE=E5=B2=9B?= Date: Thu, 28 May 2026 17:45:05 +0800 Subject: [PATCH 1/8] Add Credential Vault OSEP --- oseps/0012-credential-vault.md | 577 +++++++++++++++++++++++++++++++++ oseps/README.md | 1 + 2 files changed, 578 insertions(+) create mode 100644 oseps/0012-credential-vault.md diff --git a/oseps/0012-credential-vault.md b/oseps/0012-credential-vault.md new file mode 100644 index 000000000..3982b3319 --- /dev/null +++ b/oseps/0012-credential-vault.md @@ -0,0 +1,577 @@ +--- +title: Credential Vault and Credential Proxy +authors: + - "@jwx0925" +creation-date: 2026-05-28 +last-updated: 2026-05-28 +status: provisional +--- + +# OSEP-0012: Credential Vault and Credential Proxy + + +- [Summary](#summary) +- [Motivation](#motivation) + - [Goals](#goals) + - [Non-Goals](#non-goals) +- [Requirements](#requirements) +- [Proposal](#proposal) + - [Notes/Constraints/Caveats](#notesconstraintscaveats) + - [Risks and Mitigations](#risks-and-mitigations) +- [Design Details](#design-details) + - [Terminology](#terminology) + - [Architecture Overview](#architecture-overview) + - [Request Flow](#request-flow) + - [API Schema](#api-schema) + - [Credential Sources](#credential-sources) + - [Credential Injection](#credential-injection) + - [Runtime Modes](#runtime-modes) + - [Policy and Egress Integration](#policy-and-egress-integration) + - [Observability](#observability) + - [Component Changes](#component-changes) +- [Test Plan](#test-plan) +- [Drawbacks](#drawbacks) +- [Alternatives](#alternatives) +- [Infrastructure Needed](#infrastructure-needed) +- [Upgrade & Migration Strategy](#upgrade--migration-strategy) + + +## Summary + +This proposal introduces **Credential Vault**, a brokered credential layer for OpenSandbox, and **Credential Proxy**, the runtime component that injects scoped credentials into approved outbound requests without exposing plaintext credentials inside the sandbox. + +Instead of passing secrets through environment variables, files, or command arguments, users attach credential bindings to a sandbox. Credential Proxy evaluates the sandbox identity, destination, method, path, and injection policy before adding credentials to outbound HTTP requests. + +## Motivation + +AI agents frequently need credentials to call external systems such as GitHub, model APIs, cloud storage, package registries, databases, and internal services. Today the common approach is to place secrets inside the sandbox as environment variables or files. That makes the secret available to every process in the sandbox, and an untrusted or compromised agent can print, persist, exfiltrate, or transform the secret. + +OpenSandbox already provides isolated execution and per-sandbox egress control. The next security requirement is brokered credential use: a sandboxed agent should be able to use an approved credential for an approved destination, but it should not be able to read the underlying plaintext credential. + +Credential Vault extends OpenSandbox's sandbox security model from: + +- where code can run, +- what network destinations it can reach, + +to: + +- what credentials it can use for those destinations. + +### Goals + +1. **Brokered credentials**: Let sandboxed workloads use credentials without receiving plaintext secret values in the sandbox environment, filesystem, command line, or user-visible process output. +2. **Declarative binding**: Add a sandbox creation-time credential binding model that describes source, scope, and injection behavior. +3. **Policy-aware runtime injection**: Inject credentials only when sandbox identity, destination FQDN, HTTP method, and path all match the binding. +4. **Egress alignment**: Integrate with `networkPolicy.egress` so credential scope and network reachability are consistent. +5. **Runtime agnostic**: Support both Docker and Kubernetes through the existing egress sidecar pattern that shares the sandbox network namespace. +6. **Transparent by default**: Use the existing egress transparent mitmproxy path as Credential Proxy so applications do not need proxy or base URL changes. +7. **Auditable and redacted**: Emit useful audit events and metrics while redacting credential material from logs, diagnostics, and responses. +8. **Backward compatible**: Keep existing sandbox creation and egress behavior unchanged unless credential bindings are explicitly requested. + +### Non-Goals + +1. **General-purpose secret manager**: Credential Vault is not intended to replace HashiCorp Vault, Infisical, cloud secret managers, or Kubernetes Secret. It brokers credentials from configured sources into sandbox traffic. +2. **Secret lifecycle management**: Rotation, versioning, approval workflows, and cross-environment secret synchronization are out of scope for the initial design. +3. **Plaintext exposure inside sandbox**: This proposal does not add an API for sandbox processes to retrieve raw credential values. +4. **Generic body rewriting as MVP**: Request/response body mutation is out of scope for the MVP; header injection is sufficient for the first set of credential use cases. +5. **Per-process policy**: Credential policies apply to a sandbox, not to individual processes inside that sandbox. +6. **Non-HTTP protocols as MVP**: SSH, database wire protocols, Git smart protocol credential helpers, and arbitrary TCP credential injection are future work. +7. **Replacing egress policy**: Credential Vault complements egress control but does not replace network allow/deny enforcement. + +## Requirements + +| ID | Requirement | Priority | +|----|-------------|----------| +| R1 | Users can attach credential bindings to a sandbox at creation time | Must Have | +| R2 | Plaintext credentials are not exposed through sandbox env vars, files, lifecycle API responses, command output, or diagnostic APIs | Must Have | +| R3 | Credential Proxy injects credentials only for matching FQDN, HTTP method, and path scope | Must Have | +| R4 | Initial injection supports HTTP request headers | Must Have | +| R5 | Kubernetes Secret and server-local configuration can be used as credential sources | Must Have | +| R6 | Credential bindings can be validated against `networkPolicy.egress` when both are present | Should Have | +| R7 | Audit logs and metrics identify binding usage without logging credential values | Must Have | +| R8 | Docker and Kubernetes runtimes use the same user-facing API semantics | Must Have | +| R9 | Credential Proxy is default-deny for missing, invalid, or non-matching bindings | Must Have | +| R10 | The runtime uses egress transparent mitmproxy as the Credential Proxy implementation | Must Have | +| R11 | Future secret managers can be added through a provider interface | Should Have | + +## Proposal + +Add Credential Vault as a lifecycle API and server-side control-plane capability. Add Credential Proxy as the credential-aware runtime behavior of the existing egress transparent mitmproxy path. + +The first implementation supports **transparent proxy mode**: + +1. The user creates a sandbox with `credentialVault.bindings`. +2. OpenSandbox server validates bindings, resolves source references, and enables egress transparent mitmproxy for the sandbox. +3. The sandbox application container sends normal outbound HTTP/HTTPS traffic, for example to `https://api.github.com/repos/alibaba/OpenSandbox`. +4. Egress transparent mitmproxy intercepts outbound `TCP 80/443` traffic in the sandbox network namespace. +5. Credential Proxy evaluates the intercepted request against the sandbox credential bindings. +6. If exactly one binding matches and policy allows the request, Credential Proxy fetches or receives the credential material from a trusted source path and injects it into the request. +7. The external service receives the credential-bearing request; the sandbox process only sees the service response. + +At a high level: + +``` +┌───────────────────────────────────────────────────────────────────────┐ +│ OpenSandbox Server │ +│ │ +│ ┌──────────────────────┐ ┌──────────────────────────────────┐ │ +│ │ Lifecycle API │ │ Credential Vault Control Plane │ │ +│ │ - create sandbox │──────▶│ - validate binding │ │ +│ │ - store metadata │ │ - resolve source reference │ │ +│ │ - start runtime │ │ - provide credential bootstrap │ │ +│ └──────────────────────┘ └──────────────────────────────────┘ │ +└──────────────────────────────────────────┬────────────────────────────┘ + │ binding config + ▼ +┌───────────────────────────────────────────────────────────────────────┐ +│ Sandbox Pod / Network Namespace │ +│ │ +│ ┌──────────────────────┐ ┌───────────────────────────────┐ │ +│ │ Application Container │HTTP(S)│ Egress Sidecar / Credential │ │ +│ │ - no plaintext secret │────────▶│ Proxy (transparent mitmproxy) │ │ +│ │ - no proxy config │ │ - policy match │ │ +│ └──────────────────────┘ │ - injection, redaction, audit │ │ +│ └───────────────┬───────────────┘ │ +└────────────────────────────────────────────────────┼──────────────────┘ + │ authenticated request + ▼ + External Service +``` + +### Notes/Constraints/Caveats + +1. **Credential Proxy is the egress transparent mitmproxy path**: This proposal does not introduce an explicit proxy or local gateway mode. Applications keep using their normal target URLs. +2. **HTTPS interception requires trusted CA setup**: Transparent HTTPS injection depends on the sandbox trusting the mitmproxy root CA. Images or runtime startup must install the OpenSandbox MITM CA, otherwise HTTPS handshakes fail. +3. **Credential source access is control-plane trusted**: Runtime sidecars should not be granted broad cluster secret access. The server should resolve or mint scoped runtime material for only the requested sandbox bindings. +4. **Credential material must be short-lived in memory**: Credential Proxy should not persist plaintext credentials on disk. If temporary files are unavoidable for runtime bootstrap, they must be mounted read-only, scoped to one sandbox, and cleaned up with sandbox deletion. +5. **Binding scope must be narrower than or equal to egress scope**: A binding that injects a credential for a destination not allowed by egress policy should fail validation or produce a warning depending on compatibility mode. +6. **Multiple matching bindings are ambiguous**: If more than one binding matches a request and no deterministic precedence is declared, Credential Proxy must fail closed. + +### Risks and Mitigations + +| Risk | Impact | Mitigation | +|------|--------|------------| +| Sandbox bypasses Credential Proxy | Credential not injected, or traffic reaches destination without policy mediation | Use egress transparent redirect for TCP 80/443 and recommend `networkPolicy.defaultAction=deny` with `dns+nft` | +| Credential leakage through logs | Secret exposure | Central redaction helpers; never log injected headers or rendered values; regression tests for logs | +| Credential source over-permissioned to sidecars | Cluster-wide secret access risk | Server resolves sources and passes only sandbox-scoped material; sidecar has no Kubernetes API permission by default | +| Binding and egress policy drift | Credential may be configured for unreachable or unintended destinations | Validate binding targets against `networkPolicy.egress`; expose diagnostics for mismatches | +| Header injection into wrong host due to redirects | Credential sent to unintended destination | Re-evaluate policy after each redirected request; strip injected credentials on cross-host redirect unless target scope matches | +| HTTPS CA not trusted by sandbox image | Authenticated HTTPS requests fail | Install/export the OpenSandbox mitmproxy CA during sandbox startup or document image requirements | +| Multiple bindings match one request | Wrong credential injection | Fail closed unless a single highest-priority binding is configured | +| Long-lived credentials remain in proxy memory | Expanded exposure window | Cache with TTL, zero buffers where practical, prefer short-lived tokens from providers | +| Users expect full secret management | Product confusion | Document Credential Vault as a broker layer, not a standalone secret manager | + +## Design Details + +### Terminology + +- **Credential Vault**: OpenSandbox control-plane capability for declaring, validating, and managing credential bindings on sandboxes. +- **Credential Proxy**: Credential-aware runtime behavior in the egress sidecar's transparent mitmproxy path. It evaluates outbound HTTP/HTTPS requests and injects credentials when policy matches. +- **Credential Binding**: A per-sandbox declaration that connects a credential source to an allowed destination and injection rule. +- **Credential Source**: A trusted source of credential material, such as Kubernetes Secret or server-local configuration. +- **Credential Injection**: The act of adding credential material to an outbound request, for example as an `Authorization` header. + +### Architecture Overview + +Credential Vault should be modeled as a control-plane extension of sandbox lifecycle. Credential Proxy should be modeled as a credential-aware extension of the existing egress sidecar transparent mitmproxy path: + +- Egress sidecar controls which network destinations are reachable. +- Credential Proxy controls which credentials are attached to allowed outbound HTTP/HTTPS requests. + +For Kubernetes, this means the existing egress sidecar in the sandbox Pod starts mitmproxy transparent mode and loads OpenSandbox's credential addon. For Docker, this means the existing egress sidecar shares the sandbox network namespace, redirects outbound `80/443` traffic to mitmproxy, and runs the same credential addon. + +The egress sidecar already has the transparent MITM primitives required for Credential Proxy: + +- starts `mitmdump --mode transparent`, +- redirects outbound `TCP 80/443` traffic to the mitmproxy listener using `iptables`, +- loads system and user mitm addons, +- exports the mitmproxy root CA, +- exposes health readiness so sandboxes do not start before interception is ready. + +Credential Vault adds a first-party credential addon and binding bootstrap config to that path. + +### Request Flow + +For a GitHub read-only token binding: + +1. Sandbox process calls `https://api.github.com/repos/alibaba/OpenSandbox` normally. +2. Egress transparent mitmproxy intercepts the request and exposes host `api.github.com`, method `GET`, and path `/repos/alibaba/OpenSandbox` to the Credential Proxy addon. +3. Credential Proxy loads matching bindings for the sandbox. +4. It finds `github-readonly` where: + - `targets` contains `api.github.com`, + - `methods` contains `GET`, + - `paths` contains `/repos/*`, + - injection type is `header`. +5. It retrieves credential material from the scoped source path. +6. It sends the upstream request with: + +```http +Authorization: Bearer +``` + +7. It records an audit event with sandbox ID, binding name, target, method, path pattern, decision, and response status. The credential value is not logged. + +### API Schema + +Extension to `specs/sandbox-lifecycle.yml`: + +```yaml +components: + schemas: + CreateSandboxRequest: + properties: + credentialVault: + $ref: '#/components/schemas/CredentialVaultSpec' + + CredentialVaultSpec: + type: object + properties: + mode: + type: string + enum: [transparentProxy] + default: transparentProxy + bindings: + type: array + items: + $ref: '#/components/schemas/CredentialBinding' + additionalProperties: false + + CredentialBinding: + type: object + required: [name, sourceRef, scope, injection] + properties: + name: + type: string + description: Sandbox-local credential binding name. + sourceRef: + $ref: '#/components/schemas/CredentialSourceRef' + scope: + $ref: '#/components/schemas/CredentialScope' + injection: + $ref: '#/components/schemas/CredentialInjection' + additionalProperties: false + + CredentialSourceRef: + type: object + required: [type, name] + properties: + type: + type: string + enum: [kubernetesSecret, serverLocal] + name: + type: string + key: + type: string + additionalProperties: false + + CredentialScope: + type: object + required: [targets] + properties: + targets: + type: array + items: + type: string + description: FQDN or wildcard domain targets, for example api.github.com or *.example.com. + methods: + type: array + items: + type: string + default: [GET, POST, PUT, PATCH, DELETE] + paths: + type: array + items: + type: string + default: ["/*"] + additionalProperties: false + + CredentialInjection: + type: object + required: [type, name, value] + properties: + type: + type: string + enum: [header] + name: + type: string + example: Authorization + value: + type: string + example: Bearer {{ credential }} + additionalProperties: false +``` + +Example request: + +```json +{ + "image": "python:3.12", + "networkPolicy": { + "defaultAction": "deny", + "egress": [ + { "action": "allow", "target": "api.github.com" } + ] + }, + "credentialVault": { + "mode": "transparentProxy", + "bindings": [ + { + "name": "github-readonly", + "sourceRef": { + "type": "kubernetesSecret", + "name": "github-readonly-token", + "key": "token" + }, + "scope": { + "targets": ["api.github.com"], + "methods": ["GET"], + "paths": ["/repos/*", "/search/*"] + }, + "injection": { + "type": "header", + "name": "Authorization", + "value": "Bearer {{ credential }}" + } + } + ] + } +} +``` + +### Credential Sources + +The MVP supports two source types. + +1. **Kubernetes Secret** + - Available only for Kubernetes runtime. + - The OpenSandbox server reads the referenced secret through its existing service account permissions. + - Credential Proxy does not receive Kubernetes API permissions by default. + - The resolved value is passed to the proxy through a sandbox-scoped secret volume or bootstrap channel. + +2. **Server-local source** + - Available for Docker and local development. + - Configured in server TOML, for example: + +```toml +[credential_vault] +enabled = true + +[[credential_vault.sources]] +type = "server_local" +name = "github-readonly-token" +value_env = "OPENSANDBOX_GITHUB_READONLY_TOKEN" +``` + +Future providers may include HashiCorp Vault, Infisical, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, and internal credential brokers. + +### Credential Injection + +The MVP supports request header injection only. Credential Proxy injects the header into intercepted HTTP/HTTPS requests after the transparent mitmproxy path has decoded request metadata: + +```yaml +injection: + type: header + name: Authorization + value: "Bearer {{ credential }}" +``` + +Rules: + +- `{{ credential }}` is the only supported template variable in the MVP. +- Credential Proxy must reject templates that do not include `{{ credential }}`. +- Credential Proxy must reject attempts to inject hop-by-hop proxy headers unless explicitly allowed by implementation. +- Credential Proxy must remove any existing request header with the same name before injecting a credential, unless a future merge strategy is added. +- On redirect, Credential Proxy must re-evaluate target scope before preserving injected headers. + +### Runtime Modes + +The initial supported runtime mode is **transparent proxy**. + +#### Transparent Proxy Mode + +The runtime enables egress transparent mitmproxy when `credentialVault.bindings` is present. The application container keeps using normal outbound URLs. Credential Proxy runs as an OpenSandbox-managed mitm addon loaded by the egress sidecar. + +Advantages: + +- No application proxy or base URL changes. +- Reuses existing egress sidecar network namespace, `iptables` redirect, health gate, and mitmproxy integration. +- Works with existing HTTP clients, SDKs, CLIs, and agent-generated code as long as they use TCP `80/443` and trust the sandbox CA. +- Keeps credential policy enforcement at the egress boundary, where network policy is already enforced. + +Limitations: + +- Requires Linux network namespace support and `CAP_NET_ADMIN` for the egress sidecar. +- Requires the sandbox to trust the mitmproxy CA for HTTPS interception. +- Applies to HTTP/HTTPS traffic on `80/443`; non-HTTP protocols need future designs. +- In `ignore_hosts` pass-through mode, Credential Proxy cannot inspect or inject credentials for those hosts. + +### Policy and Egress Integration + +When both `credentialVault` and `networkPolicy` are present, the server should validate destination consistency. + +Recommended validation: + +- Every credential binding target must be covered by an allow rule in `networkPolicy.egress`. +- If `networkPolicy.defaultAction` is `allow`, the server should warn that credential-bearing requests may coexist with broad outbound access. +- If a binding target is not reachable under egress policy, sandbox creation should fail with HTTP 400 in strict mode. + +Suggested configuration: + +```toml +[credential_vault] +enabled = true +egress_validation = "strict" # strict | warn | disabled +``` + +Credential Proxy remains fail-closed even if egress validation is disabled. + +### Observability + +Credential Proxy should emit structured logs and metrics without credential values. + +Suggested audit log fields: + +- `sandbox_id` +- `credential_binding` +- `source_type` +- `target_host` +- `method` +- `path_pattern` +- `decision` (`injected`, `denied`, `no_match`, `ambiguous_match`, `source_error`) +- `status_code` +- `duration_ms` +- `request_id` + +Suggested metrics: + +- `opensandbox_credential_proxy_requests_total` +- `opensandbox_credential_proxy_injections_total` +- `opensandbox_credential_proxy_denials_total` +- `opensandbox_credential_proxy_source_errors_total` +- `opensandbox_credential_proxy_request_duration_seconds` + +All diagnostics APIs that surface runtime logs must preserve redaction behavior. + +### Component Changes + +#### Specs + +- Add `credentialVault` schemas to `specs/sandbox-lifecycle.yml`. +- Add examples for sandbox creation with credential bindings. +- Consider a future `credential-proxy-api.yaml` only if runtime policy inspection/mutation is exposed separately from the egress API. + +#### Server + +- Add config model for `[credential_vault]`. +- Add source provider interface. +- Validate `CreateSandboxRequest.credentialVault`. +- Persist credential binding metadata without plaintext credential values. +- Resolve or prepare sandbox-scoped credential material during sandbox creation. +- Enable egress transparent mitmproxy and credential addon bootstrap for Docker and Kubernetes runtimes when bindings are present. + +#### Components / Egress + +- Extend `components/egress` transparent mitmproxy support with a first-party credential addon. +- Load credential binding bootstrap config into the egress sidecar. +- Implement binding evaluation, header injection, redaction, and audit events in the mitm addon path. +- Keep the existing system addon behavior for streaming and user addon loading. + +#### Kubernetes + +- Enable the egress sidecar with transparent mitmproxy when credential bindings are present. +- Add secret projection or bootstrap delivery for sandbox-scoped credential material. +- Ensure Credential Proxy has no broad Kubernetes API permissions by default. +- Ensure the mitmproxy CA is trusted by the sandbox application container when HTTPS interception is enabled. + +#### Docker + +- Enable the egress sidecar with transparent mitmproxy sharing the sandbox network namespace. +- Ensure the mitmproxy CA is trusted by the sandbox application container when HTTPS interception is enabled. +- Clean up sidecar and sandbox-scoped credential material when the sandbox is deleted. + +#### SDKs and CLI + +- Add typed request models for credential bindings. +- Add examples for common providers such as GitHub and model APIs. +- CLI may include validation helpers, but it should not print credential values. + +## Test Plan + +### Unit Tests + +- Schema validation accepts valid bindings and rejects missing `name`, `sourceRef`, `scope`, or `injection`. +- FQDN, wildcard, method, and path matching work as expected. +- Multiple matching bindings fail closed. +- Existing headers with the injection name are replaced or rejected according to the selected implementation rule. +- Redaction removes credential values from logs and errors. +- Egress validation catches binding targets not allowed by `networkPolicy.egress`. + +### Integration Tests + +- Docker sandbox with server-local source can call a mock HTTP/HTTPS server that requires an injected header without setting proxy or base URL configuration. +- Docker sandbox cannot read credential value from environment variables, mounted files, lifecycle API response, command output, or diagnostics. +- Kubernetes sandbox with Kubernetes Secret source can call a mock HTTP/HTTPS server that requires an injected header without setting proxy or base URL configuration. +- Credential Proxy denies non-matching hosts, paths, and methods. +- Cross-host redirect strips or re-evaluates injected credentials. +- Sandbox deletion cleans up Credential Proxy and any sandbox-scoped credential material. + +### E2E Tests + +- Create a sandbox with `networkPolicy.defaultAction=deny`, allow `api.github.com`, bind a read-only GitHub credential, and verify a normal `https://api.github.com/...` call succeeds through Credential Proxy. +- Verify direct access to a non-allowed domain fails under egress policy. +- Verify logs and diagnostic APIs never contain the credential string. + +## Drawbacks + +- Requires enabling transparent MITM for credential-bearing HTTP/HTTPS traffic. +- Adds a new control-plane surface and a credential-aware path inside egress. +- Users may confuse Credential Vault with a full secret management system. +- Debugging outbound requests becomes more complex because credentials are injected outside the application process. +- Header injection covers common API use cases but not all credential workflows, such as SSH private keys or database passwords. + +## Alternatives + +### Inject Secrets as Environment Variables + +This is simple and already common, but it exposes plaintext credentials to the sandbox process. It does not satisfy the primary security goal. + +### Mount Secrets as Files + +This avoids environment variable leakage but still exposes plaintext credentials to sandbox processes. Agents can read, print, copy, or upload the files. + +### Rely Only on External Secret Managers + +External secret managers are still needed as sources, but sandbox workloads would need secret manager credentials to fetch secrets directly. That moves the same exposure problem into a different API. + +### SDK-only Credential Clients + +SDK mediation can be safer and more structured, but it requires language-specific client changes and does not cover existing CLIs, package managers, curl, git-over-HTTPS, or arbitrary agent-generated code. Credential Proxy works at the runtime egress boundary. + +### Explicit Proxy or Local Gateway First + +Explicit proxy and local gateway modes avoid transparent network interception, but they require application configuration and do not match the current OpenSandbox egress direction. The existing egress transparent mitmproxy path already provides the correct runtime interception point for Credential Proxy. + +## Infrastructure Needed + +- No new Credential Proxy component image for the MVP; Credential Proxy is implemented in the existing egress image through transparent mitmproxy and a first-party credential addon. +- Server configuration for credential source providers. +- Kubernetes RBAC for server-side secret reads where Kubernetes Secret sources are enabled. +- CI tests for Docker and Kubernetes runtime paths. +- Documentation and examples for common credential binding patterns. +- Sandbox image or runtime support for trusting the OpenSandbox mitmproxy CA. + +No new required external service is introduced by the MVP. + +## Upgrade & Migration Strategy + +Credential Vault is opt-in. Existing sandboxes, SDK calls, egress policies, and runtime behavior remain unchanged when `credentialVault` is omitted. + +Recommended rollout: + +1. Add schema and server validation behind `[credential_vault].enabled = false` by default. +2. Extend egress transparent mitmproxy with credential addon support and server-local source for local development. +3. Implement Kubernetes Secret source and egress sidecar credential bootstrap. +4. Add SDK models and CLI examples. +5. Document production guidance: use `networkPolicy.defaultAction=deny`, keep credential targets narrow, avoid broad methods and paths, and monitor audit events. + +No migration is required for existing users. Users currently injecting secrets through environment variables can gradually migrate by moving those values into configured credential sources and letting Credential Proxy inject them into normal outbound HTTP/HTTPS calls. diff --git a/oseps/README.md b/oseps/README.md index f22928ca9..bc2dacda2 100644 --- a/oseps/README.md +++ b/oseps/README.md @@ -17,3 +17,4 @@ This is the complete list of OpenSandbox Enhancement Proposals: | [OSEP-0009](0009-auto-renew-sandbox-on-ingress-access.md) | Auto-Renew Sandbox on Ingress Access | implemented | 2026-03-23 | | [OSEP-0010](0010-opentelemetry-instrumentation.md) | OpenTelemetry Metrics and Logs (execd, egress, and ingress) | implementing | 2026-04-12 | | [OSEP-0011](0011-secure-access-endpoint.md) | Secure Access on GetEndpoint and Signed Endpoint | implemented | 2026-04-25 | +| [OSEP-0012](0012-credential-vault.md) | Credential Vault and Credential Proxy | provisional | 2026-05-28 | From 88567c1d32c1253954de3ab5907fd16ea654ea4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B4=BE=E5=B2=9B?= Date: Thu, 28 May 2026 18:03:49 +0800 Subject: [PATCH 2/8] Add inline ephemeral credential source --- oseps/0012-credential-vault.md | 41 ++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/oseps/0012-credential-vault.md b/oseps/0012-credential-vault.md index 3982b3319..28897459f 100644 --- a/oseps/0012-credential-vault.md +++ b/oseps/0012-credential-vault.md @@ -86,7 +86,7 @@ to: | R2 | Plaintext credentials are not exposed through sandbox env vars, files, lifecycle API responses, command output, or diagnostic APIs | Must Have | | R3 | Credential Proxy injects credentials only for matching FQDN, HTTP method, and path scope | Must Have | | R4 | Initial injection supports HTTP request headers | Must Have | -| R5 | Kubernetes Secret and server-local configuration can be used as credential sources | Must Have | +| R5 | Kubernetes Secret, server-local configuration, and inline ephemeral values can be used as credential sources | Must Have | | R6 | Credential bindings can be validated against `networkPolicy.egress` when both are present | Should Have | | R7 | Audit logs and metrics identify binding usage without logging credential values | Must Have | | R8 | Docker and Kubernetes runtimes use the same user-facing API semantics | Must Have | @@ -153,6 +153,7 @@ At a high level: |------|--------|------------| | Sandbox bypasses Credential Proxy | Credential not injected, or traffic reaches destination without policy mediation | Use egress transparent redirect for TCP 80/443 and recommend `networkPolicy.defaultAction=deny` with `dns+nft` | | Credential leakage through logs | Secret exposure | Central redaction helpers; never log injected headers or rendered values; regression tests for logs | +| Inline ephemeral value leaked by lifecycle logging | Secret exposure | Treat `inlineEphemeral.value` as write-only input; redact request bodies, validation errors, SDK debug logs, and persisted sandbox metadata | | Credential source over-permissioned to sidecars | Cluster-wide secret access risk | Server resolves sources and passes only sandbox-scoped material; sidecar has no Kubernetes API permission by default | | Binding and egress policy drift | Credential may be configured for unreachable or unintended destinations | Validate binding targets against `networkPolicy.egress`; expose diagnostics for mismatches | | Header injection into wrong host due to redirects | Credential sent to unintended destination | Re-evaluate policy after each redirected request; strip injected credentials on cross-host redirect unless target scope matches | @@ -253,15 +254,21 @@ components: CredentialSourceRef: type: object - required: [type, name] + required: [type] properties: type: type: string - enum: [kubernetesSecret, serverLocal] + enum: [kubernetesSecret, serverLocal, inlineEphemeral] name: type: string + description: Provider-local credential source name. Required for kubernetesSecret and serverLocal; omitted for inlineEphemeral. key: type: string + description: Provider-local key name for multi-key sources. + value: + type: string + writeOnly: true + description: Inline ephemeral credential value accepted only at sandbox creation time. Never returned, logged, or persisted as plaintext. additionalProperties: false CredentialScope: @@ -340,7 +347,7 @@ Example request: ### Credential Sources -The MVP supports two source types. +The MVP supports three source types. 1. **Kubernetes Secret** - Available only for Kubernetes runtime. @@ -362,6 +369,25 @@ name = "github-readonly-token" value_env = "OPENSANDBOX_GITHUB_READONLY_TOKEN" ``` +3. **Inline ephemeral source** + - Available for cases where an upper-layer platform creates a sandbox-scoped credential at sandbox creation time. + - The inline value is accepted only in `CreateSandboxRequest`. + - The OpenSandbox server must treat the value as write-only: do not return it in lifecycle responses, do not persist it as plaintext, and redact it from logs and validation errors. + - The server converts the value into sandbox-scoped runtime credential material for the egress sidecar / Credential Proxy. + - For Kubernetes, the runtime material may be represented as a generated Secret that is mounted only into the egress sidecar, not the application container. + - Generated runtime Secrets must be labeled with the sandbox identity and cleaned up when the sandbox is deleted. Use `ownerReferences` where possible and finalizers only when external revocation or cross-namespace cleanup is required. + +Example: + +```json +{ + "sourceRef": { + "type": "inlineEphemeral", + "value": "ghp_xxx" + } +} +``` + Future providers may include HashiCorp Vault, Infisical, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, and internal credential brokers. ### Credential Injection @@ -467,6 +493,7 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Validate `CreateSandboxRequest.credentialVault`. - Persist credential binding metadata without plaintext credential values. - Resolve or prepare sandbox-scoped credential material during sandbox creation. +- Redact `inlineEphemeral.value` from request logging, validation errors, persisted metadata, and lifecycle responses. - Enable egress transparent mitmproxy and credential addon bootstrap for Docker and Kubernetes runtimes when bindings are present. #### Components / Egress @@ -480,6 +507,8 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Enable the egress sidecar with transparent mitmproxy when credential bindings are present. - Add secret projection or bootstrap delivery for sandbox-scoped credential material. +- For `inlineEphemeral`, optionally create a generated sandbox-scoped Kubernetes Secret mounted only into the egress sidecar. +- Generated runtime Secrets must use labels and `ownerReferences` when possible; finalizers are reserved for cleanup that Kubernetes garbage collection cannot cover. - Ensure Credential Proxy has no broad Kubernetes API permissions by default. - Ensure the mitmproxy CA is trusted by the sandbox application container when HTTPS interception is enabled. @@ -504,13 +533,16 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Multiple matching bindings fail closed. - Existing headers with the injection name are replaced or rejected according to the selected implementation rule. - Redaction removes credential values from logs and errors. +- `inlineEphemeral.value` is accepted only as write-only create input and never appears in serialized sandbox metadata or API responses. - Egress validation catches binding targets not allowed by `networkPolicy.egress`. ### Integration Tests - Docker sandbox with server-local source can call a mock HTTP/HTTPS server that requires an injected header without setting proxy or base URL configuration. +- Docker sandbox with inline ephemeral source can call a mock HTTP/HTTPS server and cannot recover the inline credential from environment, filesystem, diagnostics, or lifecycle responses. - Docker sandbox cannot read credential value from environment variables, mounted files, lifecycle API response, command output, or diagnostics. - Kubernetes sandbox with Kubernetes Secret source can call a mock HTTP/HTTPS server that requires an injected header without setting proxy or base URL configuration. +- Kubernetes sandbox with inline ephemeral source creates sandbox-scoped runtime material mounted only into the egress sidecar and cleans it up on sandbox deletion. - Credential Proxy denies non-matching hosts, paths, and methods. - Cross-host redirect strips or re-evaluates injected credentials. - Sandbox deletion cleans up Credential Proxy and any sandbox-scoped credential material. @@ -556,6 +588,7 @@ Explicit proxy and local gateway modes avoid transparent network interception, b - No new Credential Proxy component image for the MVP; Credential Proxy is implemented in the existing egress image through transparent mitmproxy and a first-party credential addon. - Server configuration for credential source providers. - Kubernetes RBAC for server-side secret reads where Kubernetes Secret sources are enabled. +- Kubernetes permission to create/delete sandbox-scoped runtime Secrets when `inlineEphemeral` is enabled for Kubernetes runtime. - CI tests for Docker and Kubernetes runtime paths. - Documentation and examples for common credential binding patterns. - Sandbox image or runtime support for trusting the OpenSandbox mitmproxy CA. From e7c9a44762340fae73e2a87286f5cdb89ac80afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B4=BE=E5=B2=9B?= Date: Thu, 28 May 2026 18:34:34 +0800 Subject: [PATCH 3/8] Tighten Credential Vault security requirements --- oseps/0012-credential-vault.md | 99 +++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 20 deletions(-) diff --git a/oseps/0012-credential-vault.md b/oseps/0012-credential-vault.md index 28897459f..24ea63ceb 100644 --- a/oseps/0012-credential-vault.md +++ b/oseps/0012-credential-vault.md @@ -25,6 +25,7 @@ status: provisional - [API Schema](#api-schema) - [Credential Sources](#credential-sources) - [Credential Injection](#credential-injection) + - [Response Redaction and Echo Handling](#response-redaction-and-echo-handling) - [Runtime Modes](#runtime-modes) - [Policy and Egress Integration](#policy-and-egress-integration) - [Observability](#observability) @@ -59,7 +60,7 @@ to: ### Goals -1. **Brokered credentials**: Let sandboxed workloads use credentials without receiving plaintext secret values in the sandbox environment, filesystem, command line, or user-visible process output. +1. **Brokered credentials**: Let sandboxed workloads use credentials without receiving plaintext secret values through OpenSandbox-managed environment variables, files, lifecycle API responses, diagnostics, or logs. 2. **Declarative binding**: Add a sandbox creation-time credential binding model that describes source, scope, and injection behavior. 3. **Policy-aware runtime injection**: Inject credentials only when sandbox identity, destination FQDN, HTTP method, and path all match the binding. 4. **Egress alignment**: Integrate with `networkPolicy.egress` so credential scope and network reachability are consistent. @@ -83,16 +84,17 @@ to: | ID | Requirement | Priority | |----|-------------|----------| | R1 | Users can attach credential bindings to a sandbox at creation time | Must Have | -| R2 | Plaintext credentials are not exposed through sandbox env vars, files, lifecycle API responses, command output, or diagnostic APIs | Must Have | -| R3 | Credential Proxy injects credentials only for matching FQDN, HTTP method, and path scope | Must Have | +| R2 | Plaintext credentials are not exposed through OpenSandbox-managed sandbox env vars, files, lifecycle API responses, diagnostics, or logs | Must Have | +| R3 | Credential Proxy injects credentials only for matching scheme, port, FQDN, HTTP method, and path scope | Must Have | | R4 | Initial injection supports HTTP request headers | Must Have | | R5 | Kubernetes Secret, server-local configuration, and inline ephemeral values can be used as credential sources | Must Have | -| R6 | Credential bindings can be validated against `networkPolicy.egress` when both are present | Should Have | +| R6 | Credential-enabled sandboxes require explicit `networkPolicy.egress` coverage for every binding target | Must Have | | R7 | Audit logs and metrics identify binding usage without logging credential values | Must Have | | R8 | Docker and Kubernetes runtimes use the same user-facing API semantics | Must Have | | R9 | Credential Proxy is default-deny for missing, invalid, or non-matching bindings | Must Have | | R10 | The runtime uses egress transparent mitmproxy as the Credential Proxy implementation | Must Have | -| R11 | Future secret managers can be added through a provider interface | Should Have | +| R11 | Credential-enabled egress startup fails closed when transparent redirect, mitm readiness, CA bootstrap, or egress API auth cannot be configured | Must Have | +| R12 | Future secret managers can be added through a provider interface | Should Have | ## Proposal @@ -104,7 +106,7 @@ The first implementation supports **transparent proxy mode**: 2. OpenSandbox server validates bindings, resolves source references, and enables egress transparent mitmproxy for the sandbox. 3. The sandbox application container sends normal outbound HTTP/HTTPS traffic, for example to `https://api.github.com/repos/alibaba/OpenSandbox`. 4. Egress transparent mitmproxy intercepts outbound `TCP 80/443` traffic in the sandbox network namespace. -5. Credential Proxy evaluates the intercepted request against the sandbox credential bindings. +5. Credential Proxy evaluates the intercepted request against the sandbox credential bindings, including scheme and port. 6. If exactly one binding matches and policy allows the request, Credential Proxy fetches or receives the credential material from a trusted source path and injects it into the request. 7. The external service receives the credential-bearing request; the sandbox process only sees the service response. @@ -144,8 +146,9 @@ At a high level: 2. **HTTPS interception requires trusted CA setup**: Transparent HTTPS injection depends on the sandbox trusting the mitmproxy root CA. Images or runtime startup must install the OpenSandbox MITM CA, otherwise HTTPS handshakes fail. 3. **Credential source access is control-plane trusted**: Runtime sidecars should not be granted broad cluster secret access. The server should resolve or mint scoped runtime material for only the requested sandbox bindings. 4. **Credential material must be short-lived in memory**: Credential Proxy should not persist plaintext credentials on disk. If temporary files are unavoidable for runtime bootstrap, they must be mounted read-only, scoped to one sandbox, and cleaned up with sandbox deletion. -5. **Binding scope must be narrower than or equal to egress scope**: A binding that injects a credential for a destination not allowed by egress policy should fail validation or produce a warning depending on compatibility mode. +5. **Binding scope must be covered by egress scope**: A credential-enabled sandbox must include `networkPolicy.egress`, and every credential binding target must be covered by an allow rule. Missing or inconsistent egress policy fails sandbox creation. 6. **Multiple matching bindings are ambiguous**: If more than one binding matches a request and no deterministic precedence is declared, Credential Proxy must fail closed. +7. **Upstream echo is outside the absolute secrecy guarantee**: OpenSandbox prevents its own control plane and runtime surfaces from exposing credentials, but an upstream service can still echo request headers in response bodies or headers. Credential Proxy should redact known credential values from responses where practical, and users should avoid binding credentials to services that echo sensitive request headers. ### Risks and Mitigations @@ -153,11 +156,17 @@ At a high level: |------|--------|------------| | Sandbox bypasses Credential Proxy | Credential not injected, or traffic reaches destination without policy mediation | Use egress transparent redirect for TCP 80/443 and recommend `networkPolicy.defaultAction=deny` with `dns+nft` | | Credential leakage through logs | Secret exposure | Central redaction helpers; never log injected headers or rendered values; regression tests for logs | +| Upstream echoes injected credential | Credential appears in sandbox-visible response content | Redact known credential values from response headers and text bodies where practical; document that services which echo sensitive request headers are unsupported unless response redaction is sufficient | | Inline ephemeral value leaked by lifecycle logging | Secret exposure | Treat `inlineEphemeral.value` as write-only input; redact request bodies, validation errors, SDK debug logs, and persisted sandbox metadata | | Credential source over-permissioned to sidecars | Cluster-wide secret access risk | Server resolves sources and passes only sandbox-scoped material; sidecar has no Kubernetes API permission by default | -| Binding and egress policy drift | Credential may be configured for unreachable or unintended destinations | Validate binding targets against `networkPolicy.egress`; expose diagnostics for mismatches | +| Arbitrary Kubernetes Secret binding | Sandbox creator can use server RBAC to access unrelated cluster Secrets | Require configured source providers, namespace scope, allowlists, and requester authorization before resolving any Kubernetes Secret | +| Binding and egress policy drift | Credential may be configured for unreachable or unintended destinations | Require `networkPolicy.egress` and fail creation when any binding target is not covered | | Header injection into wrong host due to redirects | Credential sent to unintended destination | Re-evaluate policy after each redirected request; strip injected credentials on cross-host redirect unless target scope matches | +| Credential injected over cleartext HTTP | Credential exposed on the network | Default binding scope to `schemes: [https]` and `ports: [443]`; require explicit opt-in for any HTTP injection | | HTTPS CA not trusted by sandbox image | Authenticated HTTPS requests fail | Install/export the OpenSandbox mitmproxy CA during sandbox startup or document image requirements | +| Transparent redirect unavailable | Credential traffic bypasses proxy policy | Fail sandbox creation/readiness when credential bindings are present and mitmproxy, iptables redirect, CA bootstrap, or egress auth cannot be configured | +| User mitm addon observes injected headers | Credential exposure through extension hook | Disable user-provided mitm addons for credential-enabled sandboxes unless a future isolation model prevents addon access to credential-bearing flows | +| Egress policy API unauthenticated | Sandbox process rewrites sidecar policy | Always provision `OPENSANDBOX_EGRESS_TOKEN` for credential-enabled sidecars, even when network policy would otherwise be omitted | | Multiple bindings match one request | Wrong credential injection | Fail closed unless a single highest-priority binding is configured | | Long-lived credentials remain in proxy memory | Expanded exposure window | Cache with TTL, zero buffers where practical, prefer short-lived tokens from providers | | Users expect full secret management | Product confusion | Document Credential Vault as a broker layer, not a standalone secret manager | @@ -275,6 +284,19 @@ components: type: object required: [targets] properties: + schemes: + type: array + items: + type: string + enum: [https, http] + default: [https] + description: URL schemes eligible for credential injection. Defaults to HTTPS only. + ports: + type: array + items: + type: integer + default: [443] + description: Destination ports eligible for credential injection. Defaults to 443. targets: type: array items: @@ -330,6 +352,8 @@ Example request: "key": "token" }, "scope": { + "schemes": ["https"], + "ports": [443], "targets": ["api.github.com"], "methods": ["GET"], "paths": ["/repos/*", "/search/*"] @@ -351,7 +375,9 @@ The MVP supports three source types. 1. **Kubernetes Secret** - Available only for Kubernetes runtime. - - The OpenSandbox server reads the referenced secret through its existing service account permissions. + - The OpenSandbox server reads the referenced secret through a configured source provider, not arbitrary namespace/name input from the sandbox creator. + - The source provider must define allowed namespaces, allowed secret names or label selectors, and requester authorization rules. + - Sandbox creation must fail if the requester is not authorized to bind the referenced Kubernetes Secret. - Credential Proxy does not receive Kubernetes API permissions by default. - The resolved value is passed to the proxy through a sandbox-scoped secret volume or bootstrap channel. @@ -405,10 +431,24 @@ Rules: - `{{ credential }}` is the only supported template variable in the MVP. - Credential Proxy must reject templates that do not include `{{ credential }}`. +- Credential Proxy must inject only for HTTPS on port 443 by default. +- Any HTTP or non-443 injection requires explicit `scope.schemes` and `scope.ports` opt-in and should be rejected by default platform policy unless the operator enables it. - Credential Proxy must reject attempts to inject hop-by-hop proxy headers unless explicitly allowed by implementation. - Credential Proxy must remove any existing request header with the same name before injecting a credential, unless a future merge strategy is added. - On redirect, Credential Proxy must re-evaluate target scope before preserving injected headers. +### Response Redaction and Echo Handling + +Credential Proxy should redact known credential values from upstream response headers and text-like response bodies where practical. This protects against common debug endpoints and error handlers that echo request headers. + +This redaction is best effort, not an absolute secrecy guarantee: + +- Binary, compressed, encrypted, streaming, or very large response bodies may be passed without full body rewriting. +- If response redaction is disabled or not possible, Credential Proxy should at least strip or redact response headers that contain known credential values. +- Operators should not bind credentials to upstream services that intentionally echo sensitive request headers back to callers unless the response path is known to be safely redacted. + +The formal guarantee is that OpenSandbox-controlled surfaces do not expose plaintext credentials. It cannot guarantee that arbitrary upstream services will never include an injected credential in application-level response content. + ### Runtime Modes The initial supported runtime mode is **transparent proxy**. @@ -430,26 +470,30 @@ Limitations: - Requires the sandbox to trust the mitmproxy CA for HTTPS interception. - Applies to HTTP/HTTPS traffic on `80/443`; non-HTTP protocols need future designs. - In `ignore_hosts` pass-through mode, Credential Proxy cannot inspect or inject credentials for those hosts. +- Credential-enabled startup must fail closed if mitmproxy, `iptables` redirect, CA bootstrap, credential addon loading, or egress API authentication cannot be configured. +- User-provided mitm addons are disabled for credential-enabled sandboxes unless a future isolation model prevents them from observing credential-bearing flows. ### Policy and Egress Integration -When both `credentialVault` and `networkPolicy` are present, the server should validate destination consistency. +Credential-enabled sandboxes must include `networkPolicy.egress`. The server must validate destination consistency before sandbox creation. -Recommended validation: +Required validation: - Every credential binding target must be covered by an allow rule in `networkPolicy.egress`. -- If `networkPolicy.defaultAction` is `allow`, the server should warn that credential-bearing requests may coexist with broad outbound access. -- If a binding target is not reachable under egress policy, sandbox creation should fail with HTTP 400 in strict mode. +- `networkPolicy.defaultAction` should be `deny`; if `allow` is accepted for compatibility, the server must still require explicit allow coverage for every binding target and warn that broad outbound access coexists with credential-bearing traffic. +- If `networkPolicy` is omitted, if `egress` is empty, or if a binding target is not reachable under egress policy, sandbox creation must fail with HTTP 400. Suggested configuration: ```toml [credential_vault] enabled = true -egress_validation = "strict" # strict | warn | disabled +egress_validation = "strict" ``` -Credential Proxy remains fail-closed even if egress validation is disabled. +For credential-enabled sandboxes, strict validation is required. Non-strict egress validation would allow credential-bearing traffic to run without an explicit network policy boundary. + +Credential-enabled sidecars must always provision `OPENSANDBOX_EGRESS_TOKEN` for the egress policy API, including cases where the egress sidecar starts solely because credentials are enabled. The application container must not receive this token. ### Observability @@ -491,17 +535,20 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Add config model for `[credential_vault]`. - Add source provider interface. - Validate `CreateSandboxRequest.credentialVault`. +- Require and validate `networkPolicy.egress` for credential-enabled sandboxes. - Persist credential binding metadata without plaintext credential values. - Resolve or prepare sandbox-scoped credential material during sandbox creation. - Redact `inlineEphemeral.value` from request logging, validation errors, persisted metadata, and lifecycle responses. -- Enable egress transparent mitmproxy and credential addon bootstrap for Docker and Kubernetes runtimes when bindings are present. +- Enable egress transparent mitmproxy, egress API auth, and credential addon bootstrap for Docker and Kubernetes runtimes when bindings are present. #### Components / Egress - Extend `components/egress` transparent mitmproxy support with a first-party credential addon. - Load credential binding bootstrap config into the egress sidecar. - Implement binding evaluation, header injection, redaction, and audit events in the mitm addon path. -- Keep the existing system addon behavior for streaming and user addon loading. +- Keep the existing system addon behavior for streaming. +- Disable user-provided mitm addon loading for credential-enabled sandboxes until addon isolation is designed. +- Fail readiness/startup when transparent redirect, credential addon loading, CA bootstrap, or egress API auth is unavailable for a credential-enabled sandbox. #### Kubernetes @@ -511,12 +558,14 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Generated runtime Secrets must use labels and `ownerReferences` when possible; finalizers are reserved for cleanup that Kubernetes garbage collection cannot cover. - Ensure Credential Proxy has no broad Kubernetes API permissions by default. - Ensure the mitmproxy CA is trusted by the sandbox application container when HTTPS interception is enabled. +- Ensure generated egress API auth material is available only to the control plane and egress sidecar, not the application container. #### Docker - Enable the egress sidecar with transparent mitmproxy sharing the sandbox network namespace. - Ensure the mitmproxy CA is trusted by the sandbox application container when HTTPS interception is enabled. - Clean up sidecar and sandbox-scoped credential material when the sandbox is deleted. +- Ensure generated egress API auth material is not exposed to the application container. #### SDKs and CLI @@ -529,21 +578,27 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. ### Unit Tests - Schema validation accepts valid bindings and rejects missing `name`, `sourceRef`, `scope`, or `injection`. -- FQDN, wildcard, method, and path matching work as expected. +- Scheme, port, FQDN, wildcard, method, and path matching work as expected. +- Injection defaults to HTTPS/443 only and rejects HTTP injection unless explicitly configured and permitted. - Multiple matching bindings fail closed. - Existing headers with the injection name are replaced or rejected according to the selected implementation rule. - Redaction removes credential values from logs and errors. +- Response redaction removes known credential values from response headers and supported text bodies. - `inlineEphemeral.value` is accepted only as write-only create input and never appears in serialized sandbox metadata or API responses. -- Egress validation catches binding targets not allowed by `networkPolicy.egress`. +- Egress validation requires `networkPolicy.egress` and catches binding targets not allowed by policy. +- Kubernetes Secret source providers reject namespace/name references that are outside configured allowlists or requester authorization. ### Integration Tests - Docker sandbox with server-local source can call a mock HTTP/HTTPS server that requires an injected header without setting proxy or base URL configuration. - Docker sandbox with inline ephemeral source can call a mock HTTP/HTTPS server and cannot recover the inline credential from environment, filesystem, diagnostics, or lifecycle responses. -- Docker sandbox cannot read credential value from environment variables, mounted files, lifecycle API response, command output, or diagnostics. +- Docker sandbox cannot read credential value from environment variables, mounted files, lifecycle API response, or diagnostics. - Kubernetes sandbox with Kubernetes Secret source can call a mock HTTP/HTTPS server that requires an injected header without setting proxy or base URL configuration. - Kubernetes sandbox with inline ephemeral source creates sandbox-scoped runtime material mounted only into the egress sidecar and cleans it up on sandbox deletion. - Credential Proxy denies non-matching hosts, paths, and methods. +- Credential-enabled sandbox creation/readiness fails when transparent redirect, mitm readiness, CA bootstrap, credential addon loading, or egress API auth cannot be configured. +- Credential-enabled sidecars require egress API auth even when credentials are the only reason the egress sidecar starts. +- User-provided mitm addons are not loaded for credential-enabled sandboxes. - Cross-host redirect strips or re-evaluates injected credentials. - Sandbox deletion cleans up Credential Proxy and any sandbox-scoped credential material. @@ -552,11 +607,15 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Create a sandbox with `networkPolicy.defaultAction=deny`, allow `api.github.com`, bind a read-only GitHub credential, and verify a normal `https://api.github.com/...` call succeeds through Credential Proxy. - Verify direct access to a non-allowed domain fails under egress policy. - Verify logs and diagnostic APIs never contain the credential string. +- Verify a mock upstream that echoes request headers does not expose known credential values in supported response headers or text bodies. ## Drawbacks - Requires enabling transparent MITM for credential-bearing HTTP/HTTPS traffic. - Adds a new control-plane surface and a credential-aware path inside egress. +- Requires stricter startup behavior than ordinary egress policy; credential-enabled sandboxes fail closed instead of gracefully degrading when transparent interception is unavailable. +- Disables user-provided mitm addons for credential-enabled sandboxes until addon isolation is available. +- Cannot provide an absolute secrecy guarantee against arbitrary upstream services that echo credentials in unsupported response encodings or protocols. - Users may confuse Credential Vault with a full secret management system. - Debugging outbound requests becomes more complex because credentials are injected outside the application process. - Header injection covers common API use cases but not all credential workflows, such as SSH private keys or database passwords. From b2aa652dfbb524620abd94ca8438a44dffa37a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B4=BE=E5=B2=9B?= Date: Fri, 29 May 2026 07:28:00 +0800 Subject: [PATCH 4/8] Add credential binding templates --- oseps/0012-credential-vault.md | 166 ++++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 5 deletions(-) diff --git a/oseps/0012-credential-vault.md b/oseps/0012-credential-vault.md index 24ea63ceb..bacfea0d7 100644 --- a/oseps/0012-credential-vault.md +++ b/oseps/0012-credential-vault.md @@ -23,6 +23,7 @@ status: provisional - [Architecture Overview](#architecture-overview) - [Request Flow](#request-flow) - [API Schema](#api-schema) + - [Binding Templates](#binding-templates) - [Credential Sources](#credential-sources) - [Credential Injection](#credential-injection) - [Response Redaction and Echo Handling](#response-redaction-and-echo-handling) @@ -61,7 +62,7 @@ to: ### Goals 1. **Brokered credentials**: Let sandboxed workloads use credentials without receiving plaintext secret values through OpenSandbox-managed environment variables, files, lifecycle API responses, diagnostics, or logs. -2. **Declarative binding**: Add a sandbox creation-time credential binding model that describes source, scope, and injection behavior. +2. **Declarative binding**: Add a sandbox creation-time credential binding model that describes source, scope, and injection behavior, with operator-approved templates for common patterns. 3. **Policy-aware runtime injection**: Inject credentials only when sandbox identity, destination FQDN, HTTP method, and path all match the binding. 4. **Egress alignment**: Integrate with `networkPolicy.egress` so credential scope and network reachability are consistent. 5. **Runtime agnostic**: Support both Docker and Kubernetes through the existing egress sidecar pattern that shares the sandbox network namespace. @@ -94,7 +95,8 @@ to: | R9 | Credential Proxy is default-deny for missing, invalid, or non-matching bindings | Must Have | | R10 | The runtime uses egress transparent mitmproxy as the Credential Proxy implementation | Must Have | | R11 | Credential-enabled egress startup fails closed when transparent redirect, mitm readiness, CA bootstrap, or egress API auth cannot be configured | Must Have | -| R12 | Future secret managers can be added through a provider interface | Should Have | +| R12 | Users can reference built-in or operator-configured binding templates instead of repeating full scope and injection rules | Should Have | +| R13 | Future secret managers can be added through a provider interface | Should Have | ## Proposal @@ -178,6 +180,7 @@ At a high level: - **Credential Vault**: OpenSandbox control-plane capability for declaring, validating, and managing credential bindings on sandboxes. - **Credential Proxy**: Credential-aware runtime behavior in the egress sidecar's transparent mitmproxy path. It evaluates outbound HTTP/HTTPS requests and injects credentials when policy matches. - **Credential Binding**: A per-sandbox declaration that connects a credential source to an allowed destination and injection rule. +- **Credential Binding Template**: A built-in or operator-configured template that expands user parameters and a credential source into a full credential binding. - **Credential Source**: A trusted source of credential material, such as Kubernetes Secret or server-local configuration. - **Credential Injection**: The act of adding credential material to an outbound request, for example as an `Authorization` header. @@ -248,11 +251,15 @@ components: CredentialBinding: type: object - required: [name, sourceRef, scope, injection] + required: [name] properties: name: type: string description: Sandbox-local credential binding name. + templateRef: + $ref: '#/components/schemas/CredentialBindingTemplateRef' + credential: + $ref: '#/components/schemas/CredentialSourceRef' sourceRef: $ref: '#/components/schemas/CredentialSourceRef' scope: @@ -261,6 +268,20 @@ components: $ref: '#/components/schemas/CredentialInjection' additionalProperties: false + CredentialBindingTemplateRef: + type: object + required: [name] + properties: + name: + type: string + description: Built-in or operator-configured template name. + params: + type: object + additionalProperties: + type: string + description: Non-sensitive template parameters. Sensitive values must use credential, not params. + additionalProperties: false + CredentialSourceRef: type: object required: [type] @@ -330,7 +351,17 @@ components: additionalProperties: false ``` -Example request: +Validation rules: + +- A `CredentialBinding` must use exactly one of these forms: + - **Inline full binding**: `sourceRef`, `scope`, and `injection`. + - **Template binding**: `templateRef` and `credential`. +- `templateRef.params` is for non-sensitive values only and may be logged in validation errors. +- `credential` has the same schema as `sourceRef` and is treated as sensitive according to its source type. +- Sandbox creators cannot define arbitrary templates in `CreateSandboxRequest`; they can only reference built-in or operator-configured templates. +- The server expands templates before egress validation, ambiguity checks, and runtime bootstrap. + +Example full binding request: ```json { @@ -369,6 +400,117 @@ Example request: } ``` +Example template binding request: + +```json +{ + "image": "python:3.12", + "networkPolicy": { + "defaultAction": "deny", + "egress": [ + { "action": "allow", "target": "code.alibaba-inc.com" } + ] + }, + "credentialVault": { + "mode": "transparentProxy", + "bindings": [ + { + "name": "code-alibaba-git", + "templateRef": { + "name": "git-https-basic", + "params": { + "target": "code.alibaba-inc.com", + "repoPath": "/foo/bar.git" + } + }, + "credential": { + "type": "inlineEphemeral", + "value": "domain-account:private-token" + } + } + ] + } +} +``` + +The template expands this into an internal binding equivalent to: + +```json +{ + "scope": { + "schemes": ["https"], + "ports": [443], + "targets": ["code.alibaba-inc.com"], + "methods": ["GET", "POST"], + "paths": ["/foo/bar.git", "/foo/bar.git/*"] + }, + "injection": { + "type": "header", + "name": "Authorization", + "value": "Basic {{ credential | base64 }}" + } +} +``` + +The sandbox workload can then use the normal unauthenticated repository URL: + +```bash +git clone https://code.alibaba-inc.com/foo/bar.git +``` + +### Binding Templates + +Binding templates reduce repeated boilerplate for common credential injection patterns. They are resolved by OpenSandbox server before sandbox creation reaches runtime providers. + +Template sources: + +1. **Built-in templates** + - Shipped with OpenSandbox. + - Cover common protocols such as `git-https-basic`, `generic-bearer`, and `generic-basic`. +2. **Operator-configured templates** + - Defined in server configuration under `[credential_vault]`. + - Intended for enterprise-specific targets and path constraints. + - Override or conflict with built-in names only if the operator uses an explicit namespace such as `operator/alibaba-code-git`. + +Example server configuration: + +```toml +[credential_vault] +enabled = true + +[[credential_vault.binding_templates]] +name = "operator/alibaba-code-git" +type = "git_https_basic" +target = "code.alibaba-inc.com" +allowed_repo_path_prefixes = ["/foo/", "/bar/"] +``` + +Example use: + +```json +{ + "name": "code-alibaba-git", + "templateRef": { + "name": "operator/alibaba-code-git", + "params": { + "repoPath": "/foo/bar.git" + } + }, + "credential": { + "type": "inlineEphemeral", + "value": "domain-account:private-token" + } +} +``` + +Template safety rules: + +- Templates must be built-in or configured by an operator; sandbox creators cannot submit arbitrary template definitions. +- Template params must be schema-validated by the selected template. +- Sensitive values must not be passed through `templateRef.params`; use `credential` so redaction and write-only handling apply. +- Expanded bindings must still pass HTTPS/port defaults, egress policy validation, source authorization, and ambiguity checks. +- Templates should keep targets and paths as narrow as possible. Operator-configured templates may fix `target` and allow only path parameters, which is safer for enterprise deployments. + ### Credential Sources The MVP supports three source types. @@ -535,6 +677,8 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Add config model for `[credential_vault]`. - Add source provider interface. - Validate `CreateSandboxRequest.credentialVault`. +- Load built-in and operator-configured binding templates. +- Expand template bindings into full bindings before egress validation and runtime bootstrap. - Require and validate `networkPolicy.egress` for credential-enabled sandboxes. - Persist credential binding metadata without plaintext credential values. - Resolve or prepare sandbox-scoped credential material during sandbox creation. @@ -570,14 +714,19 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. #### SDKs and CLI - Add typed request models for credential bindings. +- Add typed request models for template references and credential values. - Add examples for common providers such as GitHub and model APIs. +- Add examples for `git-https-basic` and enterprise operator-configured Git templates. - CLI may include validation helpers, but it should not print credential values. ## Test Plan ### Unit Tests -- Schema validation accepts valid bindings and rejects missing `name`, `sourceRef`, `scope`, or `injection`. +- Schema validation accepts valid full bindings and rejects full bindings missing `name`, `sourceRef`, `scope`, or `injection`. +- Schema validation accepts valid template bindings and rejects bindings that mix full-binding fields with template fields. +- Template params are validated by template type, and sensitive values in params are rejected. +- Template expansion produces the expected scope and injection policy for `git-https-basic`. - Scheme, port, FQDN, wildcard, method, and path matching work as expected. - Injection defaults to HTTPS/443 only and rejects HTTP injection unless explicitly configured and permitted. - Multiple matching bindings fail closed. @@ -587,6 +736,7 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - `inlineEphemeral.value` is accepted only as write-only create input and never appears in serialized sandbox metadata or API responses. - Egress validation requires `networkPolicy.egress` and catches binding targets not allowed by policy. - Kubernetes Secret source providers reject namespace/name references that are outside configured allowlists or requester authorization. +- Operator-configured templates reject repo paths outside allowed prefixes. ### Integration Tests @@ -605,6 +755,7 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. ### E2E Tests - Create a sandbox with `networkPolicy.defaultAction=deny`, allow `api.github.com`, bind a read-only GitHub credential, and verify a normal `https://api.github.com/...` call succeeds through Credential Proxy. +- Create a sandbox with a `git-https-basic` template, run `git clone https://code.alibaba-inc.com/foo/bar.git`, and verify the injected Basic auth succeeds without credentials in the URL. - Verify direct access to a non-allowed domain fails under egress policy. - Verify logs and diagnostic APIs never contain the credential string. - Verify a mock upstream that echoes request headers does not expose known credential values in supported response headers or text bodies. @@ -642,6 +793,10 @@ SDK mediation can be safer and more structured, but it requires language-specifi Explicit proxy and local gateway modes avoid transparent network interception, but they require application configuration and do not match the current OpenSandbox egress direction. The existing egress transparent mitmproxy path already provides the correct runtime interception point for Credential Proxy. +### User-defined Templates in CreateSandboxRequest + +Allowing sandbox creators to define arbitrary templates inline would reduce server configuration work, but templates decide target hosts, path scope, headers, schemes, ports, and credential rendering. Those are security policy surfaces. This proposal only allows built-in and operator-configured templates; sandbox creators may pass validated non-sensitive params and a credential source. + ## Infrastructure Needed - No new Credential Proxy component image for the MVP; Credential Proxy is implemented in the existing egress image through transparent mitmproxy and a first-party credential addon. @@ -650,6 +805,7 @@ Explicit proxy and local gateway modes avoid transparent network interception, b - Kubernetes permission to create/delete sandbox-scoped runtime Secrets when `inlineEphemeral` is enabled for Kubernetes runtime. - CI tests for Docker and Kubernetes runtime paths. - Documentation and examples for common credential binding patterns. +- Documentation for built-in binding templates and operator-configured templates. - Sandbox image or runtime support for trusting the OpenSandbox mitmproxy CA. No new required external service is introduced by the MVP. From ffc4ee54ce2d71e1ef0ed27f345674bbe0583ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B4=BE=E5=B2=9B?= Date: Fri, 29 May 2026 07:30:17 +0800 Subject: [PATCH 5/8] Document built-in credential templates --- oseps/0012-credential-vault.md | 65 ++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/oseps/0012-credential-vault.md b/oseps/0012-credential-vault.md index bacfea0d7..348676723 100644 --- a/oseps/0012-credential-vault.md +++ b/oseps/0012-credential-vault.md @@ -466,12 +466,72 @@ Template sources: 1. **Built-in templates** - Shipped with OpenSandbox. - - Cover common protocols such as `git-https-basic`, `generic-bearer`, and `generic-basic`. + - Cover common protocols listed in the built-in template catalog below. 2. **Operator-configured templates** - Defined in server configuration under `[credential_vault]`. - Intended for enterprise-specific targets and path constraints. - Override or conflict with built-in names only if the operator uses an explicit namespace such as `operator/alibaba-code-git`. +Built-in template catalog: + +| Template | Required params | Optional params | Credential input | Injection | +|----------|-----------------|-----------------|------------------|-----------| +| `git-https-basic` | `target`, `repoPath` | none | `username:token` | `Authorization: Basic {{ credential | base64 }}` | +| `generic-bearer` | `target` | `pathPrefix`, `methods` | bearer token | `Authorization: Bearer {{ credential }}` | +| `generic-basic` | `target` | `pathPrefix`, `methods` | `username:password` | `Authorization: Basic {{ credential | base64 }}` | +| `openai-bearer` | `target` | `pathPrefix` | API key | `Authorization: Bearer {{ credential }}` | +| `github-token` | `target` | `pathPrefix` | GitHub token | `Authorization: Bearer {{ credential }}` | +| `gitlab-token` | `target` | `pathPrefix`, `mode` | GitLab token | `PRIVATE-TOKEN: {{ credential }}` by default, or `Authorization: Bearer {{ credential }}` when `mode=bearer` | +| `npm-token` | `target` | `pathPrefix` | npm token | `Authorization: Bearer {{ credential }}` | +| `pypi-token` | `target` | `pathPrefix` | PyPI API token | `Authorization: Basic {{ '__token__:' + credential | base64 }}` | + +All built-in templates default to: + +- `schemes: ["https"]` +- `ports: [443]` +- egress validation against the expanded target +- no credential injection on HTTP unless an operator-configured template explicitly allows it + +Built-in template expansion examples: + +```yaml +git-https-basic: + requiredParams: [target, repoPath] + credentialInput: username:token + scope: + schemes: [https] + ports: [443] + targets: ["{{ target }}"] + methods: [GET, POST] + paths: ["{{ repoPath }}", "{{ repoPath }}/*"] + injection: + type: header + name: Authorization + value: "Basic {{ credential | base64 }}" + +generic-bearer: + requiredParams: [target] + optionalParams: [pathPrefix, methods] + credentialInput: token + scope: + schemes: [https] + ports: [443] + targets: ["{{ target }}"] + methods: "{{ methods | default([GET, POST, PUT, PATCH, DELETE]) }}" + paths: ["{{ pathPrefix | default('/*') }}"] + injection: + type: header + name: Authorization + value: "Bearer {{ credential }}" +``` + +Provider-specific notes: + +- `openai-bearer` is equivalent to `generic-bearer` with OpenAI-compatible API defaults. Operators should pin `target` for managed deployments. +- `github-token` uses `Authorization: Bearer` by default for modern GitHub API usage. +- `gitlab-token` defaults to GitLab's `PRIVATE-TOKEN` header for API usage; `mode=bearer` is available for deployments that use OAuth/JWT bearer tokens. +- `pypi-token` is intended for PyPI-compatible APIs that accept Basic auth with `__token__` as the username. Package manager behavior varies, so operators may prefer an operator-configured template for private indexes. + Example server configuration: ```toml @@ -726,7 +786,8 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Schema validation accepts valid full bindings and rejects full bindings missing `name`, `sourceRef`, `scope`, or `injection`. - Schema validation accepts valid template bindings and rejects bindings that mix full-binding fields with template fields. - Template params are validated by template type, and sensitive values in params are rejected. -- Template expansion produces the expected scope and injection policy for `git-https-basic`. +- Template expansion produces the expected scope and injection policy for every built-in template. +- Provider-specific built-ins such as `gitlab-token` and `pypi-token` validate supported modes and credential rendering. - Scheme, port, FQDN, wildcard, method, and path matching work as expected. - Injection defaults to HTTPS/443 only and rejects HTTP injection unless explicitly configured and permitted. - Multiple matching bindings fail closed. From f68309d7fe981b7317ba10a086a9ab6abd65f349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B4=BE=E5=B2=9B?= Date: Fri, 29 May 2026 09:35:27 +0800 Subject: [PATCH 6/8] Refine Credential Vault source model --- oseps/0012-credential-vault.md | 175 ++++++++++++++++----------------- 1 file changed, 83 insertions(+), 92 deletions(-) diff --git a/oseps/0012-credential-vault.md b/oseps/0012-credential-vault.md index 348676723..107a0650f 100644 --- a/oseps/0012-credential-vault.md +++ b/oseps/0012-credential-vault.md @@ -88,7 +88,7 @@ to: | R2 | Plaintext credentials are not exposed through OpenSandbox-managed sandbox env vars, files, lifecycle API responses, diagnostics, or logs | Must Have | | R3 | Credential Proxy injects credentials only for matching scheme, port, FQDN, HTTP method, and path scope | Must Have | | R4 | Initial injection supports HTTP request headers | Must Have | -| R5 | Kubernetes Secret, server-local configuration, and inline ephemeral values can be used as credential sources | Must Have | +| R5 | Inline ephemeral values can be used as the public credential input | Must Have | | R6 | Credential-enabled sandboxes require explicit `networkPolicy.egress` coverage for every binding target | Must Have | | R7 | Audit logs and metrics identify binding usage without logging credential values | Must Have | | R8 | Docker and Kubernetes runtimes use the same user-facing API semantics | Must Have | @@ -159,16 +159,18 @@ At a high level: | Sandbox bypasses Credential Proxy | Credential not injected, or traffic reaches destination without policy mediation | Use egress transparent redirect for TCP 80/443 and recommend `networkPolicy.defaultAction=deny` with `dns+nft` | | Credential leakage through logs | Secret exposure | Central redaction helpers; never log injected headers or rendered values; regression tests for logs | | Upstream echoes injected credential | Credential appears in sandbox-visible response content | Redact known credential values from response headers and text bodies where practical; document that services which echo sensitive request headers are unsupported unless response redaction is sufficient | -| Inline ephemeral value leaked by lifecycle logging | Secret exposure | Treat `inlineEphemeral.value` as write-only input; redact request bodies, validation errors, SDK debug logs, and persisted sandbox metadata | +| Inline credential value leaked by lifecycle logging | Secret exposure | Treat inline credential `value` as write-only input; redact request bodies, validation errors, SDK debug logs, and persisted sandbox metadata | | Credential source over-permissioned to sidecars | Cluster-wide secret access risk | Server resolves sources and passes only sandbox-scoped material; sidecar has no Kubernetes API permission by default | -| Arbitrary Kubernetes Secret binding | Sandbox creator can use server RBAC to access unrelated cluster Secrets | Require configured source providers, namespace scope, allowlists, and requester authorization before resolving any Kubernetes Secret | | Binding and egress policy drift | Credential may be configured for unreachable or unintended destinations | Require `networkPolicy.egress` and fail creation when any binding target is not covered | | Header injection into wrong host due to redirects | Credential sent to unintended destination | Re-evaluate policy after each redirected request; strip injected credentials on cross-host redirect unless target scope matches | | Credential injected over cleartext HTTP | Credential exposed on the network | Default binding scope to `schemes: [https]` and `ports: [443]`; require explicit opt-in for any HTTP injection | | HTTPS CA not trusted by sandbox image | Authenticated HTTPS requests fail | Install/export the OpenSandbox mitmproxy CA during sandbox startup or document image requirements | | Transparent redirect unavailable | Credential traffic bypasses proxy policy | Fail sandbox creation/readiness when credential bindings are present and mitmproxy, iptables redirect, CA bootstrap, or egress auth cannot be configured | -| User mitm addon observes injected headers | Credential exposure through extension hook | Disable user-provided mitm addons for credential-enabled sandboxes unless a future isolation model prevents addon access to credential-bearing flows | +| Untrusted mitm addon observes injected headers | Future credential exposure if sandbox users can control addon loading | Current egress mitm addons are operator/platform-controlled and are not a sandbox-user API. Preserve that boundary: reject any future sandbox-supplied mitm addons for credential-enabled sandboxes, and allow only operator-configured trusted addons. | | Egress policy API unauthenticated | Sandbox process rewrites sidecar policy | Always provision `OPENSANDBOX_EGRESS_TOKEN` for credential-enabled sidecars, even when network policy would otherwise be omitted | +| IPv6 bypasses transparent MITM | HTTP(S) traffic avoids inspection, injection, and audit | Short term: fail closed unless IPv6 is disabled or equivalently redirected; long term: support IPv6 through ip6tables/nftables redirect and test coverage | +| Upstream TLS verification disabled | Credential injected into a spoofed upstream peer | Reject `OPENSANDBOX_EGRESS_MITMPROXY_SSL_INSECURE=true` for credential-enabled sandboxes | +| Platform credential authorized for wrong destination | An operator-managed credential is injected into an attacker-controlled target | Do not expose platform credential sources as user-selectable source refs. Operator templates that use platform credentials must constrain allowed targets, methods, paths, schemes, ports, and injection renderings. | | Multiple bindings match one request | Wrong credential injection | Fail closed unless a single highest-priority binding is configured | | Long-lived credentials remain in proxy memory | Expanded exposure window | Cache with TTL, zero buffers where practical, prefer short-lived tokens from providers | | Users expect full secret management | Product confusion | Document Credential Vault as a broker layer, not a standalone secret manager | @@ -181,7 +183,7 @@ At a high level: - **Credential Proxy**: Credential-aware runtime behavior in the egress sidecar's transparent mitmproxy path. It evaluates outbound HTTP/HTTPS requests and injects credentials when policy matches. - **Credential Binding**: A per-sandbox declaration that connects a credential source to an allowed destination and injection rule. - **Credential Binding Template**: A built-in or operator-configured template that expands user parameters and a credential source into a full credential binding. -- **Credential Source**: A trusted source of credential material, such as Kubernetes Secret or server-local configuration. +- **Credential Source**: Credential material supplied to Credential Proxy. In the MVP public API this is an inline ephemeral value accepted only at sandbox creation time. Operator-managed credentials may exist behind operator-configured templates, but are not user-selectable source refs. - **Credential Injection**: The act of adding credential material to an outbound request, for example as an `Authorization` header. ### Architecture Overview @@ -191,13 +193,13 @@ Credential Vault should be modeled as a control-plane extension of sandbox lifec - Egress sidecar controls which network destinations are reachable. - Credential Proxy controls which credentials are attached to allowed outbound HTTP/HTTPS requests. -For Kubernetes, this means the existing egress sidecar in the sandbox Pod starts mitmproxy transparent mode and loads OpenSandbox's credential addon. For Docker, this means the existing egress sidecar shares the sandbox network namespace, redirects outbound `80/443` traffic to mitmproxy, and runs the same credential addon. +For Kubernetes, this means the existing egress sidecar in the sandbox Pod starts mitmproxy transparent mode and loads OpenSandbox's credential addon. For Docker, this means the existing egress sidecar shares the sandbox network namespace, redirects outbound configured HTTP/HTTPS ports to mitmproxy, and runs the same credential addon. The MVP built-in intercept ports are `80` and `443`. The egress sidecar already has the transparent MITM primitives required for Credential Proxy: - starts `mitmdump --mode transparent`, -- redirects outbound `TCP 80/443` traffic to the mitmproxy listener using `iptables`, -- loads system and user mitm addons, +- redirects outbound configured HTTP/HTTPS destination ports, initially `TCP 80/443`, to the mitmproxy listener using `iptables`, +- loads system and operator-configured mitm addons, - exports the mitmproxy root CA, - exposes health readiness so sandboxes do not start before interception is ready. @@ -284,21 +286,12 @@ components: CredentialSourceRef: type: object - required: [type] + required: [value] properties: - type: - type: string - enum: [kubernetesSecret, serverLocal, inlineEphemeral] - name: - type: string - description: Provider-local credential source name. Required for kubernetesSecret and serverLocal; omitted for inlineEphemeral. - key: - type: string - description: Provider-local key name for multi-key sources. value: type: string writeOnly: true - description: Inline ephemeral credential value accepted only at sandbox creation time. Never returned, logged, or persisted as plaintext. + description: Inline ephemeral credential value accepted only at sandbox creation time. This is the default and only public MVP credential input. Never returned, logged, or persisted as plaintext. additionalProperties: false CredentialScope: @@ -357,7 +350,7 @@ Validation rules: - **Inline full binding**: `sourceRef`, `scope`, and `injection`. - **Template binding**: `templateRef` and `credential`. - `templateRef.params` is for non-sensitive values only and may be logged in validation errors. -- `credential` has the same schema as `sourceRef` and is treated as sensitive according to its source type. +- `sourceRef` and `credential` both carry inline ephemeral credential material. Since inline ephemeral is the only public MVP credential input, no `type` discriminator is required. - Sandbox creators cannot define arbitrary templates in `CreateSandboxRequest`; they can only reference built-in or operator-configured templates. - The server expands templates before egress validation, ambiguity checks, and runtime bootstrap. @@ -378,9 +371,7 @@ Example full binding request: { "name": "github-readonly", "sourceRef": { - "type": "kubernetesSecret", - "name": "github-readonly-token", - "key": "token" + "value": "ghp_xxx" }, "scope": { "schemes": ["https"], @@ -424,8 +415,7 @@ Example template binding request: } }, "credential": { - "type": "inlineEphemeral", - "value": "domain-account:private-token" + "value": "ZG9tYWluLWFjY291bnQ6cHJpdmF0ZS10b2tlbg==" } } ] @@ -447,7 +437,7 @@ The template expands this into an internal binding equivalent to: "injection": { "type": "header", "name": "Authorization", - "value": "Basic {{ credential | base64 }}" + "value": "Basic {{ credential }}" } } ``` @@ -476,14 +466,14 @@ Built-in template catalog: | Template | Required params | Optional params | Credential input | Injection | |----------|-----------------|-----------------|------------------|-----------| -| `git-https-basic` | `target`, `repoPath` | none | `username:token` | `Authorization: Basic {{ credential | base64 }}` | -| `generic-bearer` | `target` | `pathPrefix`, `methods` | bearer token | `Authorization: Bearer {{ credential }}` | -| `generic-basic` | `target` | `pathPrefix`, `methods` | `username:password` | `Authorization: Basic {{ credential | base64 }}` | +| `git-https-basic` | `target`, `repoPath` | none | pre-encoded `base64(username:token)` | `Authorization: Basic {{ credential }}` | +| `generic-bearer` | `target` | `pathPrefix` | bearer token | `Authorization: Bearer {{ credential }}` | +| `generic-basic` | `target` | `pathPrefix` | pre-encoded `base64(username:password)` | `Authorization: Basic {{ credential }}` | | `openai-bearer` | `target` | `pathPrefix` | API key | `Authorization: Bearer {{ credential }}` | | `github-token` | `target` | `pathPrefix` | GitHub token | `Authorization: Bearer {{ credential }}` | | `gitlab-token` | `target` | `pathPrefix`, `mode` | GitLab token | `PRIVATE-TOKEN: {{ credential }}` by default, or `Authorization: Bearer {{ credential }}` when `mode=bearer` | | `npm-token` | `target` | `pathPrefix` | npm token | `Authorization: Bearer {{ credential }}` | -| `pypi-token` | `target` | `pathPrefix` | PyPI API token | `Authorization: Basic {{ '__token__:' + credential | base64 }}` | +| `pypi-token` | `target` | `pathPrefix` | pre-encoded `base64(__token__:token)` | `Authorization: Basic {{ credential }}` | All built-in templates default to: @@ -497,7 +487,7 @@ Built-in template expansion examples: ```yaml git-https-basic: requiredParams: [target, repoPath] - credentialInput: username:token + credentialInput: base64(username:token) scope: schemes: [https] ports: [443] @@ -507,17 +497,17 @@ git-https-basic: injection: type: header name: Authorization - value: "Basic {{ credential | base64 }}" + value: "Basic {{ credential }}" generic-bearer: requiredParams: [target] - optionalParams: [pathPrefix, methods] + optionalParams: [pathPrefix] credentialInput: token scope: schemes: [https] ports: [443] targets: ["{{ target }}"] - methods: "{{ methods | default([GET, POST, PUT, PATCH, DELETE]) }}" + methods: [GET, POST, PUT, PATCH, DELETE] paths: ["{{ pathPrefix | default('/*') }}"] injection: type: header @@ -530,7 +520,9 @@ Provider-specific notes: - `openai-bearer` is equivalent to `generic-bearer` with OpenAI-compatible API defaults. Operators should pin `target` for managed deployments. - `github-token` uses `Authorization: Bearer` by default for modern GitHub API usage. - `gitlab-token` defaults to GitLab's `PRIVATE-TOKEN` header for API usage; `mode=bearer` is available for deployments that use OAuth/JWT bearer tokens. -- `pypi-token` is intended for PyPI-compatible APIs that accept Basic auth with `__token__` as the username. Package manager behavior varies, so operators may prefer an operator-configured template for private indexes. +- `pypi-token` is intended for PyPI-compatible APIs that accept Basic auth with `__token__` as the username. For the MVP, callers or upper-layer platforms pass the already encoded `base64(__token__:token)` value. Package manager behavior varies, so operators may prefer an operator-configured template for private indexes. +- Built-in templates intentionally do not accept typed template params in the MVP. Method lists and other typed behavior are fixed by template type or by operator-configured templates. +- The MVP renderer supports only `{{ credential }}`. Built-in templates that need Basic auth require callers or upper-layer platforms to pre-encode the credential value before sandbox creation. Future versions may add server-side encoding helpers after the renderer model is explicitly designed. Example server configuration: @@ -557,8 +549,7 @@ Example use: } }, "credential": { - "type": "inlineEphemeral", - "value": "domain-account:private-token" + "value": "ZG9tYWluLWFjY291bnQ6cHJpdmF0ZS10b2tlbg==" } } ``` @@ -568,55 +559,36 @@ Template safety rules: - Templates must be built-in or configured by an operator; sandbox creators cannot submit arbitrary template definitions. - Template params must be schema-validated by the selected template. - Sensitive values must not be passed through `templateRef.params`; use `credential` so redaction and write-only handling apply. -- Expanded bindings must still pass HTTPS/port defaults, egress policy validation, source authorization, and ambiguity checks. +- Expanded bindings must still pass HTTPS/port defaults, egress policy validation, operator credential constraints, and ambiguity checks. - Templates should keep targets and paths as narrow as possible. Operator-configured templates may fix `target` and allow only path parameters, which is safer for enterprise deployments. +- Operator-configured templates may internally use platform-managed credentials, but those credentials are selected by the operator template configuration, not by sandbox user input. ### Credential Sources -The MVP supports three source types. +The MVP exposes one public credential input: an inline ephemeral value. -1. **Kubernetes Secret** - - Available only for Kubernetes runtime. - - The OpenSandbox server reads the referenced secret through a configured source provider, not arbitrary namespace/name input from the sandbox creator. - - The source provider must define allowed namespaces, allowed secret names or label selectors, and requester authorization rules. - - Sandbox creation must fail if the requester is not authorized to bind the referenced Kubernetes Secret. - - Credential Proxy does not receive Kubernetes API permissions by default. - - The resolved value is passed to the proxy through a sandbox-scoped secret volume or bootstrap channel. - -2. **Server-local source** - - Available for Docker and local development. - - Configured in server TOML, for example: - -```toml -[credential_vault] -enabled = true - -[[credential_vault.sources]] -type = "server_local" -name = "github-readonly-token" -value_env = "OPENSANDBOX_GITHUB_READONLY_TOKEN" -``` - -3. **Inline ephemeral source** - - Available for cases where an upper-layer platform creates a sandbox-scoped credential at sandbox creation time. - - The inline value is accepted only in `CreateSandboxRequest`. - - The OpenSandbox server must treat the value as write-only: do not return it in lifecycle responses, do not persist it as plaintext, and redact it from logs and validation errors. - - The server converts the value into sandbox-scoped runtime credential material for the egress sidecar / Credential Proxy. - - For Kubernetes, the runtime material may be represented as a generated Secret that is mounted only into the egress sidecar, not the application container. - - Generated runtime Secrets must be labeled with the sandbox identity and cleaned up when the sandbox is deleted. Use `ownerReferences` where possible and finalizers only when external revocation or cross-namespace cleanup is required. +- Available for cases where a caller or upper-layer platform creates a sandbox-scoped credential at sandbox creation time. +- The inline value is accepted only in `CreateSandboxRequest`. +- Because inline ephemeral is the only public MVP credential input, callers do not declare a `type`. +- The OpenSandbox server must treat the value as write-only: do not return it in lifecycle responses, do not persist it as plaintext, and redact it from logs and validation errors. +- The server converts the value into sandbox-scoped runtime credential material for the egress sidecar / Credential Proxy. +- For Kubernetes, the runtime material may be represented as a generated Secret that is mounted only into the egress sidecar, not the application container. +- Generated runtime Secrets must be labeled with the sandbox identity and cleaned up when the sandbox is deleted. Use `ownerReferences` where possible and finalizers only when external revocation or cross-namespace cleanup is required. +- Platform-level credentials are not exposed through user-selectable `sourceRef` names in the MVP. Platforms that need create-once, reference-many credentials should wrap them behind operator-configured binding templates and enforce template usage authorization at the platform layer. Example: ```json { "sourceRef": { - "type": "inlineEphemeral", "value": "ghp_xxx" } } ``` -Future providers may include HashiCorp Vault, Infisical, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, and internal credential brokers. +Kubernetes Secret is not a user-facing credential source in the MVP. In Kubernetes runtime, it may be used only as a sandbox-scoped runtime delivery mechanism generated by OpenSandbox, mounted only into the egress sidecar, and owned/cleaned up with the sandbox. Sandbox creators cannot reference arbitrary pre-existing Kubernetes Secret names. + +Future providers may include HashiCorp Vault, Infisical, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, internal credential brokers, and a Kubernetes Secret provider. Adding any user-selectable source provider requires a separate authorization model, namespace/scope policy, allowlists/selectors, requester authorization, and source-to-destination constraints. ### Credential Injection @@ -632,9 +604,11 @@ injection: Rules: - `{{ credential }}` is the only supported template variable in the MVP. +- The MVP renderer does not support filters, functions, string concatenation, arbitrary expressions, or user-defined transforms. - Credential Proxy must reject templates that do not include `{{ credential }}`. - Credential Proxy must inject only for HTTPS on port 443 by default. -- Any HTTP or non-443 injection requires explicit `scope.schemes` and `scope.ports` opt-in and should be rejected by default platform policy unless the operator enables it. +- Transparent mode supports credential injection only for destination ports that the egress sidecar redirects into mitmproxy. The MVP supports `443` by default and `80` when an operator-configured template or explicit operator policy enables cleartext HTTP injection. +- Any binding scope that uses ports outside `80/443` must be rejected unless the operator explicitly configures those ports as transparent MITM intercept ports and the runtime can install matching redirect rules. - Credential Proxy must reject attempts to inject hop-by-hop proxy headers unless explicitly allowed by implementation. - Credential Proxy must remove any existing request header with the same name before injecting a credential, unless a future merge strategy is added. - On redirect, Credential Proxy must re-evaluate target scope before preserving injected headers. @@ -643,6 +617,12 @@ Rules: Credential Proxy should redact known credential values from upstream response headers and text-like response bodies where practical. This protects against common debug endpoints and error handlers that echo request headers. +Redaction must include both source and rendered credential values, including: + +- raw `credential` source material, +- rendered injected header values such as `Bearer `, +- rendered Basic header values such as `Basic `. + This redaction is best effort, not an absolute secrecy guarantee: - Binary, compressed, encrypted, streaming, or very large response bodies may be passed without full body rewriting. @@ -663,17 +643,19 @@ Advantages: - No application proxy or base URL changes. - Reuses existing egress sidecar network namespace, `iptables` redirect, health gate, and mitmproxy integration. -- Works with existing HTTP clients, SDKs, CLIs, and agent-generated code as long as they use TCP `80/443` and trust the sandbox CA. +- Works with existing HTTP clients, SDKs, CLIs, and agent-generated code as long as they use intercepted HTTP/HTTPS ports and trust the sandbox CA. The MVP supported ports are `80/443`. - Keeps credential policy enforcement at the egress boundary, where network policy is already enforced. Limitations: - Requires Linux network namespace support and `CAP_NET_ADMIN` for the egress sidecar. - Requires the sandbox to trust the mitmproxy CA for HTTPS interception. -- Applies to HTTP/HTTPS traffic on `80/443`; non-HTTP protocols need future designs. -- In `ignore_hosts` pass-through mode, Credential Proxy cannot inspect or inject credentials for those hosts. +- Applies to HTTP/HTTPS traffic on configured transparent MITM intercept ports. The MVP supports `80/443`; additional ports require operator configuration and runtime redirect support. Non-HTTP protocols need future designs. +- Credential-bound targets must not match `ignore_hosts`. Because `ignore_hosts` is pass-through TLS, Credential Proxy cannot inspect, inject, redact, or audit those flows. +- Short term: credential-enabled startup must fail closed unless IPv6 HTTP(S) egress is disabled or covered by equivalent transparent redirect. Long term: IPv6 support should use ip6tables or nftables redirect with the same tests as IPv4. +- Credential-enabled startup must reject upstream TLS-insecure mode such as `OPENSANDBOX_EGRESS_MITMPROXY_SSL_INSECURE=true`. - Credential-enabled startup must fail closed if mitmproxy, `iptables` redirect, CA bootstrap, credential addon loading, or egress API authentication cannot be configured. -- User-provided mitm addons are disabled for credential-enabled sandboxes unless a future isolation model prevents them from observing credential-bearing flows. +- MITM addon trust boundary: current egress mitm addons are configured by the operator/platform through egress sidecar configuration, not by sandbox users. This is not a credential leak by itself because operator-configured addons are part of the platform trust boundary. Credential-enabled sandboxes must preserve this boundary by rejecting any future sandbox-supplied addon mechanism and by ensuring addon script paths do not come from app-container-writable locations. Operator-configured trusted addons may be loaded and must follow the same credential redaction and audit rules. ### Policy and Egress Integration @@ -682,6 +664,7 @@ Credential-enabled sandboxes must include `networkPolicy.egress`. The server mus Required validation: - Every credential binding target must be covered by an allow rule in `networkPolicy.egress`. +- More formally, the set of credential binding targets must be a subset of the egress allow targets after template expansion and wildcard normalization. - `networkPolicy.defaultAction` should be `deny`; if `allow` is accepted for compatibility, the server must still require explicit allow coverage for every binding target and warn that broad outbound access coexists with credential-bearing traffic. - If `networkPolicy` is omitted, if `egress` is empty, or if a binding target is not reachable under egress policy, sandbox creation must fail with HTTP 400. @@ -691,10 +674,13 @@ Suggested configuration: [credential_vault] enabled = true egress_validation = "strict" +transparent_intercept_ports = [80, 443] ``` For credential-enabled sandboxes, strict validation is required. Non-strict egress validation would allow credential-bearing traffic to run without an explicit network policy boundary. +`transparent_intercept_ports` defines the destination ports that the egress sidecar must redirect into transparent mitmproxy for credential injection. The default MVP value is `[80, 443]`. Operators may add ports only when the runtime supports redirecting those ports and the deployment has test coverage for them. Sandbox creation must fail if any credential binding `scope.ports` value is not included in `transparent_intercept_ports`. + Credential-enabled sidecars must always provision `OPENSANDBOX_EGRESS_TOKEN` for the egress policy API, including cases where the egress sidecar starts solely because credentials are enabled. The application container must not receive this token. ### Observability @@ -735,31 +721,33 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. #### Server - Add config model for `[credential_vault]`. -- Add source provider interface. +- Add write-only inline credential material handling for sandbox creation. - Validate `CreateSandboxRequest.credentialVault`. - Load built-in and operator-configured binding templates. - Expand template bindings into full bindings before egress validation and runtime bootstrap. - Require and validate `networkPolicy.egress` for credential-enabled sandboxes. - Persist credential binding metadata without plaintext credential values. - Resolve or prepare sandbox-scoped credential material during sandbox creation. -- Redact `inlineEphemeral.value` from request logging, validation errors, persisted metadata, and lifecycle responses. +- Redact inline credential values from request logging, validation errors, persisted metadata, and lifecycle responses. - Enable egress transparent mitmproxy, egress API auth, and credential addon bootstrap for Docker and Kubernetes runtimes when bindings are present. #### Components / Egress - Extend `components/egress` transparent mitmproxy support with a first-party credential addon. +- Keep transparent MITM redirect ports configurable. The default credential-enabled intercept set is `80/443`; additional ports require explicit operator configuration and matching redirect rules. - Load credential binding bootstrap config into the egress sidecar. - Implement binding evaluation, header injection, redaction, and audit events in the mitm addon path. - Keep the existing system addon behavior for streaming. -- Disable user-provided mitm addon loading for credential-enabled sandboxes until addon isolation is designed. -- Fail readiness/startup when transparent redirect, credential addon loading, CA bootstrap, or egress API auth is unavailable for a credential-enabled sandbox. +- Reject sandbox-supplied mitm addon loading for credential-enabled sandboxes. Operator-configured trusted addons may run as part of the platform trust boundary. +- Fail readiness/startup when transparent redirect, credential addon loading, CA bootstrap, upstream TLS verification, IPv6 coverage/disablement, or egress API auth is unavailable for a credential-enabled sandbox. #### Kubernetes - Enable the egress sidecar with transparent mitmproxy when credential bindings are present. - Add secret projection or bootstrap delivery for sandbox-scoped credential material. -- For `inlineEphemeral`, optionally create a generated sandbox-scoped Kubernetes Secret mounted only into the egress sidecar. +- For inline credentials, optionally create a generated sandbox-scoped Kubernetes Secret mounted only into the egress sidecar. - Generated runtime Secrets must use labels and `ownerReferences` when possible; finalizers are reserved for cleanup that Kubernetes garbage collection cannot cover. +- Kubernetes Secrets are runtime delivery artifacts only in the MVP; they are not user-facing credential sources. - Ensure Credential Proxy has no broad Kubernetes API permissions by default. - Ensure the mitmproxy CA is trusted by the sandbox application container when HTTPS interception is enabled. - Ensure generated egress API auth material is available only to the control plane and egress sidecar, not the application container. @@ -790,26 +778,29 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Provider-specific built-ins such as `gitlab-token` and `pypi-token` validate supported modes and credential rendering. - Scheme, port, FQDN, wildcard, method, and path matching work as expected. - Injection defaults to HTTPS/443 only and rejects HTTP injection unless explicitly configured and permitted. +- Binding scopes that use ports outside configured transparent MITM intercept ports are rejected. - Multiple matching bindings fail closed. - Existing headers with the injection name are replaced or rejected according to the selected implementation rule. - Redaction removes credential values from logs and errors. - Response redaction removes known credential values from response headers and supported text bodies. -- `inlineEphemeral.value` is accepted only as write-only create input and never appears in serialized sandbox metadata or API responses. +- Inline credential `value` is accepted only as write-only create input and never appears in serialized sandbox metadata or API responses. - Egress validation requires `networkPolicy.egress` and catches binding targets not allowed by policy. -- Kubernetes Secret source providers reject namespace/name references that are outside configured allowlists or requester authorization. - Operator-configured templates reject repo paths outside allowed prefixes. ### Integration Tests -- Docker sandbox with server-local source can call a mock HTTP/HTTPS server that requires an injected header without setting proxy or base URL configuration. - Docker sandbox with inline ephemeral source can call a mock HTTP/HTTPS server and cannot recover the inline credential from environment, filesystem, diagnostics, or lifecycle responses. - Docker sandbox cannot read credential value from environment variables, mounted files, lifecycle API response, or diagnostics. -- Kubernetes sandbox with Kubernetes Secret source can call a mock HTTP/HTTPS server that requires an injected header without setting proxy or base URL configuration. - Kubernetes sandbox with inline ephemeral source creates sandbox-scoped runtime material mounted only into the egress sidecar and cleans it up on sandbox deletion. +- Kubernetes runtime rejects requests that try to reference arbitrary pre-existing Kubernetes Secret names as credential sources. - Credential Proxy denies non-matching hosts, paths, and methods. - Credential-enabled sandbox creation/readiness fails when transparent redirect, mitm readiness, CA bootstrap, credential addon loading, or egress API auth cannot be configured. +- Credential-enabled sandbox creation rejects a binding that targets a non-configured port, and accepts the same port only after the operator configures transparent MITM interception for that port. - Credential-enabled sidecars require egress API auth even when credentials are the only reason the egress sidecar starts. -- User-provided mitm addons are not loaded for credential-enabled sandboxes. +- Sandbox-supplied mitm addons are not loaded for credential-enabled sandboxes; operator-configured trusted addons may run. +- Credential-enabled startup fails when IPv6 is enabled without equivalent transparent redirect coverage. +- Credential-enabled startup rejects upstream TLS-insecure mode. +- Credential-enabled startup rejects bindings that match `ignore_hosts`. - Cross-host redirect strips or re-evaluates injected credentials. - Sandbox deletion cleans up Credential Proxy and any sandbox-scoped credential material. @@ -820,13 +811,14 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Verify direct access to a non-allowed domain fails under egress policy. - Verify logs and diagnostic APIs never contain the credential string. - Verify a mock upstream that echoes request headers does not expose known credential values in supported response headers or text bodies. +- Verify response redaction covers both raw and rendered injected credential values. ## Drawbacks - Requires enabling transparent MITM for credential-bearing HTTP/HTTPS traffic. - Adds a new control-plane surface and a credential-aware path inside egress. - Requires stricter startup behavior than ordinary egress policy; credential-enabled sandboxes fail closed instead of gracefully degrading when transparent interception is unavailable. -- Disables user-provided mitm addons for credential-enabled sandboxes until addon isolation is available. +- Keeps MITM addons inside the operator/platform trust boundary; sandbox-supplied addons remain unsupported for credential-enabled sandboxes. - Cannot provide an absolute secrecy guarantee against arbitrary upstream services that echo credentials in unsupported response encodings or protocols. - Users may confuse Credential Vault with a full secret management system. - Debugging outbound requests becomes more complex because credentials are injected outside the application process. @@ -861,9 +853,8 @@ Allowing sandbox creators to define arbitrary templates inline would reduce serv ## Infrastructure Needed - No new Credential Proxy component image for the MVP; Credential Proxy is implemented in the existing egress image through transparent mitmproxy and a first-party credential addon. -- Server configuration for credential source providers. -- Kubernetes RBAC for server-side secret reads where Kubernetes Secret sources are enabled. -- Kubernetes permission to create/delete sandbox-scoped runtime Secrets when `inlineEphemeral` is enabled for Kubernetes runtime. +- Server configuration for binding templates and optional operator-managed credential backing stores used by those templates. +- Kubernetes permission to create/delete sandbox-scoped runtime Secrets when inline credentials are enabled for Kubernetes runtime. - CI tests for Docker and Kubernetes runtime paths. - Documentation and examples for common credential binding patterns. - Documentation for built-in binding templates and operator-configured templates. @@ -878,9 +869,9 @@ Credential Vault is opt-in. Existing sandboxes, SDK calls, egress policies, and Recommended rollout: 1. Add schema and server validation behind `[credential_vault].enabled = false` by default. -2. Extend egress transparent mitmproxy with credential addon support and server-local source for local development. -3. Implement Kubernetes Secret source and egress sidecar credential bootstrap. +2. Extend egress transparent mitmproxy with credential addon support and inline credential bootstrap. +3. Implement Kubernetes egress sidecar credential bootstrap using sandbox-scoped runtime material. 4. Add SDK models and CLI examples. 5. Document production guidance: use `networkPolicy.defaultAction=deny`, keep credential targets narrow, avoid broad methods and paths, and monitor audit events. -No migration is required for existing users. Users currently injecting secrets through environment variables can gradually migrate by moving those values into configured credential sources and letting Credential Proxy inject them into normal outbound HTTP/HTTPS calls. +No migration is required for existing users. Users currently injecting secrets through environment variables can gradually migrate by passing sandbox-scoped inline credentials at creation time, or by using platform-provided binding templates that hide operator-managed credentials behind platform authorization. From 28d1488ee60fce32328ef1f1f9cdd62e4244df58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B4=BE=E5=B2=9B?= Date: Fri, 29 May 2026 09:36:34 +0800 Subject: [PATCH 7/8] Clarify Credential Vault startup gate --- oseps/0012-credential-vault.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/oseps/0012-credential-vault.md b/oseps/0012-credential-vault.md index 107a0650f..a5414b86b 100644 --- a/oseps/0012-credential-vault.md +++ b/oseps/0012-credential-vault.md @@ -655,6 +655,7 @@ Limitations: - Short term: credential-enabled startup must fail closed unless IPv6 HTTP(S) egress is disabled or covered by equivalent transparent redirect. Long term: IPv6 support should use ip6tables or nftables redirect with the same tests as IPv4. - Credential-enabled startup must reject upstream TLS-insecure mode such as `OPENSANDBOX_EGRESS_MITMPROXY_SSL_INSECURE=true`. - Credential-enabled startup must fail closed if mitmproxy, `iptables` redirect, CA bootstrap, credential addon loading, or egress API authentication cannot be configured. +- Credential-enabled runtimes must gate the application process until the egress sidecar is actually ready. Readiness alone is not sufficient if sandbox code can start before redirect rules, mitmproxy, CA bootstrap, and credential addon loading are complete. - MITM addon trust boundary: current egress mitm addons are configured by the operator/platform through egress sidecar configuration, not by sandbox users. This is not a credential leak by itself because operator-configured addons are part of the platform trust boundary. Credential-enabled sandboxes must preserve this boundary by rejecting any future sandbox-supplied addon mechanism and by ensuring addon script paths do not come from app-container-writable locations. Operator-configured trusted addons may be loaded and must follow the same credential redaction and audit rules. ### Policy and Egress Integration @@ -740,6 +741,7 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Keep the existing system addon behavior for streaming. - Reject sandbox-supplied mitm addon loading for credential-enabled sandboxes. Operator-configured trusted addons may run as part of the platform trust boundary. - Fail readiness/startup when transparent redirect, credential addon loading, CA bootstrap, upstream TLS verification, IPv6 coverage/disablement, or egress API auth is unavailable for a credential-enabled sandbox. +- Expose a readiness signal that means redirect rules, mitmproxy, credential addon loading, CA material, upstream TLS verification, IPv6 handling, and egress API auth are all ready before application startup is released. #### Kubernetes @@ -751,6 +753,7 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Ensure Credential Proxy has no broad Kubernetes API permissions by default. - Ensure the mitmproxy CA is trusted by the sandbox application container when HTTPS interception is enabled. - Ensure generated egress API auth material is available only to the control plane and egress sidecar, not the application container. +- Add an init/startup gate so application containers do not run user code until the egress sidecar readiness signal is ready for credential-enabled sandboxes. #### Docker @@ -758,6 +761,7 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Ensure the mitmproxy CA is trusted by the sandbox application container when HTTPS interception is enabled. - Clean up sidecar and sandbox-scoped credential material when the sandbox is deleted. - Ensure generated egress API auth material is not exposed to the application container. +- Start or release the sandbox application container only after the egress sidecar readiness signal is ready for credential-enabled sandboxes. #### SDKs and CLI @@ -795,6 +799,7 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Kubernetes runtime rejects requests that try to reference arbitrary pre-existing Kubernetes Secret names as credential sources. - Credential Proxy denies non-matching hosts, paths, and methods. - Credential-enabled sandbox creation/readiness fails when transparent redirect, mitm readiness, CA bootstrap, credential addon loading, or egress API auth cannot be configured. +- Credential-enabled sandbox code cannot run before the egress sidecar readiness signal is ready. - Credential-enabled sandbox creation rejects a binding that targets a non-configured port, and accepts the same port only after the operator configures transparent MITM interception for that port. - Credential-enabled sidecars require egress API auth even when credentials are the only reason the egress sidecar starts. - Sandbox-supplied mitm addons are not loaded for credential-enabled sandboxes; operator-configured trusted addons may run. From b20320fa448eeda183350ae17d7cf53910d5ad95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B4=BE=E5=B2=9B?= Date: Tue, 2 Jun 2026 17:25:16 +0800 Subject: [PATCH 8/8] Add runtime credential mutation to OSEP-0012 --- oseps/0012-credential-vault.md | 176 +++++++++++++++++++++++++++++---- 1 file changed, 158 insertions(+), 18 deletions(-) diff --git a/oseps/0012-credential-vault.md b/oseps/0012-credential-vault.md index a5414b86b..10b36e0a1 100644 --- a/oseps/0012-credential-vault.md +++ b/oseps/0012-credential-vault.md @@ -3,7 +3,7 @@ title: Credential Vault and Credential Proxy authors: - "@jwx0925" creation-date: 2026-05-28 -last-updated: 2026-05-28 +last-updated: 2026-06-02 status: provisional --- @@ -25,6 +25,7 @@ status: provisional - [API Schema](#api-schema) - [Binding Templates](#binding-templates) - [Credential Sources](#credential-sources) + - [Runtime Credential Mutations](#runtime-credential-mutations) - [Credential Injection](#credential-injection) - [Response Redaction and Echo Handling](#response-redaction-and-echo-handling) - [Runtime Modes](#runtime-modes) @@ -62,13 +63,14 @@ to: ### Goals 1. **Brokered credentials**: Let sandboxed workloads use credentials without receiving plaintext secret values through OpenSandbox-managed environment variables, files, lifecycle API responses, diagnostics, or logs. -2. **Declarative binding**: Add a sandbox creation-time credential binding model that describes source, scope, and injection behavior, with operator-approved templates for common patterns. +2. **Declarative binding**: Add a credential binding model that describes source, scope, and injection behavior at sandbox creation time or through authorized runtime mutations, with operator-approved templates for common patterns. 3. **Policy-aware runtime injection**: Inject credentials only when sandbox identity, destination FQDN, HTTP method, and path all match the binding. 4. **Egress alignment**: Integrate with `networkPolicy.egress` so credential scope and network reachability are consistent. 5. **Runtime agnostic**: Support both Docker and Kubernetes through the existing egress sidecar pattern that shares the sandbox network namespace. 6. **Transparent by default**: Use the existing egress transparent mitmproxy path as Credential Proxy so applications do not need proxy or base URL changes. 7. **Auditable and redacted**: Emit useful audit events and metrics while redacting credential material from logs, diagnostics, and responses. 8. **Backward compatible**: Keep existing sandbox creation and egress behavior unchanged unless credential bindings are explicitly requested. +9. **Runtime mutation**: Let authorized control-plane callers add, replace, and delete sandbox credential bindings while the sandbox is running without exposing plaintext credentials to sandbox processes. ### Non-Goals @@ -95,8 +97,10 @@ to: | R9 | Credential Proxy is default-deny for missing, invalid, or non-matching bindings | Must Have | | R10 | The runtime uses egress transparent mitmproxy as the Credential Proxy implementation | Must Have | | R11 | Credential-enabled egress startup fails closed when transparent redirect, mitm readiness, CA bootstrap, or egress API auth cannot be configured | Must Have | -| R12 | Users can reference built-in or operator-configured binding templates instead of repeating full scope and injection rules | Should Have | -| R13 | Future secret managers can be added through a provider interface | Should Have | +| R12 | Authorized control-plane callers can add, replace, and delete credential bindings for a running sandbox | Must Have | +| R13 | Runtime credential mutations are applied atomically by Credential Proxy with revision acknowledgement or fail without partial metadata/runtime drift | Must Have | +| R14 | Users can reference built-in or operator-configured binding templates instead of repeating full scope and injection rules | Should Have | +| R15 | Future secret managers can be added through a provider interface | Should Have | ## Proposal @@ -112,6 +116,8 @@ The first implementation supports **transparent proxy mode**: 6. If exactly one binding matches and policy allows the request, Credential Proxy fetches or receives the credential material from a trusted source path and injects it into the request. 7. The external service receives the credential-bearing request; the sandbox process only sees the service response. +The MVP also supports runtime credential mutation through the lifecycle control plane. After a sandbox is running, an authorized caller may add, replace, or delete a sandbox-local credential binding. The server validates the mutation against the same template, scope, egress, intercept-port, ambiguity, and redaction rules used at sandbox creation time, prepares sandbox-scoped runtime material, and asks Credential Proxy to atomically load a new binding revision. The mutation succeeds only after Credential Proxy acknowledges the revision; otherwise the previous binding revision remains active. + At a high level: ``` @@ -151,6 +157,7 @@ At a high level: 5. **Binding scope must be covered by egress scope**: A credential-enabled sandbox must include `networkPolicy.egress`, and every credential binding target must be covered by an allow rule. Missing or inconsistent egress policy fails sandbox creation. 6. **Multiple matching bindings are ambiguous**: If more than one binding matches a request and no deterministic precedence is declared, Credential Proxy must fail closed. 7. **Upstream echo is outside the absolute secrecy guarantee**: OpenSandbox prevents its own control plane and runtime surfaces from exposing credentials, but an upstream service can still echo request headers in response bodies or headers. Credential Proxy should redact known credential values from responses where practical, and users should avoid binding credentials to services that echo sensitive request headers. +8. **Runtime mutations are control-plane only**: Sandbox application processes must not receive permission or credentials to add, update, delete, or inspect Credential Vault bindings. Runtime mutation APIs are intended for authenticated OpenSandbox clients, platform services, and operators outside the sandbox trust boundary. ### Risks and Mitigations @@ -173,6 +180,8 @@ At a high level: | Platform credential authorized for wrong destination | An operator-managed credential is injected into an attacker-controlled target | Do not expose platform credential sources as user-selectable source refs. Operator templates that use platform credentials must constrain allowed targets, methods, paths, schemes, ports, and injection renderings. | | Multiple bindings match one request | Wrong credential injection | Fail closed unless a single highest-priority binding is configured | | Long-lived credentials remain in proxy memory | Expanded exposure window | Cache with TTL, zero buffers where practical, prefer short-lived tokens from providers | +| Runtime mutation accepted but not loaded by proxy | Control plane and runtime disagree about which credentials are active | Treat mutations as revisioned transactions; do not report success until Credential Proxy acknowledges the new revision; keep the previous revision active on failure | +| Sandbox process calls mutation API | Sandbox can bind credentials to attacker-controlled targets | Require lifecycle API authentication and authorization outside the sandbox, never expose mutation credentials or egress API tokens to the application container, and audit the caller identity | | Users expect full secret management | Product confusion | Document Credential Vault as a broker layer, not a standalone secret manager | ## Design Details @@ -183,7 +192,7 @@ At a high level: - **Credential Proxy**: Credential-aware runtime behavior in the egress sidecar's transparent mitmproxy path. It evaluates outbound HTTP/HTTPS requests and injects credentials when policy matches. - **Credential Binding**: A per-sandbox declaration that connects a credential source to an allowed destination and injection rule. - **Credential Binding Template**: A built-in or operator-configured template that expands user parameters and a credential source into a full credential binding. -- **Credential Source**: Credential material supplied to Credential Proxy. In the MVP public API this is an inline ephemeral value accepted only at sandbox creation time. Operator-managed credentials may exist behind operator-configured templates, but are not user-selectable source refs. +- **Credential Source**: Credential material supplied to Credential Proxy. In the MVP public API this is an inline ephemeral value accepted only at sandbox creation time or through authorized runtime credential mutation APIs. Operator-managed credentials may exist behind operator-configured templates, but are not user-selectable source refs. - **Credential Injection**: The act of adding credential material to an outbound request, for example as an `Authorization` header. ### Architecture Overview @@ -231,6 +240,29 @@ Authorization: Bearer Extension to `specs/sandbox-lifecycle.yml`: ```yaml +paths: + /sandboxes/{sandbox_id}/credential-vault/bindings: + get: + summary: List credential binding metadata for a sandbox. + post: + summary: Add a credential binding to a running sandbox. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialBindingMutationRequest' + + /sandboxes/{sandbox_id}/credential-vault/bindings/{binding_name}: + put: + summary: Replace a credential binding on a running sandbox. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialBindingMutationRequest' + delete: + summary: Delete a credential binding from a running sandbox. + components: schemas: CreateSandboxRequest: @@ -251,6 +283,19 @@ components: $ref: '#/components/schemas/CredentialBinding' additionalProperties: false + CredentialVaultMutationResult: + type: object + required: [revision, bindings] + properties: + revision: + type: integer + description: Monotonic sandbox-local credential binding revision acknowledged by Credential Proxy. + bindings: + type: array + items: + $ref: '#/components/schemas/CredentialBindingMetadata' + additionalProperties: false + CredentialBinding: type: object required: [name] @@ -270,6 +315,27 @@ components: $ref: '#/components/schemas/CredentialInjection' additionalProperties: false + CredentialBindingMutationRequest: + allOf: + - $ref: '#/components/schemas/CredentialBinding' + description: Runtime add or replace request. Credential values are write-only and are never returned. + + CredentialBindingMetadata: + type: object + required: [name, revision] + properties: + name: + type: string + revision: + type: integer + templateRef: + $ref: '#/components/schemas/CredentialBindingTemplateRef' + scope: + $ref: '#/components/schemas/CredentialScope' + injection: + $ref: '#/components/schemas/CredentialInjection' + additionalProperties: false + CredentialBindingTemplateRef: type: object required: [name] @@ -291,7 +357,7 @@ components: value: type: string writeOnly: true - description: Inline ephemeral credential value accepted only at sandbox creation time. This is the default and only public MVP credential input. Never returned, logged, or persisted as plaintext. + description: Inline ephemeral credential value accepted at sandbox creation time or through authorized runtime credential mutation APIs. This is the default and only public MVP credential input. Never returned, logged, or persisted as plaintext. additionalProperties: false CredentialScope: @@ -352,7 +418,9 @@ Validation rules: - `templateRef.params` is for non-sensitive values only and may be logged in validation errors. - `sourceRef` and `credential` both carry inline ephemeral credential material. Since inline ephemeral is the only public MVP credential input, no `type` discriminator is required. - Sandbox creators cannot define arbitrary templates in `CreateSandboxRequest`; they can only reference built-in or operator-configured templates. -- The server expands templates before egress validation, ambiguity checks, and runtime bootstrap. +- The server expands templates before egress validation, ambiguity checks, runtime bootstrap, or runtime mutation delivery. +- Runtime mutation APIs require lifecycle API authentication and sandbox-level authorization. Sandbox application containers must not receive credentials that allow them to call these APIs. +- Runtime mutation APIs return only `CredentialBindingMetadata` and the acknowledged `revision`; they never return credential source values. Example full binding request: @@ -567,8 +635,8 @@ Template safety rules: The MVP exposes one public credential input: an inline ephemeral value. -- Available for cases where a caller or upper-layer platform creates a sandbox-scoped credential at sandbox creation time. -- The inline value is accepted only in `CreateSandboxRequest`. +- Available for cases where a caller or upper-layer platform creates a sandbox-scoped credential at sandbox creation time or adds one to an already-running sandbox through the runtime mutation API. +- The inline value is accepted only in `CreateSandboxRequest` and authorized runtime credential mutation requests. - Because inline ephemeral is the only public MVP credential input, callers do not declare a `type`. - The OpenSandbox server must treat the value as write-only: do not return it in lifecycle responses, do not persist it as plaintext, and redact it from logs and validation errors. - The server converts the value into sandbox-scoped runtime credential material for the egress sidecar / Credential Proxy. @@ -590,6 +658,50 @@ Kubernetes Secret is not a user-facing credential source in the MVP. In Kubernet Future providers may include HashiCorp Vault, Infisical, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, internal credential brokers, and a Kubernetes Secret provider. Adding any user-selectable source provider requires a separate authorization model, namespace/scope policy, allowlists/selectors, requester authorization, and source-to-destination constraints. +### Runtime Credential Mutations + +The MVP supports adding, replacing, deleting, and listing credential binding metadata for a running sandbox. These APIs are lifecycle control-plane APIs, not sandbox-local APIs. + +Supported operations: + +- `GET /sandboxes/{sandbox_id}/credential-vault/bindings`: returns sanitized binding metadata and the current acknowledged revision. +- `POST /sandboxes/{sandbox_id}/credential-vault/bindings`: adds a new sandbox-local binding name. +- `PUT /sandboxes/{sandbox_id}/credential-vault/bindings/{binding_name}`: replaces the full binding for an existing sandbox-local binding name. +- `DELETE /sandboxes/{sandbox_id}/credential-vault/bindings/{binding_name}`: removes a binding and stops future injection for matching requests. + +Mutation rules: + +- The target sandbox must exist and be in a state where its runtime can accept credential proxy updates. Stopped or deleted sandboxes reject runtime mutation requests. +- The caller must be authorized to mutate credentials for the target sandbox. Application containers must not receive this authorization. +- Add and replace requests use the same `CredentialBinding` shape as creation-time bindings. +- Add requests fail if the binding name already exists. Replace requests fail if the binding name does not exist, unless a future upsert mode is explicitly added. +- Delete requests are idempotent only if the API contract explicitly chooses that behavior; the MVP should return `404` for unknown binding names to surface caller mistakes. +- The server validates expanded targets against the sandbox's current `networkPolicy.egress`. Runtime mutation does not widen network reachability; callers must update egress policy through a separate, explicitly authorized API if such an API exists. +- The server runs ambiguity checks against the complete post-mutation binding set. If the new set can produce multiple matching bindings without deterministic precedence, the mutation fails. +- The server rejects scopes that require transparent MITM intercept ports not configured for the sandbox runtime. +- The server rejects mutations for targets that match `ignore_hosts`. + +Revision and acknowledgement: + +- Each accepted mutation candidate receives a monotonically increasing sandbox-local `credentialVault.revision`. +- The server prepares sandbox-scoped runtime material, sends the complete sanitized binding set and source material handles to Credential Proxy, and waits for an acknowledgement for that revision. +- Credential Proxy validates and loads the revision into an immutable in-memory snapshot, then atomically swaps the active snapshot. +- The API returns success only after Credential Proxy acknowledges the revision. The response includes the acknowledged revision and sanitized binding metadata. +- If Credential Proxy is unavailable, rejects the update, or times out, the server must not report success. The previous acknowledged revision remains active. +- If the server already persisted pending mutation metadata before delivery, it must mark the mutation as failed or roll it back before returning an error so lifecycle responses do not claim an inactive binding. + +Request handling during mutation: + +- Requests already being processed may complete using the snapshot that was active when evaluation started. +- New requests after the proxy acknowledges a revision must use the new snapshot. +- Deleting a binding stops future injection after acknowledgement. It does not attempt to revoke credentials already sent to an upstream service in prior requests. + +Runtime material cleanup: + +- On replace, old runtime credential material must be removed after the new revision is acknowledged. +- On delete, runtime credential material for that binding must be removed after the delete revision is acknowledged. +- On sandbox deletion, all runtime credential material and pending mutation state must be cleaned up. + ### Credential Injection The MVP supports request header injection only. Credential Proxy injects the header into intercepted HTTP/HTTPS requests after the transparent mitmproxy path has decoded request metadata: @@ -692,6 +804,9 @@ Suggested audit log fields: - `sandbox_id` - `credential_binding` +- `credential_vault_revision` +- `operation` (`create`, `runtime_add`, `runtime_replace`, `runtime_delete`, `inject`) +- `actor_id` for control-plane mutations when available - `source_type` - `target_host` - `method` @@ -707,6 +822,8 @@ Suggested metrics: - `opensandbox_credential_proxy_injections_total` - `opensandbox_credential_proxy_denials_total` - `opensandbox_credential_proxy_source_errors_total` +- `opensandbox_credential_vault_mutations_total` +- `opensandbox_credential_vault_mutation_failures_total` - `opensandbox_credential_proxy_request_duration_seconds` All diagnostics APIs that surface runtime logs must preserve redaction behavior. @@ -717,26 +834,33 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Add `credentialVault` schemas to `specs/sandbox-lifecycle.yml`. - Add examples for sandbox creation with credential bindings. -- Consider a future `credential-proxy-api.yaml` only if runtime policy inspection/mutation is exposed separately from the egress API. +- Add runtime credential binding mutation APIs for listing, adding, replacing, and deleting sandbox-local bindings. +- Add examples for runtime add, replace, and delete flows, including acknowledged revision responses and failed proxy acknowledgement errors. #### Server - Add config model for `[credential_vault]`. - Add write-only inline credential material handling for sandbox creation. - Validate `CreateSandboxRequest.credentialVault`. +- Add runtime mutation handlers for `credentialVault.bindings` list, add, replace, and delete. - Load built-in and operator-configured binding templates. -- Expand template bindings into full bindings before egress validation and runtime bootstrap. +- Expand template bindings into full bindings before egress validation, runtime bootstrap, and runtime mutation delivery. - Require and validate `networkPolicy.egress` for credential-enabled sandboxes. - Persist credential binding metadata without plaintext credential values. -- Resolve or prepare sandbox-scoped credential material during sandbox creation. +- Resolve or prepare sandbox-scoped credential material during sandbox creation and runtime mutation. +- Maintain sandbox-local credential vault revisions and return success only after Credential Proxy acknowledges the target revision. +- Roll back or mark failed any mutation metadata that cannot be delivered to Credential Proxy. - Redact inline credential values from request logging, validation errors, persisted metadata, and lifecycle responses. - Enable egress transparent mitmproxy, egress API auth, and credential addon bootstrap for Docker and Kubernetes runtimes when bindings are present. +- Audit runtime credential mutation attempts with caller identity, binding name, revision, operation, and decision, without credential values. #### Components / Egress - Extend `components/egress` transparent mitmproxy support with a first-party credential addon. - Keep transparent MITM redirect ports configurable. The default credential-enabled intercept set is `80/443`; additional ports require explicit operator configuration and matching redirect rules. - Load credential binding bootstrap config into the egress sidecar. +- Expose a control-plane-only credential binding reload path protected by egress API auth. +- Apply runtime binding revisions through immutable snapshots and atomic swap; keep the previous acknowledged revision active on reload failure. - Implement binding evaluation, header injection, redaction, and audit events in the mitm addon path. - Keep the existing system addon behavior for streaming. - Reject sandbox-supplied mitm addon loading for credential-enabled sandboxes. Operator-configured trusted addons may run as part of the platform trust boundary. @@ -750,6 +874,7 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - For inline credentials, optionally create a generated sandbox-scoped Kubernetes Secret mounted only into the egress sidecar. - Generated runtime Secrets must use labels and `ownerReferences` when possible; finalizers are reserved for cleanup that Kubernetes garbage collection cannot cover. - Kubernetes Secrets are runtime delivery artifacts only in the MVP; they are not user-facing credential sources. +- Runtime add and replace operations may create or rotate generated sandbox-scoped Secrets; delete operations must remove unused generated Secrets after the proxy acknowledges the delete revision. - Ensure Credential Proxy has no broad Kubernetes API permissions by default. - Ensure the mitmproxy CA is trusted by the sandbox application container when HTTPS interception is enabled. - Ensure generated egress API auth material is available only to the control plane and egress sidecar, not the application container. @@ -759,13 +884,14 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Enable the egress sidecar with transparent mitmproxy sharing the sandbox network namespace. - Ensure the mitmproxy CA is trusted by the sandbox application container when HTTPS interception is enabled. -- Clean up sidecar and sandbox-scoped credential material when the sandbox is deleted. +- Create, rotate, and clean up sandbox-scoped credential material for runtime add, replace, delete, and sandbox deletion. - Ensure generated egress API auth material is not exposed to the application container. - Start or release the sandbox application container only after the egress sidecar readiness signal is ready for credential-enabled sandboxes. #### SDKs and CLI - Add typed request models for credential bindings. +- Add client helpers for runtime credential binding list, add, replace, and delete. - Add typed request models for template references and credential values. - Add examples for common providers such as GitHub and model APIs. - Add examples for `git-https-basic` and enterprise operator-configured Git templates. @@ -787,8 +913,11 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Existing headers with the injection name are replaced or rejected according to the selected implementation rule. - Redaction removes credential values from logs and errors. - Response redaction removes known credential values from response headers and supported text bodies. -- Inline credential `value` is accepted only as write-only create input and never appears in serialized sandbox metadata or API responses. +- Inline credential `value` is accepted only as write-only create or runtime mutation input and never appears in serialized sandbox metadata or API responses. - Egress validation requires `networkPolicy.egress` and catches binding targets not allowed by policy. +- Runtime add rejects duplicate binding names, replace rejects unknown binding names, and delete returns the documented response for unknown binding names. +- Runtime mutations reject post-mutation binding sets that create ambiguous matches. +- Runtime mutation revision handling keeps the previous acknowledged revision active when proxy acknowledgement fails. - Operator-configured templates reject repo paths outside allowed prefixes. ### Integration Tests @@ -797,6 +926,11 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Docker sandbox cannot read credential value from environment variables, mounted files, lifecycle API response, or diagnostics. - Kubernetes sandbox with inline ephemeral source creates sandbox-scoped runtime material mounted only into the egress sidecar and cleans it up on sandbox deletion. - Kubernetes runtime rejects requests that try to reference arbitrary pre-existing Kubernetes Secret names as credential sources. +- Running Docker and Kubernetes sandboxes can add an inline ephemeral credential binding through the lifecycle API, receive an acknowledged revision, and use it for subsequent matching requests. +- Running Docker and Kubernetes sandboxes can replace a credential binding and observe that subsequent requests use the replacement while prior credential material is cleaned up. +- Running Docker and Kubernetes sandboxes can delete a credential binding and observe that subsequent matching requests no longer receive injected credentials. +- Runtime mutation requests fail without activating partial metadata when Credential Proxy acknowledgement fails or times out. +- Sandbox application containers cannot call runtime credential mutation APIs or access the egress API token used for proxy reload. - Credential Proxy denies non-matching hosts, paths, and methods. - Credential-enabled sandbox creation/readiness fails when transparent redirect, mitm readiness, CA bootstrap, credential addon loading, or egress API auth cannot be configured. - Credential-enabled sandbox code cannot run before the egress sidecar readiness signal is ready. @@ -812,6 +946,9 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. ### E2E Tests - Create a sandbox with `networkPolicy.defaultAction=deny`, allow `api.github.com`, bind a read-only GitHub credential, and verify a normal `https://api.github.com/...` call succeeds through Credential Proxy. +- Create a sandbox without initial credential bindings, add a read-only GitHub credential at runtime, and verify subsequent normal `https://api.github.com/...` calls succeed through Credential Proxy. +- Replace the runtime GitHub credential with an invalid value and verify subsequent matching requests fail authentication without exposing either credential in logs or lifecycle responses. +- Delete the runtime GitHub credential binding and verify subsequent matching requests are not credentialed. - Create a sandbox with a `git-https-basic` template, run `git clone https://code.alibaba-inc.com/foo/bar.git`, and verify the injected Basic auth succeeds without credentials in the URL. - Verify direct access to a non-allowed domain fails under egress policy. - Verify logs and diagnostic APIs never contain the credential string. @@ -822,6 +959,7 @@ All diagnostics APIs that surface runtime logs must preserve redaction behavior. - Requires enabling transparent MITM for credential-bearing HTTP/HTTPS traffic. - Adds a new control-plane surface and a credential-aware path inside egress. +- Runtime credential mutation adds revision, acknowledgement, rollback, and cleanup complexity to the lifecycle control plane and egress sidecar. - Requires stricter startup behavior than ordinary egress policy; credential-enabled sandboxes fail closed instead of gracefully degrading when transparent interception is unavailable. - Keeps MITM addons inside the operator/platform trust boundary; sandbox-supplied addons remain unsupported for credential-enabled sandboxes. - Cannot provide an absolute secrecy guarantee against arbitrary upstream services that echo credentials in unsupported response encodings or protocols. @@ -860,6 +998,7 @@ Allowing sandbox creators to define arbitrary templates inline would reduce serv - No new Credential Proxy component image for the MVP; Credential Proxy is implemented in the existing egress image through transparent mitmproxy and a first-party credential addon. - Server configuration for binding templates and optional operator-managed credential backing stores used by those templates. - Kubernetes permission to create/delete sandbox-scoped runtime Secrets when inline credentials are enabled for Kubernetes runtime. +- A control-plane-to-egress reload path for runtime binding revisions, protected by egress API authentication and unavailable to application containers. - CI tests for Docker and Kubernetes runtime paths. - Documentation and examples for common credential binding patterns. - Documentation for built-in binding templates and operator-configured templates. @@ -875,8 +1014,9 @@ Recommended rollout: 1. Add schema and server validation behind `[credential_vault].enabled = false` by default. 2. Extend egress transparent mitmproxy with credential addon support and inline credential bootstrap. -3. Implement Kubernetes egress sidecar credential bootstrap using sandbox-scoped runtime material. -4. Add SDK models and CLI examples. -5. Document production guidance: use `networkPolicy.defaultAction=deny`, keep credential targets narrow, avoid broad methods and paths, and monitor audit events. +3. Add runtime credential mutation APIs, revision tracking, proxy acknowledgement, and rollback semantics. +4. Implement Kubernetes egress sidecar credential bootstrap and runtime rotation using sandbox-scoped runtime material. +5. Add SDK models and CLI examples. +6. Document production guidance: use `networkPolicy.defaultAction=deny`, keep credential targets narrow, avoid broad methods and paths, and monitor audit events. -No migration is required for existing users. Users currently injecting secrets through environment variables can gradually migrate by passing sandbox-scoped inline credentials at creation time, or by using platform-provided binding templates that hide operator-managed credentials behind platform authorization. +No migration is required for existing users. Users currently injecting secrets through environment variables can gradually migrate by passing sandbox-scoped inline credentials at creation time, adding them at runtime through authorized control-plane APIs, or by using platform-provided binding templates that hide operator-managed credentials behind platform authorization.