Keep a credential out of the sandbox
Give a sandbox an authenticated API without placing the token in it: the claim’s egress proxy adds the token to requests for the Connection’s hosts, and the sandbox holds only a placeholder.
Prerequisites
Section titled “Prerequisites”- A SandboxClass that declares a Connection role, such as
vcs, inspec.connections. See Binding a Connection to a sandbox. - A ToolRegistry sandbox or an AgentRuntime that binds that role.
- The API authenticates with a header, such as
Authorization: Bearer, or with HTTP basic auth. A request-signing scheme such as AWS SigV4 cannot be injected; place that credential withenvorfilesinstead. - The tools in the sandbox speak HTTP/1.1 to the API. A client that accepts only HTTP/2, such as a gRPC client, fails against an injected host.
-
Create the Secret in the namespace the sandboxes run in:
Terminal window kubectl create secret generic acme-github-token -n agents \--from-literal=user=x-access-token \--from-literal=token=<your token> -
Create the Connection. Each
injectentry names the hosts it covers and the header it sets, written as a template over the Secret’s keys:apiVersion: omnia.altairalabs.ai/v1alpha1kind: Connectionmetadata:name: github-acmenamespace: agentsspec:hosts: [api.github.com, github.com]credential:kind: secretsecretRef:name: acme-github-tokenplacement:inject:- hosts: [api.github.com]header: Authorizationvalue: "Bearer {{ .token }}"placeholderEnv: [GH_TOKEN]- hosts: [github.com]basic:usernameKey: userpasswordKey: tokenplaceholderEnvsetsGH_TOKENin the sandbox toomnia-injected-github-acme, soghstarts. The proxy replaces whateverAuthorizationthe sandbox sends. Thegithub.comentry coversgit cloneover HTTPS. -
Optional: limit what the token can be used for. Add
rulesto an entry, and the proxy forwards only the methods and path prefixes you list:- hosts: [api.github.com]header: Authorizationvalue: "Bearer {{ .token }}"placeholderEnv: [GH_TOKEN]rules:- methods: [GET, HEAD]pathPrefixes: [/repos/acme/widgets]- methods: [POST]pathPrefixes: [/repos/acme/widgets/pulls]A prefix matches whole path segments, so
/repos/acme/widgetsdoes not match/repos/acme/widgets-old. -
Bind the Connection to the class’s role, on the ToolRegistry sandbox or on the agent:
# AgentRuntimespec:sandbox:enabled: trueconnections:- {sandbox: workspace, role: vcs, connection: github-acme}
Verify
Section titled “Verify”-
Start a session that uses the sandbox, then find its claim:
Terminal window kubectl get sandboxclaims -n agents -
Check the claim’s injection objects exist:
Terminal window kubectl get secret <claim>-egress-ca -n agentskubectl get configmap <claim>-egress-trust -n agents -
Check the sandbox holds the placeholder, not the token:
Terminal window kubectl exec -n agents deploy/<claim>-sandbox -- printenv GH_TOKENThe output is
omnia-injected-github-acme. -
From inside the sandbox, call an allowed path, then one no rule allows:
Terminal window kubectl exec -n agents deploy/<claim>-sandbox -- \curl -s -o /dev/null -w '%{http_code}\n' https://api.github.com/repos/acme/widgetskubectl exec -n agents deploy/<claim>-sandbox -- \curl -s -D - -o /dev/null https://api.github.com/user | grep -i x-omnia-egress-refusedThe first prints
200. The second printsX-Omnia-Egress-Refused: l7-not-allowed.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
The claim fails with ConnectionSecretKeyMissing or ConnectionTemplateInvalid | A key the value or basic names is not in the Secret | Add the key, or correct the template |
The claim fails with ConnectionInjectionInvalid | The value renders to more than one line: a key it reads holds a line break other than a trailing one | Store the value on one line |
X-Omnia-Egress-Refused: credential-unavailable (502) | The proxy cannot read a key the entry needs, as happens between a key’s removal from the Secret and the operator dropping the binding | Restore the key. Once the binding is dropped, the host is refused with host-not-allowed |
X-Omnia-Egress-Refused: host-mismatch (403) | The request’s Host header names a different host from the one the tool connected to | Send each host’s requests on its own connection |
| A Python or git command fails certificate verification for an injected host | REQUESTS_CA_BUNDLE or GIT_SSL_CAINFO is not set: the operator found no root certificates to build bundle.pem from | Check the operator image has a root bundle, such as /etc/ssl/certs/ca-certificates.crt |
| A Python or git command fails certificate verification for another host | That host’s root CA is baked into the sandbox image only, and bundle.pem holds the operator image’s roots | Reach that host from a claim that binds no injecting Connection |
| A long-running process fails TLS to an injected host after several days | The claim’s CA was renewed and the process loaded its roots before that | Restart the process |
Related pages
Section titled “Related pages”- Connection: injected credentials:
every
injectfield and what the proxy does on an injected host - Credential injection: how it works and what it costs