This article describes how you can acquire a google-issued OpenID Connect (OIDC
) token within a Cloud Build step and securely invoke Cloud Run, Cloud Functions or any general system that accepts an id_token
.
A clear usecase for this is to have a cloud build step invoke an external secure system as part of the workflow. If the secure service is running in Cloud Run or Cloud Function or behind IAP, the Cloud Build will need some way to acquire a id_token
and specify that within the API call.
This article will not cover the id_tokens
in any detail and will just describe how to get an use an id_token
from within cloud build.
For information on id_token
see Authenticating using Google OpenID Connect Tokens.
For information on security configuration for Cloud Run, IAP and Cloud Function, see
As background, Cloud Build uses a default service account as its identity for authenticating to GCP APIs. The way that cloud build acquires its access_token
is through its local metadata sever similar to Compute Engine. Critically, while that metadata server can return an access_token
, it does NOT provide the /identity
endpoint that returns an id_token
.
Since Cloud Run, Cloud Function applications you deploy are secured by id_tokens
, this prevents easy authentication.
One workaround is to furbish Cloud Build with a service account credentials file as described here. While that works, thats also a problem since now you have just downloaded a key (don’t download keys).
Another approach is to use the default account’s access_token
and use that to make a new API call to projects.serviceAccounts.generateIdToken. Basically enable ‘self-impersonation’ and use that API by the default service account.
This approach sounds feasible if it weren’t for a nit: the default service account is managed by google’s infrastructure and is not “assignable” to the required IAM role that enables self impersonation (eg, roles/iam.serviceAccountTokenCreator
)
However, Cloud build support user-specified service accounts which you can control and can assign the role.
…and thats just what we’re going to do here
The iam.serviceAccountTokenCreator
role includes concentric set of permission (see Concentric IAMCredentials Permissions: The secret life of signBlob. If you just need to generate an access_token
or an id_token
, consider generating a custom role with that specific capability.
First acquire some environment variables and enable the required APIs
export GCLOUD_USER=`gcloud config get-value core/account`
export PROJECT_ID=`gcloud config get-value core/project`
export PROJECT_NUMBER=`gcloud projects describe $PROJECT_ID --format='value(projectNumber)'`
gcloud services enable cloudbuild.googleapis.com iam.googleapis.com
Create a service account that Cloud Build will run as and then assign the IAM permissions to run Cloud Build steps, write logs and most importantly, enable ‘self-impersonation’
gcloud iam service-accounts create generic-server
gcloud iam service-accounts add-iam-policy-binding generic-server@$PROJECT_ID.iam.gserviceaccount.com \
--role roles/iam.serviceAccountTokenCreator \
--member "serviceAccount:generic-server@$PROJECT_ID.iam.gserviceaccount.com"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:generic-server@$PROJECT_ID.iam.gserviceaccount.com \
--role=roles/logging.logWriter
gcloud iam service-accounts add-iam-policy-binding generic-server@$PROJECT_ID.iam.gserviceaccount.com \
--role roles/iam.serviceAccountUser \
--member "user:$GCLOUD_USER"
gsutil iam ch serviceAccount:generic-server@$PROJECT_ID.iam.gserviceaccount.com:objectAdmin gs://$PROJECT_ID\_cloudbuild
Once thats done, use gcloud
to impersonate and get an id_token
(you can do this in any number of ways programmatically but we’re just using gcloud for simplicity)
steps:
- name: gcr.io/cloud-builders/gcloud
id: get_id_token
entrypoint: /bin/bash
args: ['-c', 'gcloud auth print-identity-token --include-email --audiences=https://foo.com --impersonate-service-account=generic-server@$PROJECT_ID.iam.gserviceaccount.com >/workspace/id_token.txt']
- name: launcher.gcr.io/google/ubuntu1604
id: print_id_token
entrypoint: bash
args:
- -c
- |
curl -s -H "Authorization: Bearer $(cat /workspace/id_token.txt)" https://httpbin.org/get
serviceAccount: 'projects/$PROJECT_ID/serviceAccounts/generic-server@$PROJECT_ID.iam.gserviceaccount.com'
options:
logging: CLOUD_LOGGING_ONLY
Note that we’ve specified the serviceAccount
as part of the build and wrote the output token to a temp file to reuse between steps
Then invoke and view results
gcloud builds submit
The JWT that is generated will be in the expected form
{
"alg": "RS256",
"kid": "f4bb220cd094b0ae90dd73e10c10e7db54b89280",
"typ": "JWT"
}
{
"aud": "https://foo.com",
"azp": "108446643221242807280",
"email": "generic-server@cr-cb-341501.iam.gserviceaccount.com",
"email_verified": true,
"exp": 1645014638,
"iat": 1645011038,
"iss": "https://accounts.google.com",
"sub": "108446643221242807280"
}
and the output of a given run will invoke httpbin in this case but you can substitute IAP,Cloud Run or Cloud Functions there
This site supports webmentions. Send me a mention via this form.