Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions docs/api-proxy-sidecar.md
Original file line number Diff line number Diff line change
Expand Up @@ -706,8 +706,10 @@ AWF supports OIDC-based credential exchange with multiple cloud providers via Gi
| `ACTIONS_ID_TOKEN_REQUEST_URL` | ✅ | Provided automatically by the GitHub Actions runtime |
| `ACTIONS_ID_TOKEN_REQUEST_TOKEN` | ✅ | Provided automatically by the GitHub Actions runtime |

:::caution[OIDC request capability reaches the agent]
AWF forwards `ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN` to both the sidecar and the agent container. These values let code request a GitHub OIDC JWT when the job has `permissions: id-token: write`. Exchanged provider credentials remain isolated in the sidecar, but the token-minting capability itself is not isolated. Restrict provider federation policies to the expected repository, workflow, ref, and audience.
:::note[OIDC request capability is sidecar-only]
AWF forwards `ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN` only to the api-proxy sidecar when `AWF_AUTH_TYPE=github-oidc`. The variables are excluded from the agent even when `--env-all`, `--env-file`, or explicit `--env` options request them. The minted GitHub JWT and exchanged provider credentials also remain inside the sidecar.

GitHub Agentic Workflows handles HTTP MCP `auth.type: github-oidc` separately: the compiler-generated, runner-owned **Start MCP Gateway** step passes the Actions variables directly to the MCP gateway, which mints an audience-bound JWT for the remote server. AWF neither launches nor configures that gateway, and the variables do not need to pass through the agent. Recompile older workflow lock files that do not use this direct runner-to-gateway path; compatibility tracking is available in [github/gh-aw#50053](https://github.com/github/gh-aw/issues/50053).
:::

When `AWF_AUTH_TYPE=github-oidc` is set but `ACTIONS_ID_TOKEN_REQUEST_URL`/`ACTIONS_ID_TOKEN_REQUEST_TOKEN` are not available in the sidecar, Anthropic OIDC requests fail closed with:
Expand Down Expand Up @@ -770,7 +772,7 @@ Exchanges the GitHub OIDC JWT for a GCP access token via the Security Token Serv
Default OIDC audience: the `gcpWorkloadIdentityProvider` value

:::note
`ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN` are injected by the Actions runner automatically. AWF forwards them to the sidecar when `AWF_AUTH_TYPE=github-oidc` and currently also passes them through to the agent.
`ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN` are injected by the Actions runner automatically. AWF forwards them to the sidecar when `AWF_AUTH_TYPE=github-oidc` and excludes them from the agent container.
:::

:::tip
Expand Down
2 changes: 2 additions & 0 deletions docs/auth-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ All OIDC flows require GitHub Actions runtime tokens:
- `ACTIONS_ID_TOKEN_REQUEST_URL` — endpoint to mint OIDC JWTs
- `ACTIONS_ID_TOKEN_REQUEST_TOKEN` — auth token for the OIDC endpoint

AWF forwards these variables only to the api-proxy sidecar in `github-oidc` mode and excludes them from the agent container. GitHub Agentic Workflows independently passes them from its runner-owned **Start MCP Gateway** step directly to the MCP gateway when a remote HTTP MCP server uses `auth.type: github-oidc`; AWF does not launch or configure that gateway. See [github/gh-aw#50053](https://github.com/github/gh-aw/issues/50053) for lock-file compatibility tracking.

### Azure (Entra ID)

| Config | Env Var | Required |
Expand Down
18 changes: 10 additions & 8 deletions docs/authentication-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ AWF does not provide this mode. The diagram shows the risk that the always-on si

## OIDC authentication (keyless credential exchange)

AWF also supports **keyless authentication** via GitHub Actions OIDC workload identity federation. Instead of static API keys, the api-proxy sidecar exchanges a short-lived GitHub-issued JWT for provider-specific credentials — without the agent ever seeing any secret.
AWF also supports **keyless authentication** via GitHub Actions OIDC workload identity federation. Instead of static API keys, the api-proxy sidecar exchanges a short-lived GitHub-issued JWT for provider-specific credentials. The Actions token-minting variables, minted JWT, and exchanged credentials remain outside the agent container.

### How native GitHub Actions OIDC works

Expand Down Expand Up @@ -609,15 +609,15 @@ In a standard GitHub Actions workflow (without AWF), OIDC federation works like

### How AWF OIDC works (credential isolation)

AWF keeps the minted GitHub JWT and exchanged cloud credential in the api-proxy sidecar. However, the Actions runtime URL and token used to request a JWT are currently forwarded to the agent as well as the sidecar:
AWF keeps the Actions OIDC request capability, minted GitHub JWT, and exchanged cloud credential in the api-proxy sidecar:

```
┌─────────────────────────────┐ ┌───────────────────────────────────────┐
│ Agent Container │ │ API Proxy Sidecar │
│ 172.30.0.20 │ │ 172.30.0.30 │
│ │ │ │
│ Environment: │ │ Environment: │
ACTIONS_ID_TOKEN_* │ │ ✓ ACTIONS_ID_TOKEN_REQUEST_URL │
✗ No ACTIONS_ID_TOKEN_* │ │ ✓ ACTIONS_ID_TOKEN_REQUEST_URL │
│ ✗ No cloud credentials │ │ ✓ ACTIONS_ID_TOKEN_REQUEST_TOKEN │
│ ✗ No API keys │ │ ✓ AWF_AUTH_TYPE=github-oidc │
│ ✓ OPENAI_BASE_URL= │ │ ✓ AWF_AUTH_PROVIDER=azure|aws|gcp|anthropic │
Expand Down Expand Up @@ -648,19 +648,21 @@ AWF keeps the minted GitHub JWT and exchanged cloud credential in the api-proxy

#### Step 1: Configuration forwarding

The AWF CLI forwards `AWF_AUTH_*` configuration only to the api-proxy sidecar. The Actions runtime OIDC request URL and token are different: `passthroughHostEnvironment()` currently forwards both to the agent as well as `buildOidcEnv()` forwarding them to the sidecar.
The AWF CLI forwards `AWF_AUTH_*` configuration and the Actions runtime OIDC request URL and token only to the api-proxy sidecar. `buildOidcEnv()` conditionally adds the runtime variables to the sidecar in `github-oidc` mode, while `buildExclusionSet()` prevents every agent environment input path from adding them.

```
Host environment Sidecar container Agent container
───────────────── ───────────────── ───────────────
AWF_AUTH_TYPE=github-oidc ──► AWF_AUTH_TYPE ✓ ✗ (excluded)
AWF_AUTH_PROVIDER=azure ──► AWF_AUTH_PROVIDER ✓ ✗ (excluded)
AWF_AUTH_AZURE_TENANT_ID=... ──► AWF_AUTH_AZURE_TENANT_ID ✓ ✗ (excluded)
ACTIONS_ID_TOKEN_REQUEST_URL ──► forwarded when type=oidc ✓ ✓ (forwarded)
ACTIONS_ID_TOKEN_REQUEST_URL ──► forwarded when type=oidc ✓ ✗ (excluded)
```

:::caution[OIDC minting capability is visible to the agent]
With `permissions: id-token: write`, `ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN` let agent code request a GitHub OIDC JWT for an audience. AWF keeps the resulting provider credential in the sidecar, but it does not currently isolate this token-minting capability from the agent. Scope federation trust policies narrowly to the repository, workflow, ref, and expected audience.
:::note[OIDC-authenticated MCP servers]
GitHub Agentic Workflows supports `auth.type: github-oidc` for remote HTTP MCP servers through its compiler-managed MCP gateway. The generated **Start MCP Gateway** workflow step runs on the Actions runner before the AWF agent, passes the Actions variables directly to the gateway, and supplies only the gateway endpoint to the agent. The gateway mints an audience-bound JWT and injects it into the remote MCP request. AWF does not launch or configure the gateway.

Lock files generated by compiler versions that do not pass the variables directly from the runner to the gateway must be recompiled. See [github/gh-aw#50053](https://github.com/github/gh-aw/issues/50053) for compatibility validation and migration tracking.
:::

#### Step 2: GitHub OIDC token minting
Expand Down Expand Up @@ -752,7 +754,7 @@ For Anthropic bearer requests, AWF merges the OAuth beta with client-supplied `a
|----------|----------------|-----------------|
| Credential type | Long-lived secret | Short-lived token (~1h) |
| Rotation | Manual | Automatic (proactive refresh) |
| Agent sees credential material | No real provider key | No minted JWT or exchanged provider credential, but the Actions OIDC request token/URL are currently forwarded to the agent |
| Agent sees credential material | No real provider key | No Actions OIDC request token, minted JWT, or exchanged provider credential |
| GitHub Actions requirement | API key in secrets | `permissions: id-token: write` |
| Cloud provider setup | Generate API key | Configure trust policy/federation |
| Supported providers | OpenAI, Anthropic, Copilot, Gemini, Vertex AI | Azure (OpenAI/Copilot), GCP (OpenAI/Copilot adapters only — not the native Vertex/Gemini adapters), Anthropic WIF, AWS Bedrock Runtime via OpenAI/Copilot adapters |
Expand Down
7 changes: 5 additions & 2 deletions docs/awf-config-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ passthrough. A conforming implementation MUST NOT inherit them from the host:
|----------|-----------|
| System | `PATH`, `PWD`, `OLDPWD`, `SHLVL`, `_`, `SUDO_COMMAND`, `SUDO_USER`, `SUDO_UID`, `SUDO_GID` |
| Proxy | `HTTP_PROXY`, `HTTPS_PROXY`, `http_proxy`, `https_proxy`, `NO_PROXY`, `no_proxy`, `ALL_PROXY`, `all_proxy`, `FTP_PROXY`, `ftp_proxy` |
| Actions artifact tokens | `ACTIONS_RUNTIME_TOKEN`, `ACTIONS_RESULTS_URL` |
| Actions runtime credentials | `ACTIONS_RUNTIME_TOKEN`, `ACTIONS_RESULTS_URL`, `ACTIONS_ID_TOKEN_REQUEST_URL`, `ACTIONS_ID_TOKEN_REQUEST_TOKEN` |
| AWF internal controls | `AWF_PREFLIGHT_BINARY`, `AWF_GEMINI_ENABLED` |

> **Note:** Host proxy variables are read for upstream proxy auto-detection
Expand All @@ -314,13 +314,16 @@ the following host variables into the agent container:
|----------|-----------|
| GitHub authentication | `GITHUB_TOKEN`, `GH_TOKEN`, `GITHUB_PERSONAL_ACCESS_TOKEN` |
| GitHub enterprise | `GITHUB_SERVER_URL`, `GITHUB_API_URL` |
| Actions OIDC | `ACTIONS_ID_TOKEN_REQUEST_URL`, `ACTIONS_ID_TOKEN_REQUEST_TOKEN` |
| Docker client | `DOCKER_HOST`, `DOCKER_TLS`, `DOCKER_TLS_VERIFY`, `DOCKER_CERT_PATH`, `DOCKER_CONFIG`, `DOCKER_CONTEXT`, `DOCKER_API_VERSION`, `DOCKER_DEFAULT_PLATFORM` |
| User environment | `USER`, `XDG_CONFIG_HOME` |

When `--env-all` IS active, all host variables not in the excluded set
(§8.3) SHALL be forwarded, subject to credential isolation rules (§9).

Actions OIDC request variables MUST be forwarded directly to the api-proxy
sidecar when `apiProxy.auth.type` is `github-oidc` and MUST NOT be forwarded
to the agent through any environment input path.

### 8.5 Explicit Overrides

Variables passed via `-e` / `--env` MUST override values from `--env-all`
Expand Down
4 changes: 2 additions & 2 deletions docs/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Using `--env-all` passes all host environment variables to the container, which
3. **Unnecessary Access**: Extra variables increase attack surface (violates least privilege)
4. **Accidental Sharing**: Easy to forget what's in your environment when sharing commands

**Excluded variables** (even with `--env-all`): `PATH`, `PWD`, `OLDPWD`, `SHLVL`, `_`, `SUDO_*`
**Excluded variables** (even with `--env-all`): `PATH`, `PWD`, `OLDPWD`, `SHLVL`, `_`, `SUDO_*`, `ACTIONS_RUNTIME_TOKEN`, `ACTIONS_RESULTS_URL`, `ACTIONS_ID_TOKEN_REQUEST_URL`, and `ACTIONS_ID_TOKEN_REQUEST_TOKEN`. Actions OIDC variables are forwarded directly to the api-proxy sidecar in `github-oidc` mode, never to the agent.

**Proxy variables:** `HTTP_PROXY`, `HTTPS_PROXY`, `http_proxy`, `https_proxy`, `NO_PROXY`, `no_proxy`, `ALL_PROXY`, and `FTP_PROXY` (all case variants) from the host are **excluded from container passthrough** when using `--env-all`. The firewall sets its own proxy variables pointing to Squid inside the container. However, host proxy variables **are read** for upstream proxy auto-detection — if the host has `https_proxy`/`http_proxy` set, AWF configures Squid to chain outbound traffic through that corporate proxy (see [Upstream Proxy Support](#upstream-corporate-proxy-support)).

Expand All @@ -64,7 +64,7 @@ Using `--env-all` passes all host environment variables to the container, which
3. `--env-file` variables
4. `--env` / `-e` explicit variables (highest priority)

**Excluded variables** in `--env-file` (same list as `--env-all`): `PATH`, `PWD`, `HOME`, `SUDO_*`, etc.
**Excluded variables** in `--env-file` (same list as `--env-all`): `PATH`, `PWD`, `HOME`, `SUDO_*`, Actions runtime credentials, etc. Explicit `--env` cannot override credential exclusions.

**Example use case — Safe Outputs MCP:**
```bash
Expand Down
14 changes: 8 additions & 6 deletions docs/sbx-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,9 @@ What `createSandbox()` shares, in order:
`accessTokens.json`, `service_principal_entries.json`) are treated as
credential stores and scrubbed before sandbox creation (sbx) or masked with
`/dev/null` overlays (compose). Agents cannot read host Azure auth tokens
directly. Azure authentication must be obtained at runtime via OIDC
(`ACTIONS_ID_TOKEN_REQUEST_URL`/`TOKEN`, already forwarded) or via the
`ADO_MCP_AUTH_TOKEN` environment variable.
directly. Azure API authentication must be handled by the api-proxy's
sidecar-only OIDC exchange or by an external trusted service. The separate
`ADO_MCP_AUTH_TOKEN` environment variable remains available for ADO MCP.
:::

**Scrubbing nested credential stores.** Several whitelisted dirs legitimately
Expand All @@ -238,9 +238,11 @@ them after the sandbox is torn down** (`scrubHomeCredentials` /
the secrets are absent from the VM while the benign tool state stays available.
This is the sbx analog of compose mode's `/dev/null` credential overlays, and the
central credential list in `sandbox-mount-policy.json` is shared between backends
to prevent drift. The agent receives whatever credentials it needs through the
api-proxy or environment (e.g. `ADO_MCP_AUTH_TOKEN`, OIDC tokens), not by reading
the host's on-disk auth store, so removing these paths is safe.
to prevent drift. The agent accesses OIDC-backed providers through requests
routed to the api-proxy, or receives separately allowed environment credentials
such as `ADO_MCP_AUTH_TOKEN`, not by reading the host's on-disk auth store.
Provider credentials and Actions OIDC request variables remain in the api-proxy
or another trusted external service, so removing these paths is safe.

A `seenPaths` set deduplicates so no path is mounted twice, and
`execInSandbox(..., { workDir })` passes `--workdir` so commands run inside the
Expand Down
2 changes: 1 addition & 1 deletion src/config/sandbox-mount-policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"etc": ["/etc/ssl", "/etc/ca-certificates", "/etc/pki/ca-trust/extracted", "/etc/pki/tls/certs", "/etc/alternatives", "/etc/ld.so.cache", "/etc/nsswitch.conf"]
},
"home": {
"$comment": "Agent $HOME exposure. `toolSubdirs` is the ALLOW list: tool caches, language toolchains and agent state the agent legitimately needs. `forbiddenSubdirs` is a DENY guard: dirs whose primary purpose is storing credentials and which must NEVER be added to the allow list. Compose mounts an empty home + binds toolSubdirs on top; sbx mounts toolSubdirs wholesale instead of the whole $HOME. EXCEPTION: `.azure` is credential-bearing — it is intentionally mounted to provide Azure CLI config and account metadata, but its live token caches (msal_token_cache.bin, msal_token_cache.json, accessTokens.json, service_principal_entries.json) are masked by the credentials deny list so agents cannot read host auth tokens directly. Azure auth must come via OIDC (ACTIONS_ID_TOKEN_REQUEST_URL/TOKEN) or the ADO_MCP_AUTH_TOKEN env var.",
"$comment": "Agent $HOME exposure. `toolSubdirs` is the ALLOW list: tool caches, language toolchains and agent state the agent legitimately needs. `forbiddenSubdirs` is a DENY guard: dirs whose primary purpose is storing credentials and which must NEVER be added to the allow list. Compose mounts an empty home + binds toolSubdirs on top; sbx mounts toolSubdirs wholesale instead of the whole $HOME. EXCEPTION: `.azure` is credential-bearing — it is intentionally mounted to provide Azure CLI config and account metadata, but its live token caches (msal_token_cache.bin, msal_token_cache.json, accessTokens.json, service_principal_entries.json) are masked by the credentials deny list so agents cannot read host auth tokens directly. Azure API auth must use the api-proxy's sidecar-only OIDC exchange or another trusted external service; ADO MCP may use its separate ADO_MCP_AUTH_TOKEN env var.",
"toolSubdirs": [
".cache",
".config",
Expand Down
Loading
Loading