GCP Service Accounts
The gcp_auth transform mints Google Cloud access tokens from a service-account keyfile and injects them as Authorization: Bearer headers on matching requests.
- name: gcp_auth
config:
keyfile:
type: aws_sm
secret_id: "arn:aws:secretsmanager:us-east-1:123456789:secret:gcp-sa-key"
scopes:
- "https://www.googleapis.com/auth/cloud-platform"
rules:
- host: "*.googleapis.com"iron-proxy holds the keyfile, signs the JWT assertion, exchanges it at Google's token endpoint, and attaches the bearer to outbound calls. The workload never sees the keyfile. Requires MITM mode.
Under the hood, gcp_auth is an RFC 7523 JWT-bearer flow with Google's keyfile format and metadata-server stubbing. For non-Google JWT-bearer flows (DocuSign, Salesforce, Box, Zoom Server-to-Server), use oauth_token with grant: jwt_bearer instead.
How It Works
- Credential resolution. The service-account JSON loads either from disk (
keyfile_path), from any secret source (keyfile), or from the Google Cloud default credential chain (credentials_provider). Set exactly one. See Workload Identity for the metadata-server path. - Token minting. iron-proxy signs a JWT with the service account's RSA private key, posts it to Google's OAuth2 token endpoint as a
urn:ietf:params:oauth:grant-type:jwt-bearerassertion, and receives a short-lived access token in exchange. Minting goes throughgolang.org/x/oauth2/google, the same code path the official Google Cloud SDKs use. - Caching and refresh. Tokens are cached in memory until they near expiry, then refreshed in the background. Single-flight deduplication ensures concurrent requests share one mint.
- Header injection. When a request matches the entry's
rules, iron-proxy setsAuthorizationto the cached or freshly minted bearer. - Endpoint stubbing. iron-proxy intercepts two kinds of token requests and answers them with a synthetic
iron-proxy-stub-tokenresponse: requests to Google's OAuth2 token endpoint (oauth2.googleapis.com/token) and requests to the GCE/GKE metadata server's service-account token paths (metadata.google.internaland169.254.169.254under/computeMetadata/v1/instance/service-accounts/*/token). This lets Google Cloud SDKs run their normal Application Default Credentials dance without a real keyfile and without actually being on GCP. iron-proxy mints the real token separately and swaps it onto the upstream API call. - Failure handling. On any error during keyfile load, JWT signing, or token exchange, the request is rejected with HTTP 403 and the audit log carries
rejected: token_unavailable.
Scopes
OAuth2 scopes are baked into the minted token and must be declared up front. List every scope the workload's calls will need:
scopes:
- "https://www.googleapis.com/auth/cloud-platform"
- "https://www.googleapis.com/auth/bigquery.readonly"cloud-platform is the broadest scope and covers most GCP APIs. Narrower scopes (bigquery.readonly, devstorage.read_only, and so on) limit what a captured token could do.
One gcp_auth transform can serve multiple GCP APIs. Widen its scopes and rules to cover them. Use a second entry only when you want different service accounts or different scope sets for different hosts.
Domain-Wide Delegation
Set subject to impersonate a Google Workspace user. The minted token then acts as that user rather than as the service account. This is required for service-account access to user data in Gmail, Drive, Calendar, and the Admin SDK.
- name: gcp_auth
config:
keyfile:
type: aws_sm
secret_id: "arn:aws:secretsmanager:us-east-1:123456789:secret:gcp-sa-key"
subject: "automation@example.com"
scopes:
- "https://www.googleapis.com/auth/admin.directory.user.readonly"
rules:
- host: "admin.googleapis.com"The service account must be authorized in the Workspace Admin Console for the scopes it impersonates, and subject must be the email of a real Workspace user. Without delegation set up on the Google side, the token mint succeeds but every API call returns 403.
subject is incompatible with credentials_provider: metadata-server credentials can't perform the JWT-bearer assertion that domain-wide delegation requires. Setting both is a configuration error and the proxy refuses to start.
Workload Identity
On GKE, Cloud Run, or any environment with Application Default Credentials configured, you don't have to give iron-proxy a service-account JSON file. Set credentials_provider and the proxy resolves credentials through Google's default chain: GKE Workload Identity, GOOGLE_APPLICATION_CREDENTIALS, and Workload Identity Federation (for federated identities from AWS, Azure, or any OIDC provider).
- name: gcp_auth
config:
credentials_provider:
type: workload_identity
scopes:
- "https://www.googleapis.com/auth/cloud-platform"
rules:
- host: "*.googleapis.com"The proxy holds the rotating pod credentials. The workload talks to the proxy with the same iron-proxy-stub-token it would receive via metadata server stubbing, so real credentials never reach the agent even though the underlying identity is short-lived and federated.
credentials_provider is mutually exclusive with keyfile and keyfile_path. Set exactly one.
Examples
Keyfile From Disk
The simplest setup. Mount the service-account JSON file into the proxy container.
- name: gcp_auth
config:
keyfile_path: "/etc/iron-proxy/gcp-sa.json"
scopes:
- "https://www.googleapis.com/auth/cloud-platform"
rules:
- host: "*.googleapis.com"Keyfile From AWS Secrets Manager
Recommended for production. Keeps the keyfile out of disk images and CI logs.
- name: gcp_auth
config:
keyfile:
type: aws_sm
secret_id: "arn:aws:secretsmanager:us-east-1:123456789:secret:gcp-sa-key"
region: "us-east-1"
ttl: 1h
scopes:
- "https://www.googleapis.com/auth/cloud-platform"
rules:
- host: "*.googleapis.com"The ttl lets you rotate the keyfile in AWS without restarting the proxy. The same pattern works with aws_ssm, 1password_connect, or any other secret source. The keyfile JSON only has to land somewhere the proxy can read.
Multiple Service Accounts
One iron-proxy can serve several GCP service accounts. Add multiple gcp_auth entries with non-overlapping rules:
transforms:
- name: gcp_auth
config:
keyfile: {type: env, var: GCP_SA_BIGQUERY_JSON}
scopes: ["https://www.googleapis.com/auth/bigquery"]
rules:
- host: "bigquery.googleapis.com"
- name: gcp_auth
config:
keyfile: {type: env, var: GCP_SA_STORAGE_JSON}
scopes: ["https://www.googleapis.com/auth/devstorage.read_only"]
rules:
- host: "storage.googleapis.com"The first entry with matching rules wins, so order entries from most specific to least specific.
Metadata Server Stubbing
Google Cloud SDKs running outside GCP often probe the metadata server (169.254.169.254 or metadata.google.internal) to discover that they're on a GCE or GKE instance and fetch credentials from it. iron-proxy intercepts the service-account token endpoints on the metadata server and returns the stub token described above. SDKs that use Application Default Credentials work transparently:
# Inside the workload. No GCP credentials configured locally.
from google.cloud import bigquery
client = bigquery.Client()
# Library probes the metadata server, gets the stub token, makes a BigQuery call
# with Authorization: Bearer iron-proxy-stub-token. iron-proxy swaps the stub
# for a real token before the request leaves the proxy.This is the same stubbing pattern oauth_token uses for arbitrary OAuth2 token endpoints, specialized for Google's metadata service. Stubbing runs before rule evaluation, so SDKs can complete credential discovery even when your rules only target the API hosts.
Audit Log
A successful injection produces these annotations on the gcp_auth trace entry:
service_account: theclient_emailfrom the keyfile.injected:["header:Authorization"].
A stubbed metadata or token endpoint request is annotated stubbed: oauth2_token_endpoint and rendered as the stub action.
A mint failure annotates error and rejected: token_unavailable, and the request is rejected with HTTP 403.
Limitations
- Service-account keyfiles and ADC only. Static credential configs must be service-account JSON (
JWTConfigFromJSON). For Workload Identity Federation and otherexternal_accountconfigurations, usecredentials_provider, which resolves through the Google Cloud default chain. - Domain-wide delegation requires a keyfile.
subjectis incompatible withcredentials_provider. Metadata-server credentials can't perform the JWT-bearer assertion DWD needs. - MITM mode only.
sni-onlycan't rewrite headers. - One service account per entry. Use multiple
gcp_authentries with non-overlappingrulesto serve more than one.
Related
- OAuth2 Token Injection: the same JWT-bearer machinery for non-Google vendors (DocuSign, Salesforce, Box, Zoom).
- Static Secrets: supply the keyfile from AWS Secrets Manager, 1Password Connect, or any other backend.
- Configuration reference: the canonical schema for the
gcp_authtransform.