GCP – How to create public Cloud Run services when Domain Restricted Sharing is enforced
In this post, I’ll show you how to allow unauthenticated requests on a Cloud Run service and use the allUsers identity, even if your organization has enabled Domain Restricted Sharing – as many of our larger and security-conscious customers do.
Domain Restricted Sharing is a security feature that prevents you from unintentionally granting permissions on your Google Cloud projects to external users. As an example, you probably don’t want to grant an external @gmail.com account permissions to create (or delete) a VM instance. Domain Restricted Sharing ensures that only users from a trusted domain can be granted permissions.
By default, you can’t allow unauthenticated requests to a Cloud Run service if you’ve also enabled Domain Restricted Sharing. Any attempt to do so results in an error: IAM policy update failed. There are several ways to resolve this error. In this post, I’ll showcase a solution that works well for larger organizations that have a dedicated platform team that maintains infrastructure.
Understanding the two layers of Cloud Run access management
Before digging into the solution to that error, let’s understand why this error happens in the first place, starting with access control on Cloud Run. There are two layers of access control in Cloud Run: ingress settings and Identity and Access Management (IAM) policies.
Ingress settings
Ingress settings let you filter requests based on their network origin. One example is to allow only requests from sources internal to the Google Cloud project. Alternatively, you can allow all requests passing through a Google Cloud Load Balancer and from internal sources, or even from anywhere, including the Internet, to reach a Cloud Run HTTPS endpoint.
IAM policy
IAM policies let you filter requests based on the identity of the sender. In general an IAM policy binds roles to an identity, such as a user or a service account. Roles hold a set permissions. An identity is only able to reach a Cloud Run HTTPS endpoint, if it has the run.invoker role for that Cloud Run service. You can allow all (unauthenticated) requests by creating an IAM policy that grants allUsers the run.invoker role to a service.
Understanding Domain Restricted Sharing (DRS)
Many organizations use Domain Restricted Sharing (DRS) to secure their cloud environments by limiting resource sharing based on domain. In more concrete terms, DRS is an organization policy that allows organizations to restrict the set of identities that can be used in an IAM policy.
If an organization with the Google Workspace domain example.org enables DRS, alice@example.org can have the role Project Editor on one of their Google Cloud projects, while identities from other Google Workspace domains (including bob@mystore.org or an @gmail.com account) can’t.
DRS policies also affect the allUsers identities. It can not be used in any IAM policy if the DRS policy is enabled. This is also true for Cloud Run IAM policies, when granting the run.invoker role and explains the IAM policy update failed error.
Many services need to accept unauthenticated requests
Enabling a DRS policy is a best practice and prevents you from granting unwanted permissions to external users. However, a DRS policy also blocks a few valid scenarios that require the allUsers identity, including these examples:
A public website, such as for example a store locator site hosted on Cloud Run.
A public Cloud Run service, where authentication and authorization is handled by the service.
An internal Cloud Run service exposed via internal ingress only, which should receive requests only from internal sources.
Following the layers of access control in Cloud Run, as described above, DRS affects only the IAM layer, whereas the ingress settings are independent of it.
How to allow unauthenticated requests when DRS is enforced
In order to still allow specific Cloud Run services being invokeable by allUsers when a DRS policy is in place, platform administrators could disable the DRS policy, set an IAM policy including allUsers and finally enable the DRS policy again. (Refer to the documentation to learn how.) This procedure works because organization policies are not evaluated and applied retroactively. A policy only blocks updates if it is enabled. Manually turning DRS off and on for every change does not scale to large organizations and disables a security mechanism while deploying.
As a more explicit alternative, conditional organization policy can be used together withResource Manager tags. Such that a conditional policy does not affect a resource if the resource has a specific tag attached. In this way, platform administrators can tag specific Cloud Run services to be excluded from DRS. The invoker role can be assigned to allUsers for them, while still having an organization-wide DRS setting constraint.
If the thought of allowing public access from the internet to a Cloud Run service makes you feel uncomfortable, it’s good to know you can add further restrictions. As an example, you might want to only allow unauthenticated requests if the ingress setting is set to accept only internal requests. I will show you later in this article how to achieve that.
A real world example
Let me tell you about one of our customers, who faced the IAM policy update failed error and solved it. That organization, let’s call it example.org, has a platform team and a project team. Here’s what their tasks are:
Platform team
A platform team administers the entire Google Cloud Organization and setup. They are responsible for the overall security of the platform and have enabled a Domain Restriction Sharing policy on the Google Cloud organization level to only allow identities of their Google Workspace domain (example.org) to be granted permissions.
Project team
A project team is working on an open, but internal website hosted on Cloud Run. Such a service needs the ingress setting set to internal and an IAM policy with allUser to allow all incoming, internal traffic. They are prohibited from using allUsers by the Domain Restriction Sharing policy and get the IAM policy update failed error.
Using Resource Manager tags and a conditional DRS policy
In order to let the project team create an IAM policy granting the run.invoker role to allUsers for Cloud Run services, the platform team can set up a conditional DRS policy based on resource tags, as shown here:
First, the platform team creates a tag key and a tag value on the Google Cloud organization level, with the following command. They use allUsersIngress as the tag key and True as a tag value. The required organization resource ID (ORGANIZATION_ID) is a unique identifier for an organization resource.
The platform team is responsible for managing organization-wide DRS policies and their exceptions, therefore only they should be able to manage and attach this tag value to any resources. The Google Cloud Console can be used to manage the access to tags and their values, so that project team members aren’t able to apply the tag themselves, they rather need to request the tag from the platform team.
The platform team then creates a conditional DRS policy to only apply to resources without the allUsersIngress tag’s value matching ‘True’. The Google Workspace customer ID (also called DIRECTORY_CUSTOMER_ID) is required for this command because it allows identities from their own domain. Multiples Google Workspace customer ID can be used if necessary.
To set the conditional DRS policy, first create a policy file named drs-policy.yaml with the following content:
Then execute the following gcloud command to set the policy, referencing drs-policy.yaml.
The platform team can then attach the tag ORGANIZATION_ID/allUsersIngress with the value True to a specific Cloud Run service with the following gcloud command.
As of the conditional organization policy, the project team is now able to grant the run.invoker permission to the allUsers identity for the tagged Cloud Run service.
Using a conditional Cloud Run ingress policy for additional constraints
The platform team of the customer wanted to ensure that no Cloud Run service is ever publicly accessible. A public service has ingress set to all, and an IAM policy with allUser. We solved that by adding another conditional organization policy. Just like for DRS there is also an organization policy for Cloud Run ingress settings.
The run.allowedIngress organization policy defines the allowed ingress settings for Cloud Run services. When this constraint is enforced, services will be required to have ingress settings that match one of the allowed values. By making the organization policy run.allowedIngress conditional on the same tag, you can simultaneously restrict the ingress settings to stay internal only and allow IAM policies with allUser.
To force the project team to have the specific tagged Cloud Run service set and stay to an internal ingress setting, create a policy file named allowedIngress-policy.yaml with the following content:
Then execute the following gcloud command to set the policy, referencing allowedIngress-policy.yaml.
Organization policies only evaluate and apply their constraints for new configuration updates. They don’t assess and enforce existing resource configuration. Because of this retroactive behavior, the correct ingress settings need to be ensured before attaching the tag, as otherwise, the project team can only change IAM run.invoker permissions, while still having ingress settings with all or internal-and-cloud-load-balancing, and do not comply with the prerequisite of internal ingress.
As of the conditional organization policy, the project team is now allowed to grant the run.invoker permission for this Cloud Run service to the allUsers identity. Also, the project team is not able to change the ingress settings back to all or internal-and-cloud-load-balancing.
Wrapping up
In this article I showed you how to grant the run.invoker role to the allUsers identity for a Cloud Run service, even if the organization has enabled Domain Restricted Sharing. I used conditional DRS policies with Resource Manager tags, to tag some Cloud Run services that are allowed to use allUsers. Additionally, I showed you how to add a second organization policy, that prevents ingress settings other than internal for Cloud Run service with that same tag.
Conditional policies can also be set on the folder or project level, respecting the hierarchical evaluation of policies, which extends this solution to more complex organization and folder structures.
Want to learn more? Check out the resources below to dive in:
Cloud Run Access control with IAM | Cloud Run Documentation
Restricting identities by domain | Resource Manager Documentation
Understanding hierarchy evaluation | Resource Manager Documentation
Read More for the details.