Overview of Google Cloud Function Identities
Cloud Functions is a serverless service on Google Cloud Platform (GCP) that provides an execution environment for your code. In order for a Cloud Function to interact with other GCP services and resources it needs an identity. An identity for a Cloud Function is a GCP service account and that service account has a Cloud IAM role bound to it. This Cloud IAM role contains permissions which 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, and ensure your functions can only interact with the services and resources to which they are permitted. In this article we will perform a deep dive into how to assign identities to Cloud Functions, highlight common anti-patterns and provide recommendations around best practices for Cloud Functions authentication and authorization.
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 do not 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: If you are unfamiliar with GCP service accounts, visit the documentation before continuing.
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 (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: ScaleSec recommends avoiding basic Cloud IAM roles (formally known as primitive roles) because they are generically scoped and overly permissive for a majority of workloads in GCP.
Cloud Functions Defaults
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 privilege IAM role. Before updating the IAM role binded to the default service account, be aware that this service account is shared with other services, particularly App Engine.
Recommendation: ScaleSec recommends avoiding default service accounts whenever possible (due to their default
editor IAM role) and instead use a per-function identity. As an additional guardrail, enable the Organization Policy constraint
iam.automaticIamGrantsForDefaultServiceAccounts to disable the automatic assignment of the
Editor IAM role to the default service accounts.
Instead of using the default service account for your Cloud Functions, GCP supports a per-function identity. This is a recommended identity approach 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.
In order to assign a non-default service account to a Cloud Function it needs to be created beforehand. 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: ScaleSec recommends using 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.serviceAccountUser to individual users or groups and instead assign roles to IaC service accounts.
Anti-patterns can be thought of as actions that are counter to best practice. The examples outlined below are common trends that go against security guidance and should be avoided.
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.
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 the permissions to invoke a Cloud Function because they could then execute code with the basic IAM role
Editor. To prevent the automatic assignment of the
Editor IAM role to default service accounts in GCP, enable the
iam.automaticIamGrantsForDefaultServiceAccounts constraint where appropriate.
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.
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, keeps the code complexity in check, and makes 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 are also able to better follow the concept of separation of duties and limit the amount of roles a Cloud Function identity has assigned to it.
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.
Cloud Functions are a great tool whether you’re building a serverless application, automating security remediations, or filling in functionality gaps like email alerts. Unfortunately the ease of getting started can occasionally come with some negatives such as overly permissive default service accounts, but with the information in this article, you can properly scope your cloud functions to limit security risks.
The information presented in this article is accurate as of 12/10/2020. Follow the ScaleSec blog for new articles and updates.
ScaleSec is a service-disabled, veteran-owned small business (SDVOSB) for cloud security and compliance that helps innovators meet the requirements of their most scrutinizing customers. We specialize in cloud security engineering and cloud compliance. Our team of experts guides customers through complex cloud security challenges, from foundations to implementation, audit preparation and beyond.