Introduction
A common practice in Google Cloud is to create one or more service accounts to authorize the Google Cloud CLI. Using service accounts is recommended by Google instead of user accounts. However, a service account JSON or P12 file contains sensitive information, the private key, used to authorize requests in Google Cloud.
How do you manage and control access to this sensitive information? A common method is to create a service account and email it to the users. Email may or may not be a secure method of delivering a service account to users. If the service account file is leaked, you must revoke the service account. If multiple users or services depend on this service account, you might have a problem reconfiguring and recovering quickly.
In this article, we will discuss and use service account impersonation. This means that users do not need access to the service account key material. The contents of the service account remain in Google Cloud. Instead of providing users with a service account file, we provide the user authorization to use the service account (impersonation). This reduces the permissions required for that user account. Provided the user is not accessing the Google Cloud Console, the user account only requires permission to access the service account and no other Google Cloud services. The user’s authorization will be controlled by the permissions assigned to the service account. If a user no longer requires access to the service account, all that is required is to remove his identity from the service account resource in Google Cloud IAM. The service account itself is not impacted and remains protected.
I wrote a companion article on how to impersonate a service account in PowerShell:
Benefits
- User accounts have limited permissions. The only permission requires is the ability to create OAuth Access Tokens.
- Improved resource security. The user account is granted permission to a service account instead of all service accounts.
- Improved access management. To stop access to a service account, remove the IAM member from the service account resource.
- Improved access security. User credentials are not used to grant Google Cloud service authorization.
Drawbacks
- Complexity. Permissions are granted to a service account and not directly to the user account. This requires two-step authorization.
- Misunderstanding. Service account impersonation is not well understood in the Google Cloud community. Education may be required.
Summary
The improved security offered by impersonation outweighs the increased complexity of user and service account management. Far too often service accounts are created without strong management in place, which increases security risks. By reducing the permissions granted to a user account, security is improved. By impersonating service accounts, user credentials are not used to grant Google Cloud service authorization.
Service Accounts
A Google Cloud Service Account is an identity (who) that access (permission/role) to resources. The identity of a service account is the same format as an email address.
As an example, the Compute Engine default service account has this format:
1 |
PROJECTNUMBER-compute@developer.gserviceaccount.com |
A service account is also a resource. As a resource, you can grant IAM permissions to other identities to access the service account. For example, you can grant the role of Service Account Admin to a user to manage service accounts. In this article, we will treat the service account as a resource and assign permissions to users to impersonate the service account by granting the ability to create OAuth Access Tokens.
IAM Permissions
The IAM permission required is: iam.serviceAccounts.getAccessToken.
The permission required to create Identity Tokens is: iam.serviceAccounts.getOpenIdToken.
Identity Tokens are used for authentication with cloud services protected by Identity Aware Proxy (IAP) and for service to service authentication, for example Cloud Scheduler calling Cloud Functions.
These permissions are in the role: Service Account Token Creator.
Service Account Token Creator
roles/iam.serviceAccountTokenCreator
This role grants an identity the permission to impersonate a service account (create OAuth2 access tokens, sign blobs or JWTs, etc).
This role has the following permissions:
- iam.serviceAccounts.get
- iam.serviceAccounts.getAccessToken
- iam.serviceAccounts.getOpenIdToken
- iam.serviceAccounts.implicitDelegation
- iam.serviceAccounts.list
- iam.serviceAccounts.signBlob
- iam.serviceAccounts.signJwt
- resourcemanager.projects.get
- resourcemanager.projects.list
Setup
In this phase we will use the CLI with an identity that has the role Project Editor. This role or similar is required so that we have the permissions to manage Google Cloud IAM.
Get the current IAM account
This will list the current account and active configuration.
1 |
gcloud config get-value account |
If the account returned is not the correct account, authenticate with:
1 |
gcloud auth login |
Get the current project
1 |
gcloud config get-value core/project |
If the project returned is not the correct project, change the project with:
1 |
gcloud config set core/project [PROJECT-ID] |
Verify account permissions
Display the permissions that your account has. Replace ACCOUNT with your account email address.
1 2 3 4 |
gcloud projects get-iam-policy [PROJECT-ID] \ --flatten="bindings[].members" \ --format='table(bindings.role)' \ --filter="bindings.members:[ACCOUNT]" |
Verify that the returned roles have Project Owner, Project Editor or required permissions to create and manage IAM members, roles and service accounts.
Enable APIs
This step is very important. For new accounts, you probably do not have the correct APIs enabled. Impersonation requires two APIs:
- iamcredentials.googleapis.com
- cloudresourcemanager.googleapis.com
To list the enabled APIs:
1 |
gcloud services list --enabled |
To enable the required APIs:
1 2 |
gcloud services enable iamcredentials.googleapis.com gcloud services enable cloudresourcemanager.googleapis.com |
Wait a few minutes for the APIs to be enabled.
Add user account to IAM
Add the user account to IAM granting permission to inspect service states and operations, and consume quota and billing for a consumer project.
Replace [ACCOUNT] with the email address used for the user account that looks like username@gmail.com.
1 2 3 |
gcloud projects add-iam-policy-binding [PROECT_ID] \ --member "[ACCOUNT]" \ --role "roles/serviceusage.serviceUsageConsumer" |
Create a service account
Create a new service account for testing. You will need the Project ID (see above), a service account name, and the email address of the user account (G Suite or Google Accounts) to authorize.
The service account name is a simple string, in this example test100.
1 |
gcloud iam service-accounts create test100 |
This command creates a new service account. The new service account email address will be like:
1 |
test100@[PROEJECT_ID].iam.gserviceaccount.com |
Grant permissions to the service account
This step grants permissions to the service account to access Google Cloud resources. In this example, grant the Project Viewer role.
Replace [SA_FULL_EMAIL] with the service account email address that looks like test100@[PROEJECT_ID].iam.gserviceaccount.com.
Replace [ACCOUNT] with the email address used for the user account that looks like username@gmail.com.
1 2 3 |
gcloud projects add-iam-policy-binding [PROECT_ID] \ --member "[SA_FULL_EMAIL]" \ --role "roles/viewer" |
Grant permissions to the user on the service account
This step grants permission on the service account to the user. This step grants permission to the resource and not to the project.
Replace [SA_FULL_EMAIL] with the service account email address that looks like test100@[PROEJECT_ID].iam.gserviceaccount.com.
Replace [ACCOUNT] with the email address used for the user account that looks like username@gmail.com.
1 2 3 |
gcloud iam service-accounts add-iam-policy-binding [SA_FULL_EMAIL] \ --member user:[ACCOUNT] \ --role roles/iam.serviceAccountTokenCreator |
Using impersonation
The CLI supports the command-line option –impersonate-service-account.
1 |
gcloud projects list --impersonate-service-account=[SA_FULL_EMAIL] |
Configure the CLI for impersonation
Specifying the service account on the command line will get old quickly.
This step will configure the CLI to use impersonation by default.
1 |
gcloud config set auth/impersonate_service_account [SA_FULL_EMAIL] |
To reset (clear) using impersonation by default:
1 |
gcloud config unset auth/impersonate_service_account |
GSUTIL
The CLI gsutil also supports impersonation. If service account impersonation is setup by default, gsutil uses that configuration. Gsutil also supports the command-line option -i service-acount to specify the service account to impersonate.
1 |
gsutil -i [SA_FULL_EMAIL] ls |
OAuth Access Token
The CLI supports generating Access Tokens from the impersonated service account.
1 |
gcloud auth print-access-token --impersonate-service-account [SA_FULL_EMAIL] |
OIDC Identity Tokens
The CLI supports generating Identity Tokens from the impersonated service account.
1 |
gcloud auth print-identity-token --impersonate-service-account [SA_FULL_EMAIL] --include-email |
Summary
Notice that we did not download the service account JSON key file. The only IAM permission we granted the user is to consume APIs to impersonate. We granted the user account the necessary permissions to impersonate the service account. The user account has no other permissions in Google Cloud for this project.
The improved security that service account impersonation provides is worth the effort. Once you understand the process, everything is easy as most of the steps only need to be completed once.
More Information
Photography Credits
I write free articles about technology. Recently, I learned about Pexels.com which provides free images. The image in this article is courtesy of Flickr at Pexels.
I design software for enterprise-class systems and data centers. My background is 30+ years in storage (SCSI, FC, iSCSI, disk arrays, imaging) virtualization. 20+ years in identity, security, and forensics.
For the past 14+ years, I have been working in the cloud (AWS, Azure, Google, Alibaba, IBM, Oracle) designing hybrid and multi-cloud software solutions. I am an MVP/GDE with several.
August 13, 2020 at 5:11 PM
Fantastic article John!! Thank you very much. One thing that is frustrating to me is below error:
gsutil -i gcs-admin@youtube-demo-255723.iam.gserviceaccount.com ls
WARNING: This command is using service account impersonation. All API calls will be executed as [gcs-admin@youtube-demo-255723.iam.gserviceaccount.com].
AccessDeniedException: 403 gcs-admin@youtube-demo-255723.iam.gserviceaccount.com does not have storage.buckets.list access to the Google Cloud project.
I gave service account google cloud storage admin, yet I get this error.
August 14, 2020 at 11:26 PM
Which service account did you grant the Storage Admin role to? The IAM account “gcs-admin@…” does not have the roles that you think it has. Double-check in the Google Cloud Console or via the CLI “gcloud projects get-iam-policy”
If you create a question on Stack Overflow, let me know at blog@www.jhanley.com and I will look at the question.
June 10, 2021 at 1:06 PM
Thanks John, I’m attempting to implement this in Go but having problems so I’ve posted a question on Stack Overflow:
https://stackoverflow.com/questions/67927689/getting-error-using-google-cloud-client-libraries-for-go-unknown-credential-typ