serverless-google-cloudfunctions icon indicating copy to clipboard operation
serverless-google-cloudfunctions copied to clipboard

How to deploy public function on GCF?

Open Micka33 opened this issue 4 years ago • 20 comments

I can't find the answer anywhere in the documentation. Feel free to point it to me.

$> serverless
  Your Environment Information ---------------------------
     Operating System:          darwin
     Node Version:              12.13.1
     Framework Version:         1.67.0 (standalone)
     Plugin Version:            3.5.0
     SDK Version:               2.3.0
     Components Version:        2.22.3
# serverless.yml
service: my-name

provider:
  name: google
  runtime: nodejs8
  region: europe-west1
  project: project-123456
  credentials: ~/path/to/secret.json

plugins:
  - serverless-google-cloudfunctions
package:
  excludeDevDependencies: true
  exclude:
    - node_modules/**
    - .gitignore
    - .git/**

functions:
  my_name:
    handler: my_name_http
    memorySize: 128
    timeout: 60s
    events:
      - http: path
    labels:
      application: my-name
    environment:
      PROJECT_ID: ${self:provider.project}

Since the last serverless upgrade, my functions are deployed without the "Cloud function invoker" role set to "allUsers". Screenshot 2020-03-30 at 16 55 47

By the way, I can't find anywhere in the documentation how to set it in serverless.yml.
Can you help me?

N.B.: Of course, I did set it in the Google Cloud Console UI and the function works perfectly. But then, I can no longer do serverless deploy. It displays the following error:

$> serverless deploy
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Compiling function "my-name"...
Serverless: Uploading artifacts...
Serverless: Artifacts successfully uploaded...
Serverless: Updating deployment...
Serverless: Checking deployment update progress...
....
  Error --------------------------------------------------

  Error: Deployment failed: RESOURCE_ERROR

       {"ResourceType":"gcp-types/cloudfunctions-v1:projects.locations.functions","ResourceErrorCode":"400","ResourceErrorMessage":{"code":400,"message":"Invalid JSON payload received. Unknown name \"location\" at 'function': Cannot find field.","status":"INVALID_ARGUMENT","details":[{"@type":"type.googleapis.com/google.rpc.BadRequest","fieldViolations":[{"field":"function","description":"Invalid JSON payload received. Unknown name \"location\" at 'function': Cannot find field."}]}],"statusMessage":"Bad Request","requestPath":"https://cloudfunctions.googleapis.com/v1/projects/project-123456/locations/europe-west1/functions/my-name-dev-my-name","httpMethod":"PATCH"}}
      at throwErrorIfDeploymentFails (/Users/username/Documents/remotal/remotal-my-name/node_modules/serverless-google-cloudfunctions/shared/monitorDeployment.js:71:11)
      at /Users/username/Documents/remotal/remotal-my-name/node_modules/serverless-google-cloudfunctions/shared/monitorDeployment.js:42:17
      at processTicksAndRejections (internal/process/task_queues.js:93:5)

     For debugging logs, run again after setting the "SLS_DEBUG=*" environment variable.

  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues
     Issues:        forum.serverless.com

  Your Environment Information ---------------------------
     Operating System:          darwin
     Node Version:              12.13.1
     Framework Version:         1.67.0 (standalone)
     Plugin Version:            3.5.0
     SDK Version:               2.3.0
     Components Version:        2.22.3

Micka33 avatar Mar 30 '20 15:03 Micka33

@Micka33 it is caused by the location field not supported anymore, please update to latest version and try again

bao1018 avatar Apr 01 '20 14:04 bao1018

I don't understand what you mean by update to latest version and try again. I already upgraded to the last version of serverless. I did so before running serverless create --......

  • What should I update to the latest version?
  • This error only occurs when I manually add a role to a google cloud function through the GCC UI and run serverless deploy.
  • I would like to understand how I can specify that I want the role Cloud function invoker set with allUsers when running serverless deploy.

Micka33 avatar Apr 02 '20 15:04 Micka33

Up

Micka33 avatar Apr 08 '20 16:04 Micka33

Have the same issue, after updated serverless and serverless-google-cloudfunctions have an error:

Error: Forbidden
Your client does not have permission to get URL /function from this server

iSynthetica avatar Apr 09 '20 07:04 iSynthetica

Does anyone know a way to setup Cloud function invoker to allUsers while deploying with serverless ?

Micka33 avatar Apr 16 '20 12:04 Micka33

Same here, only happens if you upgrade to version 3.0.0, tried downgrading to 2.4.3 and everything was working again with the correct invoker.

Trowsing avatar Apr 17 '20 11:04 Trowsing

It would be nice to have a fix for this. Downgrading to 2.4.3 does not work anymore as Google ended support for the previous API, so we are stuck with 3.0.0.

So what is the correct way to make a function publicly accessible with serverless?

mwawrusch avatar Apr 30 '20 00:04 mwawrusch

I am using private cloud functions with Serverless v3. I have setup a google cloud project which has a Gateway API (App Engine) to sit in front of the private cloud functions. To allow communication between gateway and cloud functions, I have created a Service Account with the Cloud Function Invoker permissions. The gateway will then use a JSON keyfile for that service account to generate an ID Token to be sent as bearer token in request to Cloud Functions.

curtismenmuir avatar May 04 '20 09:05 curtismenmuir

The issue is not able setIamPolicy after deployment.

We should be able to configure IAM policy via serverless.yml; similar to AWS.

pramendra avatar May 04 '20 12:05 pramendra

Up.

zenati avatar May 14 '20 12:05 zenati

+1

andrx avatar May 20 '20 04:05 andrx

Also having this problem now after upgrading to google provider 3 here - any fix in the pipeline?

edit

My fix is the following for allowing public access to my cloud function:

Create a policy.json file with the content:

{
  "bindings": [
    {
      "role": "roles/cloudfunctions.invoker",
      "members": [
        "allUsers"
      ]
    }
  ]
}

Ensure that you have the gcloud sdk installed and logged in (gcloud auth login) and then:

$ gcloud functions set-iam-policy --region={YOUR_REGION} {FUNCTION_NAME} policy.json

Updated IAM policy for function [FUNCTION_NAME].
bindings:
- members:
  - allUsers
  role: roles/cloudfunctions.invoker
etag: blah-blah
version: 1

Replacing {YOUR_REGION} and {FUNCTION_NAME} with your own appropriate values.

nover avatar May 26 '20 07:05 nover

Docs and commands to change this policy using gcloud are here: https://cloud.google.com/functions/docs/securing/managing-access-iam#allowing_unauthenticated_function_invocation

gcloud functions add-iam-policy-binding FUNCTION_NAME \
 --member="allUsers" \
 --role="roles/cloudfunctions.invoker" \
 --project=YOUR_PROJECT_ID

The previous command needs to be run once per function. New deployments work after making functions public with this command.

Apparently, an equivalent to the --allow-unauthenticated flag is missing in SLS deploy.

frandiox avatar May 28 '20 02:05 frandiox

This was very helpful @frandiox

Here is what worked for me:

gcloud functions add-iam-policy-binding SERVICENAME-STAGE-FUNCTIONNAME \
    --member="allUsers" \
    --role="roles/cloudfunctions.invoker" \
    --project="PROJECT_ID" \
    --region="us-east1"

kevboutin avatar May 28 '20 15:05 kevboutin

When will this be available from the npm package? I see the latest version (3.1.0) was deployed 2 months ago

buffolander avatar Jun 28 '20 22:06 buffolander

The changes made in the above linked PR appear to not fully work (see #222). I'm working through a solution for it, but it does seem like more work needs to be done to get the feature stable enough for a new release. I'll post an update once I make some progress.

edaniszewski avatar Jun 29 '20 13:06 edaniszewski

I've opened a PR for an alternative approach for IAM that I've tested locally and seems to work. See: https://github.com/serverless/serverless-google-cloudfunctions/pull/223

edaniszewski avatar Jun 30 '20 21:06 edaniszewski

Wanted to add my two-pence based on suggestions of @frandiox @kevboutin, I managed to set it up using sls hooks using this plugin: https://www.npmjs.com/package/serverless-plugin-scripts, works pretty nice for me (I have just one function at this point) but I can imagine it might not work in all cases. Here is the snippet:

service: YOUR-SERVICE-NAME

custom:
  ...
 # this comes from https://www.npmjs.com/package/serverless-plugin-scripts
  scripts:
    commands:
      make-public: gcloud functions add-iam-policy-binding ${self:service}-${self:provider.stage}-${opt:function, "YOUR-DEFAULT-FUNCTION-NAME"} --member="allUsers" --role="roles/cloudfunctions.invoker" --project=${self:provider.project} --region=${self:provider.region} | xargs echo 
    hooks:
      'after:deploy:deploy': npx sls make-public --function=YOUR-FUNCTION-NAME
...

And then it automatically makes the specified functions public after deploy and otherwise I can run sls make-public --function=<function-name> at any time.

Cheers, maybe that helps.

Thanks @frandiox @kevboutin @deemetree-at-satelligence for the suggestions!

I put together a little script that combines the above by running through all the functions defined in serverless.yml and updates their IAM policy.

The script looks for a parameter allowUnauthenticated: true within each function definition to determine if the function should be made public (otherwise it's private). This is of course an "unofficial" parameter that was suggested in #223, so until the PR is merged this seems like an unobtrusive workaround.

#!/bin/bash

# Get a list of functions in the serverless.yml file and format as args
functions=$(sls print --path functions --transform keys --format text | xargs)

# Sort functions as public and private
pub=()
prv=()
for fn in ${functions[@]}; do
    # if the `allowUnauthenticated: true` flag is defined for the function flag it to be made public
    if [[ "$(sls print --path functions."$fn" --format yaml | xargs)" == *"allowUnauthenticated: true"* ]]; then
        pub+=($fn)
    else
        prv+=($fn)
    fi
done

# Run the mkfunc-pub command for each public function
for fn in ${pub[@]}; do
    echo "Making function \""$fn"\" public..."
    npx sls mkfunc-pub --function="$fn"
done

# Run the mkfunc-pvt command for each private function
for fn in ${prv[@]}; do
    echo "Making function \""$fn"\" private..."
    npx sls mkfunc-pvt --function="$fn"
done

I've also put together a Serverless Google Functions Starter Project for Golang that includes all this.

Hopefully it helps someone!

cgossain avatar Nov 20 '20 20:11 cgossain

@deemetree-at-satelligence Great workaround! For anyone trying this with a service account, make sure that your service account has the 'Security Admin' Role set, otherwise you'll get a 403 error on the scripted IAM policy update command.

J-Rojas avatar May 12 '21 10:05 J-Rojas