Skip to content
Open
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
1 change: 1 addition & 0 deletions integrations/isolation/acq-kits/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ not agent behavior. (Behavioral patterns live in `skills/`, `prompts/`, etc.)
|-----|---------|
| [`usai-provider/`](usai-provider/) | Configure the agent to use the GSA USAi model provider (OpenCode today), with egress allow-listed. |
| [`agentic-coding-playbook/`](agentic-coding-playbook/) | Clone the GSA agentic-coding-playbook at startup and link its `AGENTS.md` + skills into each agent's search paths. |
| [`cloud-gov/`](cloud-gov/) | Install the Cloud Foundry CLI and allow-list cloud.gov platform, dashboard, and docs domains, with auth supplied through acq secrets. |
| [`zscaler-ca-certificate/`](zscaler-ca-certificate/) | Trust the public Zscaler Root CA in the sandbox (msb: native `--trust-host-cas`; sbx: file-drop + `update-ca-certificates`). |
| [`git-ssh-sign/`](git-ssh-sign/) | Sign git commits and tags with the SSH key forwarded from the host agent (vendored from sbx-kits-contrib). |
| [`openchamber/`](openchamber/) | Run OpenChamber, a browser UI for OpenCode, inside the sandbox alongside the terminal TUI. Opt-in (see its parity note). |
Expand Down
160 changes: 160 additions & 0 deletions integrations/isolation/acq-kits/cloud-gov/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# cloud-gov (acq mixin kit, `hybrid/v1`)

A neutral [`acq`](https://github.com/GSA-TTS/agentic-coding-quickstart) **mixin
kit** for working with [cloud.gov](https://cloud.gov/) from an isolated coding
sandbox.

> **Neutral (backend-agnostic) kit.** This is the `schemaVersion: "hybrid/v1"`
> form consumed by `acq`, which abstracts the isolation backend. See
> [backend parity](#backend-parity) and
> [`../../docs/decisions/0001-neutral-hybrid-v1-acq-kits.md`](../../docs/decisions/0001-neutral-hybrid-v1-acq-kits.md).

## What It Does

- **Cloud Foundry CLI** - uses an existing `cf` binary when the base image already
has one. If `cf` is missing, installs Cloud Foundry CLI v8.19.0 from pinned
upstream release tarballs with committed SHA-256 checks.
- **cloud.gov egress** - allow-lists exact cloud.gov control-plane and
documentation hosts: the Cloud Foundry API, login/UAA, log-stream hosts, and
user-facing cloud.gov documentation. Public app routes and custom domains stay
project-specific instead of using wildcard egress.
- **Cloud Foundry docs egress** - allow-lists upstream Cloud Foundry CLI and docs
hosts so agents can look up command behavior and platform guidance.
- **Secret-safe authorization** - documents the `acq secret set` binding for a
host-side `cf oauth-token`. The sandbox receives only the backend placeholder
needed for direct Cloud Foundry API calls; the real token stays in the host-side
secret store / backend proxy.

## Backend Parity

The kit declares `sbx` and `msb` support. Both backends consume the neutral kit
fields: `caps.network.allow`, file drops, and create-time/startup commands. There
is no backend shortcut.

The Cloud Foundry token is not a kit field. The user stores it in the acq secret
store and binds it to `api.fr.cloud.gov`; the active backend maps that to its
own proxy or secret-substitution mechanism. The kit validates that any injected
`CF_OAUTH_TOKEN` value is a backend placeholder, not raw token material.

The network allow-list avoids wildcard semantics. Both sbx and msb receive the
same exact host list, which keeps the shared kit least-privilege and makes app
route/custom-domain access an explicit project-specific decision.

## Usage

Apply the kit alongside an agent sandbox:

```bash
acq run --kit <path-to-this-kit> opencode /path/to/project
```

The kit is a `mixin`, so it composes with the other acq-kits.

## CF CLI Installation Pin

If the base image already has `cf`, the install step exits successfully without
network package installation. If `cf` is missing, the kit installs Cloud Foundry
CLI v8.19.0 from the upstream GitHub release tarball for `linux_x86-64` or
`linux_arm64`, then verifies the archive SHA-256 before installing it. Unsupported
architectures fail closed.

To update the CLI, change the version and both SHA-256 constants in
`files/home/cloud-gov-install-cf-cli.sh` together after reviewing the upstream
release metadata.

## Prerequisites

From a host shell, log in to cloud.gov with the normal human-controlled flow:

```bash
cf login -a https://api.fr.cloud.gov --sso
```

Then store the short-lived Cloud Foundry OAuth token string in acq. `cf
oauth-token` returns the full authorization value, including the `bearer` prefix;
this command passes that value over stdin to the host-side secret store. Do not
paste it into chat or write it to a file:

```bash
cf oauth-token | acq secret set -g cloud-gov --host api.fr.cloud.gov --env CF_OAUTH_TOKEN
```

For a sandbox-specific token, scope the secret to that sandbox:

```bash
cf oauth-token | acq secret set <sandbox-name> cloud-gov --host api.fr.cloud.gov --env CF_OAUTH_TOKEN
```

The `CF_OAUTH_TOKEN` name is a placeholder binding for backend proxy/secret
substitution. At startup the kit targets `https://api.fr.cloud.gov` and, when
that value is present, refuses to continue unless it matches a recognized
backend-placeholder prefix, including the documented msb guest-secret placeholder
prefix. The startup guard rejects bearer/JWT-like raw token material before
anything can be written to the workspace. The stock CF CLI
may parse `AccessToken` locally, so the kit does not write the placeholder into
CF CLI config or claim authenticated `cf` commands work without live
verification.

## Network Allow-List

The kit allow-lists:

| Host | Purpose |
|------|---------|
| `api.fr.cloud.gov` | Cloud Foundry API |
| `uaa.fr.cloud.gov`, `login.fr.cloud.gov` | cloud.gov UAA and browser-login control plane |
| `doppler.fr.cloud.gov`, `log-cache.fr.cloud.gov` | Cloud Foundry log streaming/control-plane endpoints |
| `cloud.gov`, `www.cloud.gov`, `docs.cloud.gov` | cloud.gov public documentation |
| `docs.cloudfoundry.org`, `cli.cloudfoundry.org` | Cloud Foundry documentation |
| `github.com`, `objects.githubusercontent.com` | Pinned Cloud Foundry CLI release tarball download during kit install; if runtime access to these hosts is not acceptable for a deployment context, use a base image with `cf` preinstalled or a project-specific vetted artifact source. |

The kit intentionally does not allow `*.cloud.gov`. If the project needs a
specific app route, route service, or custom domain, add that exact host in a
project-specific kit or sandbox policy after review.

## Using `cf`

Inside the sandbox, use `cf` for unauthenticated operations such as targeting the
cloud.gov API:

```bash
cf api https://api.fr.cloud.gov
cf target
```

For authenticated checks, use direct HTTPS calls to the Cloud Foundry API so the
backend can rewrite the placeholder on the wire:

```bash
curl -H "Authorization: $CF_OAUTH_TOKEN" https://api.fr.cloud.gov/v3/organizations
```

If authenticated API calls fail, refresh the host-side secret with `cf oauth-token
| acq secret set ...` and restart or recreate the sandbox so the placeholder is
injected. Do not paste the token into chat or commit it to the workspace.

## Troubleshooting

Failure-mode notes are in [`TROUBLESHOOTING.md`](TROUBLESHOOTING.md).

## Design Decisions

- [`docs/decisions/cloud-gov-cli-and-auth.md`](docs/decisions/cloud-gov-cli-and-auth.md)
- why this kit installs `cf`, uses exact cloud.gov egress, and relies on acq
placeholder-based auth.

## Verifying

Run the bundled check:

```bash
./scripts/verify
```

By default it runs offline checks only: repository kit validation, shell syntax
for kit scripts, auth-script guard tests with synthetic placeholders, and exact
egress allow-list assertions. Set `RUN_ACQ=1` to create a throwaway sandbox and
verify that `cf` is installed, cloud.gov/docs endpoints are reachable, and the
injected placeholder reaches the CF API through a direct HTTPS request.
`RUN_ACQ=1` requires a global `cloud-gov` acq secret. Set `KEEP=1` with
`RUN_ACQ=1` to keep the sandbox for inspection.
95 changes: 95 additions & 0 deletions integrations/isolation/acq-kits/cloud-gov/TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Troubleshooting - cloud-gov kit

These are failure modes specific to the cloud-gov acq mixin kit.

## `cf` Is Not Installed

**Symptoms:** `cf version` fails inside the sandbox.

**Causes and fixes:**

- **Install egress blocked.** Check the sandbox policy log and confirm the CF CLI
package hosts in `spec.yaml` are allowed.
- **Unsupported architecture.** The install script supports upstream Linux
x86_64/amd64 and arm64/aarch64 tarballs. For another architecture, install the
CF CLI in the base image or extend `files/home/cloud-gov-install-cf-cli.sh`
with a pinned artifact and SHA-256.
- **Release artifact changed.** The install script verifies the downloaded
tarball SHA-256. A mismatch fails closed; review the upstream release metadata
before changing the pinned version or hash.

## `cf api https://api.fr.cloud.gov` Fails

**Symptoms:** `cf api` times out or cannot resolve/reach the API host.

**Causes and fixes:**

- **Kit not applied.** Recreate the sandbox with this kit applied.
- **Egress blocked by org policy.** Check `acq policy log <sandbox>` or the
active backend's policy log for `api.fr.cloud.gov`.
- **TLS trust issue.** If your network uses TLS inspection, compose this kit with
`zscaler-ca-certificate` or the local equivalent CA-trust kit.

## CF Authentication Fails

**Symptoms:** cloud.gov is reachable, but `cf` reports unauthorized, forbidden,
or no current user.

**Causes and fixes:**

- **Missing or expired host-side token.** Cloud Foundry OAuth tokens are
short-lived. If `scripts/verify` reports `authenticated /v3/organizations ->
401` or `403`, refresh the acq secret from a host shell where you are logged in
to cloud.gov:

```bash
cf oauth-token | acq secret set -g cloud-gov --host api.fr.cloud.gov --env CF_OAUTH_TOKEN
```

- **Wrong scope.** If the sandbox has a sandbox-scoped `cloud-gov` secret, update
that scoped secret instead of the global one:

```bash
cf oauth-token | acq secret set <sandbox-name> cloud-gov --host api.fr.cloud.gov --env CF_OAUTH_TOKEN
```

- **Unexpected token value in the sandbox.** The startup script only accepts
recognized backend placeholders in `CF_OAUTH_TOKEN`, including sbx/acq forms
and the documented msb guest-secret placeholder prefix.
If a raw `bearer ...` token, a JWT-looking value, or a value with control
characters appears in the sandbox, startup fails closed before writing CF CLI
auth state. Refresh the acq secret from the host and recreate the sandbox. Do
not paste the token into chat or commit it to the workspace.
- **CF CLI wants a local login.** The kit does not write the placeholder into
`~/.cf/config.json` because stock `cf` may parse cached tokens locally before
sending a request. Use direct Cloud Foundry API calls with the placeholder auth
header for authenticated probes unless a live-tested backend path for
authenticated `cf` commands is added later.

## A cloud.gov App Route Is Blocked

**Symptoms:** `curl https://<app>.app.cloud.gov` or a browser/API request to a
cloud.gov app route fails because egress is denied.

**Causes and fixes:**

- **Expected shared-kit limit.** The kit intentionally allow-lists exact
cloud.gov control-plane and documentation hosts only. It does not include a
`*.cloud.gov` wildcard, because wildcard semantics differ by backend and widen
egress beyond least privilege. Add the specific app route, route service, or
custom domain as a project-specific network allow-list entry after review.
- **Backend parity check.** Confirm the generated backend policy from `acq`
contains the exact host you added; both sbx and msb should receive concrete
host allow rules, not a suffix wildcard.

## Docs Are Not Reachable

**Symptoms:** Requests to cloud.gov or Cloud Foundry docs fail.

**Causes and fixes:**

- Confirm the requested host is one of `cloud.gov`, `www.cloud.gov`,
`docs.cloud.gov`, `docs.cloudfoundry.org`, `cli.cloudfoundry.org`, or an exact
cloud.gov control-plane host listed in `spec.yaml`.
- If docs pull assets from another host and the backend blocks them, add the
specific observed host after review instead of adding a broad wildcard.
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Decision: install CF CLI and use acq secret placeholder auth

**Status:** accepted

## Context

cloud.gov operations use the Cloud Foundry CLI (`cf`) against the cloud.gov
Cloud Foundry API. A sandbox used for this work needs the CLI, egress to exact
cloud.gov control-plane hosts, and access to cloud.gov and Cloud Foundry
documentation.

Authentication is sensitive. A token from `cf oauth-token` must not be committed,
printed in chat, or written into agent-readable workspace files. At the same
time, stock CF CLI auth paths may parse cached access tokens locally before
sending a request, so a non-JWT backend placeholder is not enough to prove
interactive `cf` authentication works.

## Decision

Create a neutral `cloud-gov` acq mixin kit for sbx and msb that:

1. Installs `cf` at create time from pinned Cloud Foundry CLI release tarballs
with committed SHA-256 values.
2. Allow-lists exact cloud.gov control-plane/documentation hosts and Cloud
Foundry docs domains, without wildcard semantics.
3. Uses acq's custom secret binding for a host-side token captured with
`cf oauth-token`:

```sh
cf oauth-token | acq secret set -g cloud-gov --host api.fr.cloud.gov --env CF_OAUTH_TOKEN
```

4. Validates any injected `CF_OAUTH_TOKEN` value as a recognized backend
placeholder, including the documented msb guest-secret placeholder prefix,
and refuses raw token-looking values. The placeholder can be used for direct
HTTPS Cloud Foundry API calls that the backend proxy rewrites on the way to
`api.fr.cloud.gov`.

The kit does not store the real Cloud Foundry OAuth token in `spec.yaml`, files,
or repository docs. The token remains in the host-side acq secret store and the
active backend's secret/proxy mechanism. The kit does not write a placeholder to
`~/.cf/config.json`; authenticated `cf` command support requires a separately
live-tested path because the stock CLI may parse cached tokens locally.

## Consequences

- Agents can use `cf` for Cloud Foundry targeting and unauthenticated inspection.
Authenticated operations should use direct HTTPS Cloud Foundry API calls until a
live-tested path for authenticated stock `cf` commands exists.
- The real token remains outside the sandbox; the sandbox may contain only a
backend-generated placeholder.
- The install currently supports Linux x86_64/amd64 and arm64/aarch64 release
tarballs. Other architectures should either preinstall `cf` or add a pinned,
integrity-checked install path before being supported.
- Egress is exact-host only for shared-kit backend parity and least privilege.
The kit includes the Cloud Foundry API, UAA/login, log streaming, cloud.gov
documentation, Cloud Foundry documentation, and pinned CLI download hosts.
The GitHub hosts are needed only when the base image lacks `cf`; if runtime
access to those hosts is unacceptable, use a base image with `cf` preinstalled
or a project-specific vetted artifact source. Public app routes, route
services, and custom domains are intentionally not included because they are
project-specific and should be added as exact host rules after review.
- Live verification with `RUN_ACQ=1` requires a configured `cloud-gov` secret and
must exercise raw authenticated CF API access through the backend placeholder.
Offline verification remains useful for schema/shell checks and auth guard
tests, but it is not evidence that cloud.gov auth wiring works end to end.

## Links

- `../../README.md` - operator usage and troubleshooting entry points.
- `../../spec.yaml` - authoritative kit behavior.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env sh
set -eu

# Configure the CF CLI to use cloud.gov. If acq injected a backend placeholder in
# CF_OAUTH_TOKEN, validate that it is not raw token material. The stock CF CLI
# parses AccessToken locally, so this script deliberately does not write a
# placeholder into ~/.cf/config.json and overclaim authenticated cf CLI support.

log() { printf '%s\n' "cloud-gov: $*" >&2; }

mkdir -p "$HOME/.cf"
chmod 0700 "$HOME/.cf"

if [ -n "${CF_OAUTH_TOKEN:-}" ]; then
msb_prefix='$'MSB_
case "$CF_OAUTH_TOKEN" in
*[![:print:]]*|*[[:space:]]*)
log "CF_OAUTH_TOKEN placeholder contains an unsafe character; refusing startup"
exit 1
;;
esac

case "$CF_OAUTH_TOKEN" in
bearer*|Bearer*|BEARER*)
log "CF_OAUTH_TOKEN looks like raw token material; refusing startup"
exit 1
;;
acq_placeholder_?*|sbx-cs-?*|"$msb_prefix"?*)
:
;;
*.*.*)
log "CF_OAUTH_TOKEN looks like raw token material; refusing startup"
exit 1
;;
*)
log "CF_OAUTH_TOKEN is not a recognized backend placeholder; refusing startup"
exit 1
;;
esac

log "validated CF_OAUTH_TOKEN as a backend placeholder"
else
log "no CF_OAUTH_TOKEN placeholder present; cf is targeted but unauthenticated"
fi

if command -v cf >/dev/null 2>&1; then
env CF_OAUTH_TOKEN= cf api https://api.fr.cloud.gov >/dev/null 2>&1 || log "warning: cf api target failed; check egress/TLS"
fi
Loading
Loading