1

I'd like to create Signed URLs to Google Cloud Storage resources from an app deployed using CloudRun.

I set up CloudRun with a custom Service Account with the GCS role following this guide.

My intent was to use V4 Signing to create Signed URLs from CloudRun. There is a guide for this use-case where a file service_account.json is used to generate JWT config. This works for me on localhost when I download the file from google's IAM. I'd like to avoid having this file committed in the repository use the one that I provided in CloudRun UI.

I was hoping that CloudRun injects this service account file to the app container and makes it accessible in GOOGLE_APPLICATION_CREDENTIALS variable but that's not the case.

Do you have a recommendation on how to do this? Thank you.

Jose V
  • 1,356
  • 1
  • 4
  • 12
zdebra
  • 948
  • 8
  • 22
  • What's your language? I would like to try something! – guillaume blaquiere Oct 05 '20 at 15:05
  • It's possible that you can use the [IAM signBlob](https://cloud.google.com/iam/docs/reference/credentials/rest/v1/projects.serviceAccounts/signBlob) Method as a workaround. I would also benefit from knowing which language are you using – Jose V Oct 05 '20 at 15:33
  • I am using golang. – zdebra Oct 05 '20 at 17:10
  • Actually, it looks like a golang code sample issue as that one relies on the injected `service_account.json` where other languages don't. See https://cloud.google.com/storage/docs/access-control/signing-urls-with-helpers#storage-signed-url-object-go – zdebra Oct 05 '20 at 17:19
  • I have the same problem, but in python https://stackoverflow.com/questions/64234214/how-to-generate-a-blob-signed-url-in-google-cloud-run – sww314 Oct 07 '20 at 01:44

2 Answers2

2

As you say, Golang Storage Client Libraries require a service account json file to sign urls.

There is currently a feature request open in GitHub for this but you should be able to work this around with this sample that I found here:

import (
"context"
  "fmt"
  "time"
  "cloud.google.com/go/storage"
  "cloud.google.com/go/iam/credentials/apiv1"
  credentialspb "google.golang.org/genproto/googleapis/iam/credentials/v1"
)

const (
  bucketName = "bucket-name"
  objectName = "object"
  serviceAccount = "[PROJECTNUMBER]-compute@developer.gserviceaccount.com"
)

func main() {
  ctx := context.Background()

  c, err := credentials.NewIamCredentialsClient(ctx)
  if err != nil {
     panic(err)
  }

  opts := &storage.SignedURLOptions{
     Method: "GET",
     GoogleAccessID: serviceAccount,
     SignBytes: func(b []byte) ([]byte, error) {
        req := &credentialspb.SignBlobRequest{
            Payload: b,
            Name: serviceAccount,
        }
        resp, err := c.SignBlob(ctx, req)
        if err != nil {
           panic(err)
        }
        return resp.SignedBlob, err
     },
     Expires: time.Now().Add(15*time.Minute),
  }

  u, err := storage.SignedURL(bucketName, objectName, opts)
  if err != nil {
     panic(err)
  }

  fmt.Printf("\"%v\"", u)
}
Jose V
  • 1,356
  • 1
  • 4
  • 12
  • 1
    Thanks. This works. The required roles for the service account are `roles/storage.objectViewer` and `roles/iam.serviceAccountTokenCreator` – zdebra Oct 07 '20 at 17:08
0

Cloud Run (and other compute platforms) does not inject a service account key file. Instead, they make access_tokens available on the instance metadata service. You can then exchange this access token with a JWT.

However, often times, Google’s client libraries and gcloud works out of the box on GCP’s compute platforms without explicitly needing to authenticate. So if you use the instructions on the page you linked (gcloud or code samples) it should be working out-of-the-box.

ahmet alp balkan
  • 42,679
  • 38
  • 138
  • 214