Skip to content

Google Cloud Storage SignedURL with Cloud Run, Cloud Functions and GCE VMs

License

Notifications You must be signed in to change notification settings

salrashid123/gcs_signedurl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Google Cloud Storage SignedURL with Cloud Run, Cloud Functions and GCE VMs

Code snippet to create a GCS Signed URL in Cloud Run, Cloud Functions and GCE VMs using impersonation.

  • Why Impersonation?
    Well, Cloud Run, Cloud Functions and GCE environments do not have anyway to sign anything (and no, do NOT embed a service account key file anywhere!). Since those environments can sign by themselves, they need to use an API to sign on behalf of itself. That API is basically a proxy for a service account key: iamcredentials.serviceAccounts.signBlob

The following examples uses the ambient service account to sign (eg, the service account cloud run uses is what signs the URL). It would be a couple more steps to make cloud run sign on behalf of another service account (and significantly more steps for java and node that made some assumptions on your behalf already)

Finally, if you must use a key, try to embed it into hardware, if possible.

Setup

export PROJECT_ID=`gcloud config get-value core/project`
export PROJECT_NUMBER=`gcloud projects describe $PROJECT_ID --format="value(projectNumber)"`
export BUCKET_NAME=crdemo-$PROJECT_NUMBER
export SA_EMAIL=$PROJECT_NUMBER-compute@developer.gserviceaccount.com

gsutil mb gs://$BUCKET_NAME

echo foo > file.txt
gsutil cp file.txt gs://$BUCKET_NAME

# allow cloud run's default service account access
gsutil acl ch -u $SA_EMAIL:R gs://$BUCKET_NAME/file.txt

# enable 'self impersonatin'
gcloud iam service-accounts  add-iam-policy-binding \
 --role=roles/iam.serviceAccountTokenCreator  \
 --member=serviceAccount:$SA_EMAIL $SA_EMAIL

gcloud config set run/region us-central1

Then build and push the language your'e interested in below

docker build -t gcr.io/$PROJECT_ID/crsigner .
docker push gcr.io/$PROJECT_ID/crsigner

gcloud run deploy signertest --image gcr.io/$PROJECT_ID/crsigner --platform=managed --set-env-vars="BUCKET_NAME=$BUCKET_NAME,SA_EMAIL=$SA_EMAIL"

export CR_URL=`gcloud run services describe  signertest --format="value(status.url)"`

curl -s $CR_URL

curl -s `curl -s $CR_URL`

golang

In golang, we're using the IAMCredentials api to sign the bytes.

After PR 4604 was merged, this is done automatically if you are using

	storageClient, _ := storage.NewClient(ctx)
	s, _ := storageClient.Bucket(bucketName).SignedURL(objectName, &storage.SignedURLOptions{
		Method:  http.MethodGet,
		Expires: expires,
	})

java

for java, first build

mvn clean install

java -jar ./target/docker-0.0.1-SNAPSHOT.jar

then build the docker image, push to gcr then deploy to cloud run

Python

google-auth python offers two signer interfaces you can use:

You might be wondering why an IDToken credentials has a signer? Well, thats a side effect of an incorrect initial implementation of the compute engine ID Token (see issue #344). The interface users should use is impersonated_credential

nodeJS

Nodejs its pretty easy since by default, the library automatically tries to use IAMCredentials API in these environments;

see bucket.getSignedUrl()

In Google Cloud Platform environments, such as Cloud Functions and App Engine, 
you usually don't provide a keyFilename or credentials during instantiation. In those environments, we call the signBlob API

However, my preference would've been to make it explicit applied which would also allow you to set a different account to sign with. For example, like in the sample below using the IAM google-auth-library-nodejs#impersonated-credentials-client

const { GoogleAuth, Impersonated } = require('google-auth-library');
const {Storage} = require('@google-cloud/storage');
const {IAMCredentialsClient} = require('@google-cloud/iam-credentials');

    let targetClient = new Impersonated({
        sourceClient: client,
        targetPrincipal: saEmail,
        lifetime: 10,
        delegates: [],
        targetScopes: ['https://www.googleapis.com/auth/cloud-platform']
    });

    const storage = new Storage({
        auth: {
            getClient: () => targetClient,
        },
    });

    const options = {
        version: 'v4',
        action: 'read',
        expires: Date.now() + 10 * 60 * 1000, // 10 minutes
    };

    const [url] = await storage
        .bucket(bucketName)
        .file(fileName)
        .getSignedUrl(options);

Unfortunately, google-cloud node js auth storage library does not support signing using impersonation with signer

dotnet

(contributions welcome)

About

Google Cloud Storage SignedURL with Cloud Run, Cloud Functions and GCE VMs

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published