terraform-provider-google icon indicating copy to clipboard operation
terraform-provider-google copied to clipboard

Cloud Functions: Allow / Disable unauthenticated invocations

Open JordanStebbings opened this issue 5 years ago • 19 comments

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request
  • Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritize the request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment. If the issue is assigned to the "modular-magician" user, it is either in the process of being autogenerated, or is planned to be autogenerated soon. If the issue is assigned to a user, that user is claiming responsibility for the issue. If the issue is assigned to "hashibot", a community member has claimed the issue already.

Description

Hey there, I am struggling to replicate the functionality of Cloud Functions GUI to stop allowing unauthenticated invocations. In the resources, I have uploaded a imgur picture of the tick box that I am trying to disable.

I have tried the recommendation for creating a google_cloudfunctions_function_iam_binding resource with the cloudfunctions.invoker role on a service account, however, this will still allow any account connect to the cloud function.

New or Affected Resource(s)

  • google_cloudfunctions_cloud_function
  • google_cloudfunctions_function_iam_binding

Potential Terraform Configuration

resource "google_cloudfunctions_function" "function-api" {
   unauthenticated_invocations = true / false
}

References

https://imgur.com/a/eukK8oy https://www.terraform.io/docs/providers/google/d/datasource_cloudfunctions_function.html

b/274817429

JordanStebbings avatar Mar 05 '20 16:03 JordanStebbings

Hey @JordanStebbings! We actually have an example of how to do this in our docs: https://www.terraform.io/docs/providers/google/r/cloudfunctions_function.html. Take a look at the first example :)

danawillow avatar Mar 09 '20 17:03 danawillow

Just kidding, I can read. Reopening for a bit more detailed response later on how to remove the binding.

danawillow avatar Mar 09 '20 17:03 danawillow

Hey Dana, thanks for the response, I will describe what I have tried below:

To replicate what I have test please use the follow:

  1. Create a Google Cloud Function with Python 3.7, keep everything the default settings however under Authentication Untick the Checkbox for Allow Unauthenticated Invocations

  2. When the function is deployed, click the HTTP Trigger and you should receive the message:

"Your client does not have permission to get URL /CLOUD_FUNCTION_NAME from this server."\

  1. Visit the Cloud Function Details page, click Source and Download Zip to get a copy of the default Python 3.7 functionality. Put this into the root of your new terraform module and save it as "function-source-terraform-test.zip"

  2. Deploy the following terraform functionality by running terraform init, plan, apply:

provider "google" {
  project = "YOUR PROJECT NAME"
  region = "YOUR PROJECT REGION"
  zone = "YOUR PROJECT ZONE"
  credentials = "YOUR DEFAULT CREDENTIALS / PATH TO FILE"
}

resource "google_storage_bucket" "resource-storage" {
    name = "temp-gcf-code"
}

resource "google_storage_bucket_object" "storage-object-code-api" {
  bucket = google_storage_bucket.resource-storage.name
  name = "index-api.zip"
  source = "function-source-terraform-test.zip"
}

resource "google_cloudfunctions_function" "function-api" {
  name = "test-invocation-terraform"
  runtime = "python37"
  timeout = 540
  trigger_http = true
  entry_point = "hello_world"
  source_archive_bucket = google_storage_bucket.resource-storage.name
  source_archive_object = google_storage_bucket_object.storage-object-code-api.name
}

# IAM entry for a single user to invoke the function
resource "google_cloudfunctions_function_iam_member" "invoker" {
  project        = google_cloudfunctions_function.function-api.project
  region         = google_cloudfunctions_function.function-api.region
  cloud_function = google_cloudfunctions_function.function-api.name

  role   = "roles/cloudfunctions.invoker"
  member = "user:[email protected]" / "serviceAccount:SERVICE_ACCOUNT"
}
  1. Visit the URL that the new Cloud Function is deployed from, you will be able to see: "Hello World!"

JordanStebbings avatar Mar 10 '20 10:03 JordanStebbings

Also just been looking at the Gcloud documentation for deploying Cloud Functions, there is a Flag which can be set for --allow-unauthenticated, could this be replicated for this?

https://cloud.google.com/sdk/gcloud/reference/functions/deploy

JordanStebbings avatar Mar 11 '20 12:03 JordanStebbings

@JordanStebbings when you create a function through google_cloudfunctions_function, the provider calls api service https://cloud.google.com/functions/docs/reference/rest/v1/projects.locations.functions/create. Currently by default, api creates google_cloudfunctions_function and implicitly creates an iam object which binds allUsers to roles/cloudfunctions.invoker role. These are all done inside API service. At the provider level, currently there is no code yet that can disable the default iam object creation. To work around this in order to achieve disable unauthenticated invocation, you may create google_cloudfunctions_function_iam_policy, similar to below code, to override that default iam object.

data "google_iam_policy" "admin" {
  binding {
    role = "roles/viewer"
    members = [
      "serviceAccount:[email protected]",
    ]
  }
}

resource "google_cloudfunctions_function_iam_policy" "editor" {
  project = google_cloudfunctions_function.function.project
  region = google_cloudfunctions_function.function.region
  cloud_function = google_cloudfunctions_function.function.name
  policy_data = data.google_iam_policy.admin.policy_data
}

@c2thorn Please note As of January 15, 2020, HTTP functions require authentication by default. (https://cloud.google.com/functions/docs/securing/managing-access-iam) We'd better update the provider code accordingly.

edwardmedia avatar Mar 17 '20 00:03 edwardmedia

@JordanStebbings when you create a function through google_cloudfunctions_function, the provider calls api service https://cloud.google.com/functions/docs/reference/rest/v1/projects.locations.functions/create. Currently by default, api creates google_cloudfunctions_function and implicitly creates an iam object which binds allUsers to roles/cloudfunctions.invoker role. These are all done inside API service. At the provider level, currently there is no code yet that can disable the default iam object creation.

This is correct, until a while ago allUsers was added by default to any cloud function created, which required explicit removal.

To work around this in order to achieve disable unauthenticated invocation, you may create google_cloudfunctions_function_iam_policy, similar to below code, to override that default iam object.

A less elegant but likely more self-explanatory way to go about this at the time was to explicitly remove the IAM binding.

resource "null_resource" "rm-iam-unauthenticated-invocations" {
  depends_on = [google_cloudfunctions_function.myfunction]
  provisioner "local-exec" {
    command = " gcloud beta functions --project=${var.project} remove-iam-policy-binding ${google_cloudfunctions_function.myfunction.name} --member=\"allUsers\" --role=\"roles/cloudfunctions.invoker\";"
  }
 triggers = { timestamp = "${timestamp()}" }
}

@c2thorn Please note As of January 15, 2020, HTTP functions require authentication by default. (https://cloud.google.com/functions/docs/securing/managing-access-iam) We'd better update the provider code accordingly.

Also, the documentation has been updated accordingly at some point this year:

image

jdsalaro avatar Oct 30 '20 13:10 jdsalaro

If using an API Gateway, it is essential for the cloud function to have ingress_settings = "ALLOW_ALL",

The other values won't allow Cloud API Gateway to access the function. Regardless of IAM settings. Then protect the function with IAM to limit access to a service account or user.

This may help others if it is more clearly documented.

intotecho avatar Feb 08 '22 08:02 intotecho

Following Google documentation about IAM permissions on Cloud Functions and Terraform Google Provider documentation you can use allUsers as a member to allow invocations as seen below:

...

resource "google_cloudfunctions_function" "function" { ... }

resource "google_cloudfunctions_function_iam_member" "member" {
  project        = google_cloudfunctions_function.function.project
  region         = google_cloudfunctions_function.function.region
  cloud_function = google_cloudfunctions_function.function.name

  role   = "roles/cloudfunctions.invoker"
  member = "allUsers"
}

On the interface: image

Though it would be simpler to have a param inside google_cloudfunctions_function resource as @JordanStebbings initially suggested.

GuilhermeFaga avatar Aug 05 '22 16:08 GuilhermeFaga

Can anyone else simply not find the mythical "Authentication section on the Configuration panel" in the google cloud console? This is completely confusing.

meropis avatar Sep 01 '22 11:09 meropis

@meropis are you referring to this section during Clound Function creation?

image

GuilhermeFaga avatar Sep 01 '22 13:09 GuilhermeFaga

Does anyone know how to do the same for Gen2 functions? Am unable to find similar solution for gen2 functions!

Aryaman6492 avatar Sep 05 '22 21:09 Aryaman6492

Does anyone know how to do the same for Gen2 functions? Am unable to find similar solution for gen2 functions!

Documentation from Terraform Registry: google_cloudfunctions2_function

GuilhermeFaga avatar Sep 05 '22 21:09 GuilhermeFaga

Does anyone know how to do the same for Gen2 functions? Am unable to find similar solution for gen2 functions!

Documentation from Terraform Registry: google_cloudfunctions2_function

Can you please specify where? :D I really dont see any exact example of allowing unauth invocation within terraform gen2 docs which allows allUsers, the example given simply allows a service account which has to pass an authentication anyway.

But anyway Got it i looked up your earlier reply and found some reference from troubleshooting#unauthorized-client and managing-access#make-service-public,

Anyone looking up for gen2, change to cloud run instead of cloud function iam binding for gen2 like below:

resource "google_cloud_run_service_iam_binding" "default" {
  location = google_cloudfunctions2_function.resolver_function.location
  service  = google_cloudfunctions2_function.resolver_function.name
  role     = "roles/run.invoker"
  members = [
    "allUsers"
  ]
}

Changes after applying within cloud run : image

Edit : Note google_cloudfunctions2_function_iam_member doesnt work, it has to be google_cloud_run_service_iam_binding

Ripeey avatar Sep 05 '22 22:09 Ripeey

@Ripeey thank you so much! The google_cloud_run_service_iam_binding worked! How did you figure that out? It's so unintuitive

toandm avatar Oct 11 '22 03:10 toandm

FYI, if you want to specify IAM role for a specific account on a Gen2 Cloud Function, you must use google_cloud_run_service_iam (binding, member, ...) too. google_cloudfunctions2_function_iam_member does not work. This also reflects on GCP console, where you must specify the permission on Cloud Run service to work. Specifying permission on Cloud Function will not work for Gen2 function.

toandm avatar Feb 02 '23 03:02 toandm

@toandm I wish I could find your comment earlier. After struggling with http trigger gen2 function and cloud scheduler I eventually realized I need to set not just google_cloudfunctions2_function_iam_member with roles/cloudfunctions.invoker, but also google_cloud_run_service_iam_member with roles/run.invoker to associated cloud run service. It was not obvious from documentation which btw contains wrong gcloud commands sometimes.

piotrekkr avatar Jun 04 '23 21:06 piotrekkr

Adding this seems to work for me:

resource "google_cloudfunctions2_function_iam_member" "cloud_function_invoker" {
  project        = google_cloudfunctions2_function.function.project
  location       = google_cloudfunctions2_function.function.location
  cloud_function = google_cloudfunctions2_function.function.name
  role           = "roles/cloudfunctions.invoker"
  member = "allUsers"
}

resource "google_cloud_run_service_iam_member" "cloud_run_invoker" {
  project  = google_cloudfunctions2_function.function.project
  location = google_cloudfunctions2_function.function.location
  service  = google_cloudfunctions2_function.function.name
  role     = "roles/run.invoker"
  member = "allUsers"
}

I got that from here

With that, when I go to the web console, I see that access is granted to allUsers in the PERMISSIONS sections.

What surprises me is that deploying the functions using the command line does not show me that allUsers in the PERMISSIONS section but my function url still works. Any one knows why this difference?

This would be an example of my gcloud command line:

gcloud functions deploy terra-test \
       --memory=128Mi \
       --timeout=60 \
       --max-instances=1 \
       --region=europe-west1 \
       --project=dmproject-1 \
       --gen2 \
       --trigger-http \
       --allow-unauthenticated \
       --runtime=python311 \
       --entry-point=main_http \
       --source=src

dmontaner avatar Jan 31 '24 11:01 dmontaner