Google Cloud Tasks trigger Cloud Function with INTERNAL only ingress

2.5k views Asked by At

How can I trigger a Cloud Function from Cloud Tasks when that function has its ingress settings set to "allow internal only"?

With that setting, the function rejects the incoming HTTP traffic from Cloud Tasks. This behavior occurs even if Cloud Tasks is in the same project as the function.

3

There are 3 answers

0
guillaume blaquiere On

It's funny because I asked Google PMs exactly about that on Tuesday this week! Because today you can't! It's in the radar of the PMs, with not timeline but it will be possible, a day.

My solution today.

If I have a cloud function in internal only mode which is used internally AND externally (or by Google serverless products not compliant with VPC connector, like Cloud Task, Cloud Scheduler, PubSub and Workflows), I create a "proxy function"

  • This proxy function is deployed in ingress=all mode and with no-allow-unauthenticated param
  • I grant only the service account of the external product on it as cloudfunctions.invoker on the proxy function to be sure that only this service account will be able to call the proxy function
  • I create a serverless VPC connector and add it to the proxy function
  • The proxy function only call the internal function.
3
Martin Beck On

Recently ran into this problem and providing a follow up for any people that are interested.

Two things of note, as mentioned by @guillaumeblaquiere Google PMs are aware of this and based on a recent support ticket I've opened with Google its been mentioned that they've put internal support for Cloud Tasks on their road map so potentially might be supported by EOY (2022). Here's two related issue trackers if anyone's interested and wants to show the need for this sort of feature.

In regards to the problem at hand, I would not recommend the above proxy function solution, this was also proposed by Google support. It's functionally no different than just making your original Cloud Function ingress settings to allow all traffic. You're just adding another hoop that doesn't provide a private solution.

A solution my team built instead looked like Cloud Tasks -> Pub/Sub -> Cloud Function.
This pattern allows you to keep everything within the VPC since Cloud Functions has a native trigger for Pub/Sub. Cloud Task can then interact with Pub/Sub through its REST API. This pattern can then be further secured by creating a runtime SA for Cloud Task that has the following permissions, can be granted at either the project level or resource depending on security needs:

  • Cloud Tasks Enqueuer
  • Pub/Sub Publisher
  • Cloud Functions Invoker
2
Chandrajyoti Das On

Hello @guillaume & @Thomas, I am facing challenge to invoke cloud Function from cloud task. Here are details of my IAM & Code:

const { CloudTasksClient } = require('@google-cloud/tasks');
const client = new CloudTasksClient();

//See https://cloud.google.com/tasks/docs/tutorial-gcf
module.exports = async (payload, scheduleTimeInSec) => {
  const project = process.env.GOOGLE_APPLICATION_PROJECTID;
  const queue = process.env.QUEUE_NAME;
  const location = process.env.QUEUE_LOCATION;
  const callBackUrl = https://asia-south2-trial-288318.cloudfunctions.net/cloud-function-node-expres/;

  // Construct the fully qualified queue name.
  const parent = client.queuePath(project, location, queue);

  const body = Buffer.from(JSON.stringify(payload)).toString('base64');

  const task = {
    httpRequest: {
      httpMethod: 'POST',
      url: callBackUrl,
      headers: { 'Content-Type': 'application/json' },
      body
    },
    scheduleTime: {
      seconds: scheduleTimeInSec,
    }
  };

  if (process.env.GOOGLE_APPLICATION_SERVICE_ACCOUNT_EMAIL) {
    task.httpRequest.oidcToken = {
      serviceAccountEmail: process.env.GOOGLE_APPLICATION_SERVICE_ACCOUNT_EMAIL
    }
  }

  const request = {
    parent: parent,
    task: task,
  };

  // Send create task request.
  try {
    let [responses] = await client.createTask(request);

    return ({ sts: true, taskName: responses.name, msg: "Email Schedule Task Created" })
  }
  catch (e) {
    return ({ sts: true, err: true, errInfo: e, msg: "Unable to Schedule Task. Internal Error." })
  }
}

The process.env.GOOGLE_APPLICATION_SERVICE_ACCOUNT_EMAIL has Cloud Functions Invoker role and the Cloud Function has allAuthenticatedUsers member with role Cloud Functions Invoker as per the doc.

But still I am seeing the 401 resposnse recevied by Cloud Task and Cloud Function is not getting called(See below image):

enter image description here

Any comment on this, whats going wrong here