4

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?

6
  • 1
    Hi @cascer1, I would like to mention that nothing of the things mentioned above are done automatically. You will need to set your environment variable for GOOGLE_APPLICATION_CREDENTIALS to be able to download a file from a GCS bucket. Check out the following Docker image sample that is later built to be executed on Cloud Run. I would also try to grant the service account that Cloud Run is using with the roles/storage.admin permission role. Keep me posted!
    – sllopis
    Mar 30, 2020 at 15:18
  • @sllopis I'm unsure how to do this securely. I don't want to include the credentials file in our source code. Why would I be allowed to specify a service account in the cloud run config if it's not used?
    – Cas
    Mar 31, 2020 at 11:10
  • @sllopis I'd like to add that github.com/googleapis/google-cloud-java#authentication mentions that the java SDK will attempt to use default google cloud credentials when no explicitly defined credentials are present. Does this not refer to the service account that I can see in the cloud run service yaml file? Thanks
    – Cas
    Mar 31, 2020 at 11:12
  • 1
    Note that when using GOOGLE_APPLICATION_CREDENTIALS you are not specifying or listing any credentials but rather giving the path to the directory where the credentials are located. The same documentation also points out that there are several authentication options when using google cloud libraries that are not within Compute Engine or App Engine. You will most likely need to set up authentication correctly and then supply the Cloud Run associated service account with the proper Cloud Storage permissions given the above error.
    – sllopis
    Apr 1, 2020 at 10:57
  • @sllopis thanks for your help. After further investigating the method I use to acquire credentials on cloud run (i.e: using the metadata server) I realized the service account needed the iam.serviceAccounts.signBlob permission. I'm not sure why this isn't required when using the credentials file but here we are.
    – Cas
    Apr 1, 2020 at 12:15

2 Answers 2

4

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)

1
  • 1
    THANK YOU! I've been running in circles trying to get this to work using a service account that had Storage Admin role. Didn't realize the ability to sign blobs was a separate permission. It was working on my personal credentials because I also had roles/iam.serviceAccountTokenCreator. So the simple fix was granting the account rights to create its own token gcloud iam service-accounts add-iam-policy-binding $SERVICEACCOUNT --role="roles/iam.serviceAccountTokenCreator" --member="serviceAccount:$SERVICEACCOUNT"
    – Greg Bray
    Aug 26, 2021 at 2:13
1

In addition, make sure you have also enabled IAM Service Account Credentials API for this to work.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .