When running a docker container via Cloud Run, calling Sign(bucket, objectName, duration, HttpMethod.Get)
on a Google.Cloud.Storage.V1.UrlSigner
, it immediately throws an HttpRequestException, stemming from a 403 Forbidden response.
I suspect that I'm doing something incorrectly, but I have no idea what it is. The Service Account attached to the Cloud Run Revision has a role granting the storage.objects.get
permission, and is capable of downloading the contents of the storage object.
Here's the code in question:
var urlSigner = UrlSigner.FromCredential(GoogleCredential.GetApplicationDefault());
var signedUrl = urlSigner.Sign("bucketName", "objectName", TimeSpan.FromMinutes(30), HttpMethod.Get);
If I run the project locally, (with GOOGLE_APPLICATION_CREDENTIALS pointing to a json service account key), I can create a signed url that works as expected. I can download the contents of the file both executing locally and on cloud run.
The service accounts are the same. I've verified this with the instance metadata at http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email
, which matches the "client_email" key in the credential file.
The only difference I've observed is that the GoogleCredential
returned by GetApplicationDefault()
wraps a ServiceAccountCredential
locally, but a ComputeCredential
when running on the cloud. This seems like a red herring, but I honestly don't know where else to look.
Versions that may be relevant:
- Google.Cloud.Storage.V1 4.5.0
- net6.0
- ASP.NET Core 6.0
Some References:
Never set GOOGLE_APPLICATION_CREDENTIALS as an environment variable on a Cloud Run service. Always configure a user-managed service account instead
https://cloud.google.com/run/docs/securing/service-identity#per-service-identity
When you generate a signed URL, you specify a user or service account which must have sufficient permission to make the request that the signed URL will make.
https://cloud.google.com/storage/docs/access-control/signed-urls
Callstack for Exception:
System.Net.Http.HttpRequestException: Response status code does not indicate success: 403 (Forbidden).
at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
at Google.Apis.Auth.OAuth2.Requests.RequestExtensions.PostJsonAsync[TResponse](Object request, HttpClient httpClient, String url, CancellationToken cancellationToken)
at Google.Apis.Auth.OAuth2.ComputeCredential.SignBlobAsync(Byte[] blob, CancellationToken cancellationToken)
at Google.Cloud.Storage.V1.UrlSigner.CredentialBlobSigner.CreateSignatureAsync(Byte[] data, BlobSignerParameters _, CancellationToken cancellationToken)
at Google.Api.Gax.TaskExtensions.WaitWithUnwrappedExceptions(Task task)
at Google.Api.Gax.TaskExtensions.ResultWithUnwrappedExceptions[T](Task`1 task)
at Google.Cloud.Storage.V1.UrlSigner.CredentialBlobSigner.CreateSignature(Byte[] data, BlobSignerParameters signerParameters)
at Google.Cloud.Storage.V1.UrlSigner.V4Signer.Sign(RequestTemplate requestTemplate, Options options, BlobSignerProvider blobSignerProvider, IClock clock)
at Google.Cloud.Storage.V1.UrlSigner.Sign(RequestTemplate requestTemplate, Options options)
at Google.Cloud.Storage.V1.UrlSigner.Sign(String bucket, String objectName, TimeSpan duration, HttpMethod httpMethod, Nullable`1 signingVersion)
at <my code>