Samples which demonstrate getting and using impersonated credentials for Google Cloud Service Accounts.
Use the impersonated token to access GCP Services and IdTokens for Cloud Run, Cloud Functions, Endpoints
id_token
See Creating Short-Lived Service Account Credentials
Note, if you use Workload Identity Federation, a type of first level impersonation is already done for you which means you can use WIF to directly access GCP resources. You can use the samples to chain impersonated credentials with WIF as well.
However, if you need to use the WIF identity to get an id_token
, you still need to manually acquire that using one of the samples below
you can find the sourcecode here:
Also see:
# Create source identity:
export PROJECT_ID=$(gcloud config list --format="value(core.project)")
gcloud iam service-accounts create source-serviceaccount --display-name="Source Identity"
gcloud iam service-accounts keys create svc-src.json \
--iam-account=source-serviceaccount@$PROJECT_ID.iam.gserviceaccount.com
# Create target identity
gcloud iam service-accounts create target-serviceaccount --display-name="Target Identity"
# Allow source to impersonate target
gcloud iam service-accounts add-iam-policy-binding \
target-serviceaccount@$PROJECT_ID.iam.gserviceaccount.com \
--member=serviceAccount:source-serviceaccount@$PROJECT_ID.iam.gserviceaccount.com \
--role='roles/iam.serviceAccountTokenCreator'
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.
For this repo we will primarily use the Google Cloud Storage set of libraries (except in the node example set where we have to use Secret Manager as well)
# Add resource ACL to target resource
## resource here is a gcs bucket
gsutil mb gs://$PROJECT_ID-test
echo -n bar > foo.txt
gsutil cp foo.txt gs://$PROJECT_ID-test/
rm foo.txt
gsutil iam ch serviceAccount:"target-serviceaccount@$PROJECT_ID.iam.gserviceaccount.com":objectViewer gs://$PROJECT_ID-test
## also secret manager (use for nodejs sample)
printf "s3cr3t" | gcloud secrets create my-secret --data-file=- --replication-policy=user-managed --locations=us-central1,us-east1
export SECRET_NAME=`gcloud secrets describe my-secret --format="value(name)"`
echo $SECRET_NAME
gcloud secrets add-iam-policy-binding my-secret \
--member=serviceAccount:target-serviceaccount@$PROJECT_ID.iam.gserviceaccount.com \
--role=roles/secretmanager.secretAccessor
# activate the srouce account
gcloud auth activate-service-account --key-file=`pwd`/svc-src.json
### try source account (fail)
gcloud alpha storage ls gs://$PROJECT_ID-test
### try impersonated account (pass)
gcloud alpha storage ls gs://$PROJECT_ID-test --impersonate-service-account=target-serviceaccount@$PROJECT_ID.iam.gserviceaccount.com
# activate the 'soruce'
gcloud auth activate-service-account --key-file=`pwd`/svc-src.json
# try with your service account
gsutil stat gs://$PROJECT_ID-test/foo.txt
# now use impersonation
gsutil -i target-serviceaccount@$PROJECT_ID.iam.gserviceaccount.com stat gs://$PROJECT_ID-test/foo.txt
(not supported)
though you can use limited bq functions through gcloud right now and then use gcloud impersonation
gcloud alpha bq jobs list
Tested with ADC variants:
export GOOGLE_APPLICATION_CREDENTIALS=`pwd`/svc-src.json
{
"type": "service_account",
"project_id": "$PROJECT_ID",
"private_key_id": "da386aab2a4f793d14dc8c809c8f8180d86dfcb9",
"private_key": "-----BEGIN PRIVATE KEY-----\nM",
"client_email": "source-serviceaccount@$PROJECT_ID.iam.gserviceaccount.com",
"client_id": "100973668658833123774",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/source-serviceaccount%40$PROJECT_ID.iam.gserviceaccount.com"
}
golang
: pass
java
: pass
python
: pass
node
: pass
dotnet
: fail
Workload Identity Federation usually uses impersonation so including impersonated credentials here does this twice (and usually unnecessarily).
If what you just need is a plain id_token
for use with Cloud Run, Cloud Functions, etc, see Using Federated Tokens to sign signJWT or get gcp idtokens
curl -s -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email
123456-compute@developer.gserviceaccount.com
in this case, 123456-compute@developer.gserviceaccount.com
is allowed to impersonate target-serviceaccount@$PROJECT_ID.iam.gserviceaccount.com
gcloud iam service-accounts add-iam-policy-binding \
target-serviceaccount@$PROJECT_ID.iam.gserviceaccount.com \
--member=serviceAccount:1071284184436-compute@developer.gserviceaccount.com \
--role='roles/iam.serviceAccountTokenCreator'
golang
: pass
java
: pass
python
: pass
node
: pass
dotnet
: fail
$ dotnet run
ERROR: Only ServiceAccountCredential and UserCredential support impersonation.
see:
Requires atleast google-auth-library-nodejs@v8.3.0
Note that the API surface for iamcredentials.*
describes a lifetime
variable. What that does is pretty self-explanatory in that it just limits the lifetime of the
derived access_token
. By default its one hour 3600s
but will automatically refresh even if the lifetime is set. Effectively, the lifetime is there for the transient token
since its auto-refreshed on expiration.
Another feature with iamcredentials.*
api suite is the delegates
parameter. What that signifies is the list of service accounts that must already have authorization to mint tokens on
behalf of the successive account. The chained list of delegates required to grant the final access_token. If set, the sequence of identities must have Service Account Token Creator
capability granted to the prceeding identity. For example, if set to [serviceAccountB, serviceAccountC]
, the source_credential
must have the Token Creator role on serviceAccountB
. serviceAccountB
must have the Token Creator on serviceAccountC
. Finally, C
must have Token Creator on target_principal
. If left unset, source_credential must have that role on target_principal.
For more information on this, see:
The examples above chain credentials for other credentials or id_tokens
:
source->impersonated->gcp_service
source->impersonated->id_token->cloud_run
The more rare way to chain credentials is to use a Downscoped Credential
in this, the flow is
source->impersonated->downscoped_token->gcp_service(gcs)
its not that common to do this type of chaining so i’ve left it out of this repo. see example in go for impersonate+downscope
The following shows the audit logs that would get generated as a result of the Impersonated and Resource (GCS) access above:
Note that the logs span two different resource.types in Cloud logging (IAM and the resource in context). That means to see them together, just run a query with a filter like:
resource.type="service_account" OR resource.type="gcs_bucket"
Here is a detailed logging payload for the IAM request:
And the actual resource request.
Notice that the api request contains the ORIGINAL user that is impersonating the service account
"authenticationInfo": {
"principalEmail": "target-serviceaccount@fabled-ray-104117.iam.gserviceaccount.com",
"serviceAccountDelegationInfo": [
{
"firstPartyPrincipal": {
"principalEmail": "admin@esodemoapp2.com"
}
}
]
},
Note: if chained delegation is used (i.,e
user->sa1->sa2->resource
), you will see multiplefirstPartyPrincipal
values. The firstfirstPartyPrincipal
will be the most recent service account used in the chain. In other words, the order here matters:
"authenticationInfo": {
"principalEmail": "sa2@project.iam.gserviceaccount.com",
"serviceAccountDelegationInfo": [
{
"firstPartyPrincipal": {
"principalEmail": "sa1@project.iam.gserviceaccount.com"
},
"firstPartyPrincipal": {
"principalEmail": "user"
}
}
}
]
},
For more information, see Audit logs for service accounts
This site supports webmentions. Send me a mention via this form.