-
Notifications
You must be signed in to change notification settings - Fork 0
aws sso
harnessed works fully without AWS. This guide is for operators who use
aws-sso-cli on the host and want their
stacks to make authenticated AWS calls — without baking any credential into an image,
copying ~/.aws-sso into the container, or leaking a long-lived SSO token past the host.
It is opt-in per stack and env-only, the same posture as secrets and the git-credential forward: a container gets AWS authority only when the stack asks for it, and only short-lived STS credentials ever cross the boundary.
The profiles aws-sso-cli writes into ~/.aws/config look like:
[profile pinnacle-dev]
credential_process = aws-sso -S "pinnacle" process --arn arn:aws:iam::…:role/AWSAdministratorAccess
region = us-east-1That credential_process needs the aws-sso binary, the ~/.aws-sso/ config, the encrypted
secure store, and a live SSO token — all inside the container. Mounting that surface into
a container is exactly what harnessed forbids: it hands the container standing authority to mint
credentials for every role you can assume, and puts secret material one image layer away.
aws-sso-cli ships a proxy built for this. aws-sso ecs server runs on the host and speaks
the same HTTP protocol AWS uses to inject
ECS task-role credentials.
Any AWS SDK/CLI that sees AWS_CONTAINER_CREDENTIALS_FULL_URI fetches short-lived STS
credentials from that endpoint — no profile, no binary, no SSO token required in the client.
host: aws-sso ecs server long-running; holds STS creds for the loaded role in memory
host: aws-sso ecs load you pick a role → it fills the server's "default slot"
ctr: AWS SDK → http://host.containers.internal:4144/ → temporary STS creds
This is the exact analog of harnessed's SSH-agent forward: the container gets a reachable
endpoint, never the key material. The server itself has no access to the secure store — the
only credentials it can serve are those you explicitly aws-sso ecs load into it.
harnessed aws-sso serveOn first run this generates a random bearer token, loads it into the aws-sso secure store, and
records it at ~/.config/harnessed/aws-sso-ecs.token (mode 0600) so the launcher can inject it.
Then it starts aws-sso ecs server in the foreground — leave this terminal running.
Prerequisite: aws-sso must be on your PATH (see the
aws-sso-cli install docs) and your SSO
instances configured in ~/.aws-sso/config.yaml.
In another terminal:
aws-sso ecs load # interactive role picker → fills the default slotEvery container consuming the server uses whichever role is loaded here. To switch the role for
all containers, just run aws-sso ecs load again and pick another — no relaunch needed.
In the stack's stack.yaml:
name: my-stack
harness: claude
forward_aws_sso: trueharnessed my-stackYour project's AWS code (boto3, aws-sdk-js/go/rust, …) picks up the credentials automatically —
every AWS SDK reads AWS_CONTAINER_CREDENTIALS_FULL_URI natively, so no application code
change is needed. The SDK is whatever your project already depends on.
To confirm from a shell you need the aws CLI, which is not in the base image. Either check
from Python if boto3 is available:
python -c "import boto3; print(boto3.client('sts').get_caller_identity()['Arn'])"or add the CLI once (mise-managed) and rebuild — see Getting the aws CLI in the container below:
aws sts get-caller-identityEither should report the role you loaded in step 2.
The container never needs aws-sso, and it only needs an AWS SDK if your workload calls AWS —
that SDK ships with your project's own dependencies (pnpm add @aws-sdk/…, uv add boto3), so it
is already present at runtime and reads the injected endpoint with no extra setup.
The aws CLI is a separate binary and is not baked into the base image. If you want it for
ad-hoc shell commands, add one line to your user-owned extra-tools list and rebuild:
echo 'aws-cli # AWS CLI (aqua:aws/aws-cli)' >> ~/.config/harnessed/extra-tools.txt
harnessed build # rebuild the base image with the extra toolWhen a stack sets forward_aws_sso: true, the launcher reads the bearer token from
~/.config/harnessed/aws-sso-ecs.token and injects two environment variables into the pod (env
only — never written to a profile, an image layer, or a repo file):
AWS_CONTAINER_CREDENTIALS_FULL_URI=http://host.containers.internal:4144/
AWS_CONTAINER_AUTHORIZATION_TOKEN=Bearer <token>-
host.containers.internalis how a harnessed pod reaches host services (the same name the hatago MCP hub uses to proxy host processes). The server must therefore bind an address the container can reach —harnessed aws-sso servedefaults to--bind-ip 0.0.0.0for exactly this reason.127.0.0.1keeps it host-only but is then unreachable from containers. -
The bearer token gates the endpoint. Because the server listens off-loopback so containers
can reach it, the token is what stops any other local (or on-network) process from using your
credentials.
harnessed aws-sso servesets one up automatically before starting the server. -
No-op when unconfigured. If the token file is absent (you never ran
harnessed aws-sso serve), the launcher injects nothing — aforward_aws_ssostack still launches fine, the AWS SDK simply finds no credentials. So the flag is safe to commit in a shared catalog.
The token is a single source of truth: harnessed aws-sso serve writes it, the launcher reads it,
and it also lives in the aws-sso secure store where the server reads it. You never type or paste it.
- Short-lived, but not touch-gated. The endpoint serves temporary STS credentials, not long-lived keys, and only for the one role currently loaded. But unlike the SSH signing agent (which prompts for a 1Password approval / YubiKey touch on every use), these credentials are usable by anything that can reach the endpoint with the token. That is why AWS forwarding is opt-in per stack, whereas the SSH agent is auto-forwarded whenever it is live.
-
The bearer token is the boundary. Choose to bind loopback-only (
--bind-ip 127.0.0.1, no container access) or accept off-loopback binding gated by a strong random token — which is what the wizard generates. Treat the token file as a secret; it is stored0600. -
Nothing is baked. No
aws-ssobinary, no~/.aws-sso, no SSO token, and no long-lived AWS key ever enters a container or an image. The credentials arrive as a per-launch-eand expire. - TLS caveat. aws-sso-cli supports TLS on the ECS server, but a bug in the AWS SDK prevents the SDK from trusting anything but a publicly-CA-signed cert on this endpoint. In practice the endpoint is bearer-token-over-HTTP on a host-local interface — acceptable for a developer laptop; do not expose the bind IP to an untrusted network.
aws-sso ecs load # change the default-slot role (all containers follow)
aws-sso ecs profile # print the currently loaded role
aws-sso ecs unload # remove the default-slot credentialsharnessed wires containers to the server's default slot (http://host.containers.internal:4144/),
so one role is active at a time across all forward_aws_sso stacks — switch it host-side with
aws-sso ecs load. aws-sso-cli can also hold several roles in named slots
(aws-sso ecs load --slotted, consumed via …/slot/<ProfileName>); harnessed does not target
named slots today. If you need that, set AWS_CONTAINER_CREDENTIALS_FULL_URI yourself in the
stack's env and file an issue.
AWS calls inside the container fail with "unable to locate credentials" or hang:
-
Server not running? The
harnessed aws-sso serveterminal must stay open. Check the host:curl -H "Authorization: Bearer $(cat ~/.config/harnessed/aws-sso-ecs.token)" http://127.0.0.1:4144/should return a credentials JSON (run it on the host, where loopback works). -
No role loaded? Run
aws-sso ecs load. The server exposes an unauthenticatedGET /healthcheckthat returns200only when the default slot has valid credentials. -
Flag not set? The stack needs
forward_aws_sso: true. Confirm the env reached the container:podman exec <instance> printenv AWS_CONTAINER_CREDENTIALS_FULL_URI. -
Token file missing? If you started the server by hand instead of via
harnessed aws-sso serve, the launcher has no token to inject. Run the wizard once to generate and record it. -
Bind IP unreachable? If you started the server with
--bind-ip 127.0.0.1, containers can't reach it. Restart with the default0.0.0.0(token-gated).
| Piece | Location |
|---|---|
| Stack opt-in flag |
forward_aws_sso: true in stack.yaml
|
| Host command | harnessed aws-sso serve [--port 4144] [--bind-ip 0.0.0.0] |
| Bearer token file |
~/.config/harnessed/aws-sso-ecs.token (0600) |
| Injected into container |
AWS_CONTAINER_CREDENTIALS_FULL_URI, AWS_CONTAINER_AUTHORIZATION_TOKEN
|
| Underlying tool | aws-sso-cli ECS server |
Start Here
Guides
- Recipe authoring
- Service authoring
- Stacks
- Extending stacks (proposed)
- Recipe catalog
- System prompt & rules (proposed)
- Secrets
- AWS SSO
- Pulumi (host login forwarding)
- Egress & exposing services
- Container filesystem
- Git hooks
- Troubleshooting
- Pin management (harnessed update)
Codebase Map
Planning & Roadmap
- open work: GitHub Issues
Research & Prompts
- research/ (home-folder requirements per harness, browse in-repo)
- prompts/ (reusable prompt templates, browse in-repo)