AWS Credentials¶
The aws-credentials namespace brokers AWS credential access between shed microVMs and the host machine. Long-lived AWS credentials never enter the VM — only short-lived STS session tokens cross the bus.
How It Works¶
shed-ext-aws-credentials runs inside the VM as a systemd service, listening on http://127.0.0.1:499. The AWS_CONTAINER_CREDENTIALS_FULL_URI environment variable points all AWS SDKs to this endpoint.
When an AWS SDK requests credentials, shed-ext-aws-credentials translates the request into a message bus call to the host agent. The host agent performs sts:AssumeRole using the developer's local AWS profile and returns temporary credentials.
sequenceDiagram
participant SDK as AWS SDK (any language)
participant Proxy as shed-ext-aws-credentials (VM)
participant Bus as Message Bus
participant Host as shed-host-agent (Mac)
participant STS as AWS STS
SDK->>Proxy: GET /credentials
Proxy->>Bus: publish(aws-credentials, get_credentials)
Bus->>Host: SSE event
Host->>Host: Check credential cache
alt Cache valid
Host->>Bus: cached credentials
else Cache stale
Host->>STS: sts:AssumeRole
STS->>Host: temporary credentials
Host->>Host: Update cache
Host->>Bus: fresh credentials
end
Bus->>Proxy: response
Proxy->>SDK: {AccessKeyId, SecretAccessKey, Token, Expiration}
AWS SDK Integration¶
The AWS SDK credential chain checks AWS_CONTAINER_CREDENTIALS_FULL_URI automatically across all languages (Java, Python, Go, Node, Kotlin). No application code changes needed.
The proxy serves credentials in the exact format the SDK expects:
{
"AccessKeyId": "ASIA...",
"SecretAccessKey": "...",
"Token": "...",
"Expiration": "2026-03-31T19:00:00Z"
}
The SDK handles refresh automatically — when credentials approach expiration, it re-requests from the endpoint.
Role Configuration¶
In the default assume-role mode, each shed maps to a specific IAM role via host-side configuration. The VM doesn't get to choose which role it receives. (For SSO/SAML setups where no assumable role exists, see Passthrough mode, which needs no role.)
aws:
source_profile: default
default_role: arn:aws:iam::123456789012:role/acmeco-dev
servers:
mini2:
sheds:
my-service:
role: arn:aws:iam::123456789012:role/acmeco-dev
integration-tests:
role: arn:aws:iam::123456789012:role/acmeco-staging-readonly
Passthrough mode (SSO/SAML)¶
AssumeRole requires a role the host's source session is allowed to assume. In SSO/SAML environments there is often no such role — the application's real role is an instance/service role (trusted only by a service principal), or the developer's own credentials came from sts:AssumeRoleWithSAML, and neither can be re-assumed with plain sts:AssumeRole.
Passthrough mode skips AssumeRole and vends the source_profile's existing session credentials directly. SSO/SAML CLI helpers conventionally write those temporary credentials to the standard ~/.aws/credentials file, so nothing new is needed to obtain them.
aws:
source_profile: my-sso # the profile whose session creds are vended
mode: passthrough # assume-role (default) | passthrough
approval:
policy: approve-all
mode layers exactly like role — set it at the top level, per server, or per shed (most specific wins). This lets one shed use passthrough while another keeps AssumeRole:
aws:
source_profile: default
default_role: arn:aws:iam::111111111111:role/dev # assume-role default
servers:
mini2:
sheds:
sso-app: { mode: passthrough }
scoped-app: { role: arn:aws:iam::111111111111:role/scoped }
modeis inherited, androleis ignored under passthrough. Whenmode: passthroughis set at a parent level it applies to every child shed unless a child overrides it withmode: assume-role; aroleconfigured on a passthrough shed has no effect.
Refresh is your responsibility. The host agent cannot perform your SSO/SAML login, so it cannot refresh these credentials — it only re-reads the shared file. Re-running your login (e.g. aws sso login) rewrites ~/.aws/credentials, and the agent picks up the new credentials on the next request, no restart needed. Passthrough is not cached for this reason.
Expiry. The standard shared-config loader treats profile credentials as static, so passthrough parses the helper-written aws_session_expiration / x_security_token_expires hint to report accurate expiry. If neither key is present, the Expiration field is omitted and the guest discovers expiry on a 403. The omit-expiry behavior is verified for the Go AWS SDK; if your guest app uses a non-Go SDK (boto3, JS v3, Java) that requires Expiration, make sure your helper writes aws_session_expiration. Only the standard aws_session_token key is read — the legacy aws_security_token written by some SAML helpers is not supported.
Tradeoffs vs AssumeRole¶
| AssumeRole (default) | Passthrough | |
|---|---|---|
| IAM prerequisite | a role the source can assume | none |
| Identity vended | scoped assumed role | the source identity itself |
| Lifetime in VM | short (e.g. 1h), auto-refreshed | full remaining source session |
| Least privilege | tight | broad |
Passthrough is less least-privilege, but it still delivers the extension's core wins over copying the credentials file into the VM: host-brokered, per-request audited, and no credential written to the VM's disk — and it's revocable by stopping the host agent.
Message Format¶
Request¶
{
"id": "0192b3a5-...",
"namespace": "aws-credentials",
"type": "request",
"payload": {
"operation": "get_credentials"
}
}
Response¶
{
"id": "0192b3a5-...",
"namespace": "aws-credentials",
"type": "response",
"payload": {
"access_key_id": "ASIAIOSFODNN7EXAMPLE",
"secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"session_token": "FwoGZXIvYXdzE...",
"expiration": "2026-03-31T19:00:00Z"
}
}
Error¶
{
"id": "0192b3a5-...",
"namespace": "aws-credentials",
"type": "response",
"payload": {
"error": "sts:AssumeRole failed",
"code": "ASSUME_ROLE_FAILED"
}
}
Credential Caching¶
The guest-side proxy does not cache credentials — every SDK request passes through to the host. In assume-role mode the host handler maintains a per-shed credential cache:
- Credentials are cached until they have less than 5 minutes remaining
- When the cache is stale, a fresh
sts:AssumeRolecall is made - The AWS SDK's own refresh timing (~once per hour) drives re-fetching
Passthrough mode is never cached — the shared credentials file is re-read on every request so a fresh SSO/SAML login is picked up immediately.
Cache parameters are configurable:
aws:
session_duration: 1h # STS token lifetime
cache_refresh_before: 5m # refresh when < 5 min remaining
STS Session Details¶
These apply to assume-role mode; passthrough mode performs no AssumeRole and creates no STS session.
- Session name format:
shed-{server}-{shed}-{timestamp}for CloudTrail traceability - Default duration: 1 hour (configurable)
- Source credentials: Loaded from
~/.aws/credentialsusing the configured profile
Timeouts¶
Credential requests use a 3-second timeout. On timeout, the proxy returns:
{
"error": "credential request timed out",
"message": "shed-host-agent not reachable. Is it running on the host machine?",
"hint": "Ensure shed-host-agent is running on the host machine."
}
Startup Health Check¶
On startup, shed-ext-aws-credentials pings the aws-credentials namespace. If no response arrives within 2 seconds, it logs a warning but continues starting.