Google Cloud Platform – Cloud Run Service Account Permission Issue

google-cloud-platformgoogle-cloud-storagejava

I'm working on a program that will run on Google Cloud Run and has files stored in Google Cloud storage.

The problem I'm experiencing happens when attempting to generate a signed URL to download a file from Cloud storage that is usually private. On my local machine it works fine, but when running in Cloud Run it does not.

Locally I am using a service account that has the Storage Object Admin role assigned to it. I load the permissions using an environment variable called GOOGLE_APPLICATION_CREDENTIALS with in its value the absolute path to the key .json file I downloaded from the cloud console.

On Cloud Run, I granted the same role to the service account that the service runs as. However, when I attempt to sign anything on there, I get an exception:

java.io.IOException: Error code 403 trying to sign provided bytes: The caller does not have permission

In my code I don't explicitly select any service account since the SDK documentation makes me believe this is done automatically. In Cloud Run I do not have a GOOGLE_APPLICATION_CREDENTIALS variable set because I thought this was also done automatically.

What confuses me is that the application running in Cloud Run can still upload files to Cloud Storage fine, so this makes me think it does have some form of credentials from somewhere.

What am I doing wrong?

Best Answer

TL;DR: Make sure the service account has the iam.serviceAccounts.signBlob permission.


After further investigating the method I used to acquire credentials on Cloud Run:

GoogleCredentials credentials = ComputeEngineCredentials.create();
Storage storage = StorageOptions.newBuilder().setCredentials(credentials).build().getService();

I read the javadoc for ComputeEngineCredentials:

OAuth2 credentials representing the built-in service account for a Google Compute Engine VM. Fetches access tokens from the Google Compute Engine metadata server. These credentials use the IAM API to sign data. See sign(byte[]) for more details.

After this I read the javadoc for sign(byte[]) (emphasis mine):

Signs the provided bytes using the private key associated with the service account. The Compute Engine's project must enable the Identity and Access Management (IAM) API and the instance's service account must have the iam.serviceAccounts.signBlob permission.

As such, I created a new role with just the iam.serviceAccounts.signBlob permission and assigned it to the service account that my Cloud Run configuration uses. After this, the issue immediately went away (no need to redeploy)