Skip to content

Credential injection

How does a sandbox call an authenticated API without holding the credential?

A secret Connection placed as env or files puts its credential inside the sandbox. A model with a shell can read it there, print it into a transcript, and use it against anything the Connection’s hosts allow, such as a second repository on the same git host.

A Connection placed as inject keeps the credential in the claim’s egress proxy instead. The sandbox gets a placeholder value and a CA to trust. For the Connection’s injected hosts, and only those, the proxy terminates TLS with a certificate from that CA, replaces whatever Authorization the request carries, and sets the real one. The credential is never in the sandbox’s environment, filesystem or memory, so nothing the model runs can print it.

sequenceDiagram
participant S as Sandbox
participant P as Egress proxy
participant H as api.github.com
S->>P: CONNECT api.github.com:443
P-->>S: 200, then TLS with a leaf from the claim CA
S->>P: GET /repos/acme/widgets<br/>Authorization: token omnia-injected-github
P->>P: Host matches, a rule allows it,<br/>placeholder replaced
P->>H: GET /repos/acme/widgets<br/>Authorization: Bearer (from the Secret)
H-->>P: 200
P-->>S: 200

The model can still use the credential: anything it sends to an injected host goes out authenticated. Request rules bound that use. With rules, the proxy forwards only the methods and path prefixes you list, so a token that GitHub would honour on every repository the account can see works on one repository through this sandbox. The proxy matches the path as the upstream receives it, which is why it refuses a path with .. or an empty segment rather than tidying it.

Terminating TLS has costs, and the design keeps them to the injected hosts:

  • A CA the sandbox trusts. The CA is per claim, name-constrained to the injected hosts and to no IP address, and its key exists only in a Secret mounted into the proxy. A client that enforces name constraints, as Go, OpenSSL and browsers do, rejects a certificate it signs for any other name. So a stolen key could impersonate those hosts, and only to that one claim’s sandbox.
  • Trust has to reach every tool. Go, OpenSSL, curl and Node.js add a CA from an environment variable. Python and git replace their roots, so the operator gives them a bundle of its own image’s roots plus the CA. A root that only the sandbox image carries drops out of that bundle.
  • Some protocols cannot be inspected. The proxy speaks HTTP/1.1 on an injected host, so a client that will only speak HTTP/2, as gRPC does, fails rather than send traffic the proxy cannot inspect. Request signing schemes (AWS SigV4, for example) cannot be injected, because the proxy cannot re-sign a request it did not build; those credentials stay env or files.

A Connection cannot mix inject with env or files. Mixing would put the Secret the proxy keeps back into the sandbox. A service that needs both forms is two Connections.