Cloud Run services are public by default, meaning anyone with the URL can invoke them.
Let’s see how this works. Imagine a simple Python Cloud Run service that just echoes back its request:
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def index():
return f"Hello! You sent: {request.data.decode('utf-8')}"
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=8080)
If you deploy this to Cloud Run without any IAM restrictions, you get a public URL. Anyone can hit it with curl:
curl https://my-public-service-xyz.a.run.app -d "Testing public access"
Output:
Hello! You sent: Testing public access
This is convenient for public APIs, but often you need to restrict access to only specific users or service accounts within your Google Cloud project. This is where IAM (Identity and Access Management) comes in.
The primary IAM role for controlling access to Cloud Run services is roles/run.invoker. When you grant this role to a principal (a user, group, or service account), you’re giving them permission to invoke or call your Cloud Run service.
Setting up IAM restrictions:
- Identify the service: You need the full name of your Cloud Run service, which looks like
projects/PROJECT_ID/locations/REGION/services/SERVICE_NAME. - Identify the principal: This could be a user’s email address (e.g.,
user@example.com), a Google Group email (e.g.,my-team@googlegroups.com), or a service account’s email address (e.g.,my-service-account@PROJECT_ID.iam.gserviceaccount.com). - Grant the role: You can do this via the Google Cloud Console or
gcloud.
Using gcloud:
Let’s say you have a service named my-private-api in us-central1 in project my-gcp-project, and you want to grant invoke access to the service account frontend-runner@my-gcp-project.iam.gserviceaccount.com.
gcloud run services add-iam-policy-binding my-private-api \
--member='serviceAccount:frontend-runner@my-gcp-project.iam.gserviceaccount.com' \
--role='roles/run.invoker' \
--region='us-central1' \
--project='my-gcp-project'
This command adds an IAM policy binding to the specified Cloud Run service. It says, "For the my-private-api service in us-central1 within my-gcp-project, grant the roles/run.invoker role to the frontend-runner service account."
What happens when access is restricted?
Now, if the frontend-runner service account tries to invoke the service:
# Assuming the service account has permission to run this command
gcloud run services call my-private-api \
--region='us-central1' \
--platform='managed' \
--data='{"message": "Hello from service account"}' \
--project='my-gcp-project'
This will succeed, and you’ll get a response from your service.
However, if any other principal (like your user account, or another service account without the roles/run.invoker role) tries to call it, they will receive a 403 Forbidden error:
# This will fail if your current gcloud identity doesn't have the role
gcloud run services call my-private-api \
--region='us-central1' \
--platform='managed' \
--data='{"message": "Trying unauthorized access"}' \
--project='my-gcp-project'
The error message you’ll typically see in the logs or as an HTTP response will indicate an authorization failure, often a 403 Forbidden status code. The underlying reason is that the Cloud Run infrastructure checks the caller’s IAM permissions against the service’s IAM policy before allowing the request to reach your container.
You can also grant the roles/run.invoker role to allUsers (which is the default for public services) or allAuthenticatedUsers (which means any user authenticated with a Google account can access it).
To remove access, you’d use remove-iam-policy-binding:
gcloud run services remove-iam-policy-binding my-private-api \
--member='serviceAccount:frontend-runner@my-gcp-project.iam.gserviceaccount.com' \
--role='roles/run.invoker' \
--region='us-central1' \
--project='my-gcp-project'
The roles/run.invoker role is a granular way to manage who can trigger your Cloud Run workloads, ensuring that sensitive internal services are only accessible by authorized entities.
When you grant the roles/run.invoker role to a service account, and that service account is used by another Google Cloud service (like another Cloud Run service, a Compute Engine VM, or a Cloud Function) to call your protected Cloud Run service, the call is authenticated using the service account’s credentials. This is often managed implicitly when using Workload Identity, where the calling service’s identity is automatically translated into the necessary IAM permissions.
The next challenge you’ll face is managing the lifecycle of these service accounts and ensuring their credentials are secure and rotated.