Credential sources
Each credential entry in gatekeeper.yaml includes a source block that determines where the credential value comes from.
Source types overview
| Type | Description | Refresh |
|---|---|---|
env | Read from an environment variable | No |
static | Literal inline value | No |
process | Run a host command, use its stdout | Yes (auto-refresh; expiry read from credential_process-format JSON, else configurable ttl) |
aws-secretsmanager | Fetch from AWS Secrets Manager | No |
gcp-secretmanager | Fetch from GCP Secret Manager | No |
gcp-service-account | Mint GCP OAuth2 access token from a service account key | Yes (auto-refresh before expiry) |
github-app | Generate GitHub App installation token | Yes (auto-refresh before expiry) |
token-exchange | RFC 8693 token exchange | Yes (per-request, cached with TTL) |
Sources marked Refresh: Yes have credentials that expire. process, github-app, and gcp-service-account implement background credential refresh — gatekeeper re-fetches at 75% of TTL (minimum 30 seconds) and hot-swaps without downtime. token-exchange uses per-request lazy caching: on cache miss, gatekeeper calls the STS and caches the result for the token’s TTL.
env
Read the credential value from an environment variable at startup.
credentials:
- host: api.github.com
source:
type: env
var: GITHUB_TOKEN
var
Name of the environment variable to read.
- Type:
string - Required: Yes
- Default: —
The variable must be set and non-empty at startup. If unset, gatekeeper exits with an error.
static
Use a literal value defined inline in the config file.
credentials:
- host: api.example.com
header: x-api-key
source:
type: static
value: sk-xxxx
value
The credential value.
- Type:
string - Required: Yes
- Default: —
Note: Avoid committing secrets in config files. Prefer
envor a secret manager source for production deployments.
process
Run a host command and use its stdout as the credential value. Any helper
that prints a credential works: OS keychain CLIs, pass, 1Password’s op,
or an AWS credential_process helper.
credentials:
- host: api.example.com
header: x-api-key
source:
type: process
command: "op read op://vault/example/api-key"
command
Shell command to run with sh -c. Stdout (trimmed) becomes the credential
value.
- Type:
string - Required: Yes
- Default: —
ttl
Refresh interval when the command output carries no expiry information, as
a Go duration (90s, 30m, 12h). Must be positive.
- Type:
string - Required: No
- Default:
5m
Like all refreshing sources, gatekeeper re-fetches at 75% of TTL (floor 30
seconds), so the default re-runs the command every 3m45s. Set a longer
ttl for helpers that are expensive or interactive (biometric prompts,
rate limits) and serve values that rarely change.
Behavior
- Expiry-aware refresh. When stdout is AWS
credential_process-format JSON — recognized by its exact-caseVersion,AccessKeyId, andExpiration(RFC 3339) keys — the credential refreshes on that expiry, andttlis ignored. The JSON is passed through as the credential value; consumers that need the individual fields parse it themselves. Other JSON output is treated as an opaque credential and refreshes onttl. - Expired output is an error. If the reported
Expirationis already in the past, the fetch fails (with the timestamp in the error) instead of installing credentials that would be rejected upstream; the standard retry backoff applies. - Sanitization. Control characters that are invalid in HTTP header values (RFC 7230) are stripped. A warning is logged (count only — the value is never logged) when non-whitespace control bytes were present; trailing newlines and pretty-printed JSON are normal and do not warn.
- Failures. A non-zero exit fails the fetch; stderr is included in the error (truncated) so helper failures such as an expired SSO session are diagnosable. Empty output is an error. Failed refreshes retry with the standard backoff.
Security: The command runs on the host with gatekeeper’s privileges. Only configure commands you trust, from config files you own. Embedding operators (like moat) must not accept this field from repository-controlled config.
aws-secretsmanager
Fetch the credential from AWS Secrets Manager at startup. Uses the AWS SDK default credential chain (AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY, IAM roles, etc.).
credentials:
- host: api.example.com
source:
type: aws-secretsmanager
secret: my-app/api-key
region: us-east-1
secret
AWS Secrets Manager secret ID or ARN.
- Type:
string - Required: Yes
- Default: —
region
AWS region for the Secrets Manager client.
- Type:
string - Required: No
- Default: — (uses
AWS_REGIONorAWS_DEFAULT_REGIONfrom the environment)
gcp-secretmanager
Fetch the credential from GCP Secret Manager at startup. Uses Application Default Credentials (GOOGLE_APPLICATION_CREDENTIALS, metadata server, etc.).
credentials:
- host: api.example.com
source:
type: gcp-secretmanager
project: my-gcp-project
secret: api-key
version: latest
project
GCP project ID containing the secret.
- Type:
string - Required: Yes
- Default: —
secret
Secret name within the project.
- Type:
string - Required: Yes
- Default: —
version
Secret version to access.
- Type:
string - Required: No
- Default:
"latest"
The underlying gRPC connection is closed on gatekeeper shutdown.
gcp-service-account
Mint short-lived GCP OAuth2 access tokens from a service account key (the JSON file format produced by gcloud iam service-accounts keys create). Gatekeeper signs a JWT with the key, exchanges it for an access token at Google’s OAuth2 token endpoint, and refreshes the token automatically in the background before expiry. Clients inside the sandbox never see the service account key — only the proxy holds it.
credentials:
- host: storage.googleapis.com
grant: gcs
source:
type: gcp-service-account
project: my-gcp-project
secret: gcs-uploader-key
scopes: https://www.googleapis.com/auth/devstorage.read_write
The service account key JSON can come from one of three locations: GCP Secret Manager (secret + project), a file (private_key_path), or an environment variable (private_key_env). Exactly one must be set.
secret
GCP Secret Manager secret holding the service account key JSON. Reading the secret uses Application Default Credentials, same as the gcp-secretmanager source. If the token endpoint rejects an assertion (e.g., after the key is rotated and the old key revoked), gatekeeper drops the cached key and re-reads it from Secret Manager on the next refresh, so key rotation is picked up without a restart.
- Type:
string - Required: One of
secret,private_key_path, orprivate_key_envis required - Default: —
project
GCP project ID containing the secret.
- Type:
string - Required: Yes, when
secretis set - Default: —
version
Secret version to access.
- Type:
string - Required: No (only valid with
secret) - Default:
"latest"
private_key_path
File path to the service account key JSON.
- Type:
string - Required: One of
secret,private_key_path, orprivate_key_envis required - Default: —
private_key_env
Name of an environment variable containing the service account key JSON.
- Type:
string - Required: One of
secret,private_key_path, orprivate_key_envis required - Default: —
The environment variable must be set and non-empty at startup.
scopes
Space-separated list of OAuth scopes to request.
- Type:
string - Required: No
- Default:
"https://www.googleapis.com/auth/cloud-platform"
github-app
Generate short-lived GitHub App installation access tokens. Tokens refresh automatically in the background before expiry.
credentials:
- host: api.github.com
grant: github
source:
type: github-app
app_id: "12345"
installation_id: "67890"
private_key_path: /etc/gatekeeper/github-app.pem
app_id
GitHub App ID.
- Type:
string - Required: Yes
- Default: —
installation_id
GitHub App installation ID.
- Type:
string - Required: Yes
- Default: —
private_key_path
File path to the PEM-encoded RSA private key for the GitHub App.
- Type:
string - Required: One of
private_key_pathorprivate_key_envis required - Default: —
Mutually exclusive with private_key_env. Supports both PKCS#1 (RSA PRIVATE KEY) and PKCS#8 (PRIVATE KEY) PEM formats.
private_key_env
Name of an environment variable containing the PEM-encoded RSA private key.
- Type:
string - Required: One of
private_key_pathorprivate_key_envis required - Default: —
Mutually exclusive with private_key_path. The environment variable must be set and non-empty at startup.
When multiple credentials share the same github-app source config (e.g., api.github.com and github.com), gatekeeper deduplicates them into a single token fetch and a single background refresh goroutine.
token-exchange
Exchange a per-request subject token for an access token via RFC 8693 (OAuth 2.0 Token Exchange). Unlike other sources, token-exchange resolves credentials dynamically per request rather than at startup.
credentials:
- host: api.github.com
grant: github
source:
type: token-exchange
endpoint: https://sts.example.com/token
client_id: gatekeeper
client_secret_env: STS_CLIENT_SECRET
subject_header: X-Subject-Token
resource: https://api.github.com
endpoint
STS token endpoint URL.
- Type:
string - Required: Yes
- Default: —
client_id
OAuth client ID for authenticating to the STS via HTTP Basic auth.
- Type:
string - Required: Yes
- Default: —
client_secret
OAuth client secret. Sent as the Basic auth password to the STS endpoint.
- Type:
string - Required: One of
client_secretorclient_secret_envis required - Default: —
Mutually exclusive with client_secret_env.
client_secret_env
Name of an environment variable containing the OAuth client secret.
- Type:
string - Required: One of
client_secretorclient_secret_envis required - Default: —
Mutually exclusive with client_secret. The environment variable must be set and non-empty at startup.
subject_header
HTTP request header containing the subject token. The header is stripped from the request before forwarding.
- Type:
string - Required: One of
subject_headerorsubject_fromis required - Default: —
Mutually exclusive with subject_from.
subject_from
Alternative subject token extraction method.
- Type:
string - Required: One of
subject_headerorsubject_fromis required - Default: —
- Valid values:
"proxy-auth"
When set to "proxy-auth", the subject token is extracted from the username in the Proxy-Authorization Basic auth header.
Mutually exclusive with subject_header.
subject_token_type
OAuth token type URI for the subject token.
- Type:
string - Required: No
- Default:
"urn:ietf:params:oauth:token-type:access_token"
resource
Target resource URI included in the token exchange request.
- Type:
string - Required: No
- Default: —
actor_token_from
Source for the optional RFC 8693 actor token.
- Type:
string - Required: No
- Default: — (no actor token)
- Valid values:
"proxy-auth-password"
When set to "proxy-auth-password", the actor token is extracted from the password in the Proxy-Authorization Basic auth header. Requires subject_from: proxy-auth.
When actor_token_from is configured, gatekeeper sets delegate auth mode — the static auth_token check is skipped and each caller’s identity is validated by the STS instead.
actor_token_type
OAuth token type URI for the actor token.
- Type:
string - Required: No
- Default:
"urn:ietf:params:oauth:token-type:access_token"
Exchanged tokens are cached per subject (and actor, if present) using the TTL from the STS expires_in response field, but the cached TTL is always capped at 1 minute regardless of what the STS advertises — a longer expires_in only means the token may remain valid that long, not that it stays valid, since the upstream credential behind the exchange can be revoked or rotated without gatekeeper’s knowledge. If the STS does not return expires_in (or returns a non-positive value), the same 1-minute cap is used as the TTL. Concurrent requests for the same subject are coalesced into a single STS call via singleflight, so the 1-minute cap does not translate into an STS call per request.
See Credential invalidation below for how gatekeeper evicts a cached token before its TTL expires.
Credential invalidation
When the upstream server rejects a forwarded request with 401 Unauthorized or 403 Forbidden, gatekeeper invalidates every credential that was injected into that request, so the next request re-resolves rather than replaying a credential the destination has already refused. This applies to any credential source that supports invalidation, not just token-exchange. Statuses other than 401/403 are left alone — a 5xx says nothing about the credential’s validity, and a 401/403 also covers cases unrelated to a stale credential (rate limits, missing repo access), not only rotation.
For token-exchange, invalidation evicts the cached token for that subject (and actor) so the next request performs a fresh exchange. Evictions are rate-limited per subject/actor key to a 10-second cooldown: repeated 401/403 responses for the same key within 10 seconds are no-ops after the first eviction. This bounds how often a client that is looping on a failing, non-idempotent request (e.g., a repeated git push) can force STS calls; the trade-off is that a genuinely rotated credential can take up to 10 seconds to be picked up after the first eviction.
Invalidation is evict-only — the request that triggered it is not retried by gatekeeper.