Google Cloud Functions is a great tool for building serverless applications, automating security remediations, or filling in functionality gaps like email alerts. In order to connect a Cloud Function to other Google Cloud Platform (GCP) services and resources, it needs an identity.
In this article, we will perform a deep dive into the following topics:
- What Are Google Cloud Functions and Identities?
- How to Assign Google Cloud Function Identities
- Common Anti-Patterns
This article focuses mainly on the Cloud Functions service, but many of the same principles apply to other GCP offerings, such as Cloud Run. Additionally, there are legacy GCP services that need to follow the new standard of requiring the
iam.serviceAccounts.actAs (detailed below) permission to assign service accounts to resources. You can read more about those outliers here.
Note: A GCP service account is a type of Google account designed to represent a non-human user that needs to be authenticated and authorized in order to access data in Google APIs. If you are unfamiliar with GCP service accounts, please visit the documentation before continuing.
Overview of Google Cloud Functions and Identities
What Is Google Cloud Functions?
Google Cloud Functions is a serverless service on Google Cloud Platform (GCP) that provides an execution environment for your code. With Cloud Functions, you can write code for simple functions and attach them to events that occur within your cloud infrastructure. Once an event occurs, Cloud Functions will trigger a particular response you set. This can all be done without having to provision underlying cloud infrastructure like servers and storage.
For example, developers could use Cloud Functions to offload some resource-heavy work that wouldn’t be sensible to run on a user’s device, such as image or filing processing.
But in order for a Cloud Function to interact with other GCP services and resources, it needs an identity.
What Are Google Cloud Function Identities?
A Google Cloud Function Identity is a GCP service account that has a Cloud Identity and Access Management (IAM) role(s) bound to it. You can grant multiple roles to an identity if necessary. These Cloud IAM roles contain permissions that authorize what the Cloud Function is permitted to do.
By properly scoping the permissions assigned to your service accounts (and therefore your individual Cloud Functions), you can:
- Reduce the blast radius in the event of a credentials compromise
- Adhere to the principle of least privilege
- Ensure your functions can only interact with the services and resources to which they are permitted
How to Assign Google Cloud Function Identities
In order to assign an identity to a Cloud Function, you will need the
iam.serviceAccounts.actAs permission on the service account you would like to assign to the Cloud Function. This is a requirement whether you are assigning the default service account — which is a security risk and should be avoided (more on that below) or a custom service account. If you attempt to assign a service account to a Cloud Function without that permission, you will see the following error:
User does not have the iam.serviceAccounts.actAs permission on <PROJECT_ID>@appspot.gserviceaccount.com required to create function.
This permission can currently be found in 16 Cloud IAM roles but is most commonly associated with the basic roles
Editor, and the role
Recommendation: Avoid basic Cloud IAM roles (formally known as primitive roles) because they are generically scoped and overly permissive for a majority of workloads in GCP.
Don’t: Use Google Cloud Functions’ Default Service Accounts
Unless updated, the Cloud Functions service uses the default service account
<PROJECT_ID>@appspot.gserviceaccount.com. This service account is shared by every Cloud Function in the project and has the IAM basic role
Editor assigned to it. This basic role is vastly overly permissive and should be avoided. The default service account is considered a user-managed service account, so you do have the ability to remove the
Editor role and assign a least privileged IAM role. Before updating the IAM role bound to the default service account, be aware that this service account is shared with other services, particularly App Engine.
Recommendation: Avoid default service accounts whenever possible (due to their default
EditorIAM role) and instead using a per-function identity. As a guardrail, enable the Organization Policy constraint
iam.automaticIamGrantsForDefaultServiceAccountsto disable the automatic assignment of the
EditorIAM role to the default service accounts.
Do: Choose Per-Function Identities
Instead of using the default service account for your Cloud Functions, GCP supports a per-function identity. This is a best practice for Cloud Functions because it allows you to assign a unique service account (and unique role) to each function in a given project. When a user creates a service account, they are automatically given the required permission
iam.serviceAccounts.actAs on that specific service account and can therefore assign it to a Cloud Function.
It’s important to note that in order to assign a non-default service account to a Cloud Function, that service account must first be created. In the cloud console, once the service account is created and the proper permissions are assigned, click the VARIABLES, NETWORKING AND ADVANCED SETTINGS option in the Cloud Functions Create UI. Next, select the target service account that was previously created.
When using the gcloud CLI, specify the
–service-account flag and pass in the email addresses of the target service account.
gcloud functions deploy **FUNCTION_NAME** --service-account **SERVICE_ACCOUNT_EMAIL**
Recommendation: Use Infrastructure as Code (IaC) with a CI/CD pipeline to create Cloud Functions and service accounts. By using IaC, you can avoid assigning sensitive roles like
iam.serviceAccountUserto individual users or groups and instead assign roles to IaC service accounts.
5 Common Anti-Patterns to Google Cloud Function Identities
Anti-patterns can be thought of as actions that are counter to Google Cloud Functions best practices. The examples outlined below are common trends that go against security guidance and should be avoided.
1. Embedding Service Account Keys in Code
Cloud Functions do not need service account keys to interact with other GCP services and resources. Avoid hardcoding credentials in your Cloud Functions to reduce the likelihood of a credentials compromise and prevent sensitive values from being checked into source code repositories.
2. Using the Default Service Account
A lot of cloud users assign the default service account to Cloud Functions because it is quick and easy. This anti-pattern can be a privilege escalation path in the event that a user has permission to invoke a Cloud Function because they could then execute code with the basic IAM role
To prevent the automatic assignment of the
Editor IAM role to default service accounts in GCP, enable the
iam.automaticIamGrantsForDefaultServiceAccounts constraint where appropriate.
3. Assigning the IAM role
iam.serviceAccountUser on the Project Level
When a user receives an error stating they need the
iam.serviceAccounts.actAs permission to create a Cloud Function, they will commonly google that permission to find more information. One of the first results recommends the assignment of the IAM role
iam.serviceAccountUser on the project level.
Following this guidance would allow the target user to assume any service account in the project, which could expose sensitive service accounts to non-privileged IAM users. If the service account already exists, the role can be assigned to only that service account which would allow the assignment of the service account to the Cloud Function.
4. Monolithic Functions with Many IAM Roles
Cloud Functions should be written with specific business logic in mind to limit the amount of code being handled at once by the function. Having small, purpose-driven functions can decrease execution time, keep code complexity in check, and make updating the function easier.
A negative side effect of large monolithic functions is the need to apply many different IAM roles to account for the different API calls or permissions needed. By breaking up your large functions, you can better follow the concept of separation of duties and limit the number of roles a Cloud Function identity has assigned to it.
5. Failure to Revisit and Review Assigned Permissions
A common trend when first deploying a Cloud Function is to assign IAM roles that are overly permissive with the intent to review and de-provision unneeded permissions at a later time. The challenge with this approach is that organizations rarely revisit and review the assigned permissions.
Normally this is a complex and lengthy process, but GCP makes it simple with the IAM Recommender. This service looks at project-level role assignments and analyzes which permissions have been used and which can be removed. Organizations can leverage this service to offload the undifferentiated heavy lifting of analyzing unneeded IAM permissions or roles.
Need Help Scoping Cloud Functions and Identities? Let’s Talk
Cloud Functions and Identities are great tools for running and organizing code in the cloud without a server or container. However, the ease of getting started with Cloud Functions and Identities can come with some negatives, like overly permissive default service accounts. Fortunately, with the information in this article, you should be able to properly scope your cloud functions to limit security risks.
If you’re looking for more support with Google Cloud Functions and Identities, don’t hesitate to reach out. As a Google Cloud Premier Partner, we’re happy to help you leverage cloud services.