Securing Dialogflow CX Webhooks with private access to Cloud Function

A Comprehensive Tutorial

Giorgio Crivellari
Google Cloud - Community
10 min readDec 10, 2023

--

In an era where data privacy and secure communication are paramount, setting up a private connection between Dialogflow CX and Cloud Functions Gen2 is not just a technical necessity but a good practice. This article aims to demystify the process of creating a secure, private channel for your cloud-based applications and services. Whether you are a cloud architect, a developer, or just a tech enthusiast exploring Google Cloud’s capabilities, this step-by-step guide is crafted to navigate you through the intricate process of integrating Dialogflow CX with Cloud Functions in a secure and private manner.

Architecture

High Level Architecture

The diagram illustrates the flow and components involved in the setup.

  1. Dialogflow CX: This is where the conversational experiences are designed and managed. Dialogflow acts as the front end, interpreting user input and determining the flow of the conversation.
  2. Service Directory: It provides service discovery for Google Cloud resources, allowing you to keep track of your services and ensure that Dialogflow CX can discover and communicate with the Cloud Function securely.
  3. Application Load Balancer: This component stands between Dialogflow CX and the Cloud Function, ensuring that all traffic is properly routed and providing an extra layer of security by allowing only authorized requests through.
  4. Cloud Function Gen2: Here lies the logic of your application, where you can process, handle, and respond to Dialogflow’s requests. The Cloud Function executes in response to Dialogflow’s requests and is the workhorse of your setup.
  5. Certificate Authority Service: This ensures that the communication between Dialogflow CX and the Cloud Function is encrypted and authenticated, providing a secure channel for data exchange.

Note on Enhanced Security: VPC Service Controls

While the architecture outlined provides a robust framework for secure communication between Dialogflow CX and Cloud Functions, it’s worth noting that incorporating VPC Service Controls can elevate your security to an even higher level. VPC Service Controls act as a perimeter that guards against data exfiltration, ensuring that sensitive information cannot leave your Google Cloud services network.

Prerequisites

This setup demands certain prerequisites to ensure a smooth and successful configuration process.

Here’s what you need to get started:

  1. Google Cloud Project: Ensure you have an active Google Cloud project with Billing Account connected.
  2. VPC or Shared VPC: In addition to your project’s VPC, a Shared VPC Network that’s shared with your Application Project is required. This allows for resource sharing across multiple projects, enhancing efficiency and management.
  3. Free /28 Subnet in the VPC: Make sure you have a free /28 subnet available in your Shared VPC. This small subnet range is sufficient for this setup and helps in allocating IP addresses efficiently.
  4. Cloud Shell or Command Line Tools: You’ll need a shell environment equipped with gcloud, git, jq, and openssl tools. Google Cloud Shell is a convenient option as it comes pre-installed with these tools and is directly integrated with your Google Cloud account, simplifying the process.

Next, we’ll move on to the step-by-step process to setup the architecture.

Step-by-step Setup

The following step-by-step guide will provide you with the practical instructions needed to establish a private and secure connection between Dialogflow CX and Cloud Functions.

Step 1: Environment Setup

To simplify all the next step I personally prefer to have variables with common information of my environment.

Here the list of variables used in the procedure:

cat <<EOF > .env
export PROJECT_ID=your-project-id
export REGION=europe-west1
export VPC_NAME=vpc-name
export SUBNET_NAME=subnet-name
# host project id (shared vpc)
export HOST_PROJECT_ID=your-host-project-id
# name of /28 subnet available on shared vpc
export HOST_VPC_SUBNET=subnet-name
# serverless connector name
export SERVERLESS_CON_NAME=serverless-connector
# service directory namespace
export SERVICE_DIR_NS=dfcx-ns
# CA Pool name
export CA_POOL_NAME=dfcx-ca-pool
# CA Rool Subject
export CA_ROOT_SUBJECT="C=US,ST=California,L=Mountain View,O=Example LLC,CN=example.com"
# Service Name
export SERVICE_NAME=hello-world

echo "default variables loaded!"
EOF

Now load your variable in the current environment and setup gcloud:

# load variables
source .env
# login on Google Cloud
gcloud auth login
# set the project id
gcloud config set project $PROJECT_ID
# get and export the project nr
export PROJECT_NR=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")

and it’s time to enable all services needed in the selected project:

gcloud  --quiet services enable compute.googleapis.com --project $PROJECT_ID
gcloud --quiet services enable dialogflow.googleapis.com --project $PROJECT_ID
gcloud --quiet services enable vpcaccess.googleapis.com --project $PROJECT_ID
gcloud --quiet services enable run.googleapis.com --project $PROJECT_ID
gcloud --quiet services enable privateca.googleapis.com --project $PROJECT_ID
gcloud --quiet services enable servicedirectory.googleapis.com --project $PROJECT_ID
gcloud --quiet services enable cloudbuild.googleapis.com --project $PROJECT_ID
gcloud --quiet services enable containerregistry.googleapis.com --project $PROJECT_ID

Step 2: Deploy Serverless VPC Connector

The Serverless VPC Connector is a bridge that connects your serverless environment to the Virtual Private Cloud (VPC), allowing Cloud Function to communicate with services available in the private networks.

If your application doesn’t need to communicate with your private VPC you can skip this step.

Serverless VPC Access Connector Architecture

Below the list of commands in case you have to connect to Shared VPC (more complex situation that need permission across projects):

# enable vpcaccess api
gcloud --quiet services enable vpcaccess.googleapis.com --project $PROJECT_ID

# assign permission to Serverless Connector Agent to list Shared VPC networks
gcloud projects add-iam-policy-binding $HOST_PROJECT_ID \
--role "roles/compute.networkUser" \
--member "serviceAccount:service-$PROJECT_NR@gcp-sa-vpcaccess.iam.gserviceaccount.com"

gcloud projects add-iam-policy-binding $HOST_PROJECT_ID \
--role "roles/compute.networkUser" \
--member "serviceAccount:$PROJECT_NR@cloudservices.gserviceaccount.com"

# deploy serverless connector (adapt instance min, max and type to your needs)
gcloud compute networks vpc-access connectors create $SERVERLESS_CON_NAME \
--region $REGION \
--subnet $HOST_VPC_SUBNET \
--subnet-project $HOST_PROJECT_ID \
--min-instances 2 \
--max-instances 10 \
--machine-type e2-micro

Having established a secure VPC foundation, the next move is to deploy a Cloud Function.

Step 3: Deploy ‘Hello World’ Cloud Function

We’ll start to deploy simple ‘Hello World’ function to verify that our setup is working correctly before we proceed with more complex integrations.

Here the commands to deploy the Cloud Function application from local machine:

# clone locally the application repository
git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

# deploy the application with ingress internal-only and serveless connector
gcloud --quiet functions deploy $SERVICE_NAME \
--gen2 \
--project=$PROJECT_ID \
--region=$REGION \
--runtime=nodejs16 \
--source=nodejs-docs-samples/functions/helloworld/helloworldGet/ \
--entry-point=helloGET \
--ingress-settings=internal-only \
--trigger-http \
--no-allow-unauthenticated \
--vpc-connector projects/$HOST_PROJECT_ID/locations/$REGION/connectors/$SERVERLESS_CON_NAME

# gather cloud function uri
export CF_HELLO_URL=$(gcloud functions describe $SERVICE_NAME --region=$REGION --project=$PROJECT_ID --format=json | jq --raw-output .serviceConfig.uri)

# assign permission to Dialogflow Service Agent
gcloud projects add-iam-policy-binding $PROJECT_ID \
--role "roles/run.invoker" \
--member "serviceAccount:service-$PROJECT_NR@gcp-sa-dialogflow.iam.gserviceaccount.com"

Pro Tip: Gen2 is required because it creates automatically a Cloud Run service used by Serverless NEG in the next steps.

After deploying a simple Cloud Function, the next step is to ensure it can be securely discovered and invoked by Dialogflow CX.

Step 4: Setup Service Directory

The Service Directory is a scalable and secure registry for discovering, publishing, and connecting services. In this setup Service Directory will expose the Cloud Function Services behind the Internal Load Balancer endpoint and Dialogflow will point to that.

Assign permissions to Dialoflow Service Agent to view Service Directory:

gcloud projects add-iam-policy-binding $PROJECT_ID \
--role "roles/servicedirectory.viewer" \
--member "serviceAccount:service-$PROJECT_NR@gcp-sa-dialogflow.iam.gserviceaccount.com"

gcloud projects add-iam-policy-binding $PROJECT_ID \
--role "rules/pscAuthorizedService" \
--member "serviceAccount:service-$PROJECT_NR@gcp-sa-dialogflow.iam.gserviceaccount.com"

Create Service Directory Namespace and Service for the new Cloud Function Application created before:

# create Service Directory namespace
gcloud service-directory namespaces create $SERVICE_DIR_NS --location=$REGION

# create Service on the namespace for Cloud Run app
gcloud service-directory services create $SERVICE_NAME \
--project=$PROJECT_ID \
--namespace=$SERVICE_DIR_NS \
--location=$REGION

By completing this step, your ‘Hello World’ Cloud Function will be securely registered in your Google Cloud environment, making it discoverable and invokable by Dialogflow CX in a controlled and private manner.

Step 5: Configure Private Certification Authority for ILB Certificates

Securing your internal communication channels with SSL encryption is critical. In this step, we will configure a Private Certification Authority (CA) to issue certificates for your Internal Load Balancer, which fronts the Cloud Function.

Note: If you plan to use your CA, you can skip this step

All Cloud Function and Cloud Run application are deployed by default with hostname *.a.run.app. Based on that the procedure will release a certificate for that wildcard domain.

# create Private CA Pool
gcloud privateca pools create $CA_POOL_NAME \
--project=$PROJECT_ID \
--location=$REGION \
--tier=devops

# create Private CA Root CA
gcloud privateca roots create ${CA_POOL_NAME}-root \
--project=$PROJECT_ID \
--location=$REGION \
--pool=$CA_POOL_NAME \
--auto-enable \
--subject="$CA_ROOT_SUBJECT" \
--validity="P10Y" \
--key-algorithm="rsa-pkcs1-4096-sha256"

# export root ca cert in PEM format
curl -H "Authorization: Bearer $(gcloud auth print-access-token)" \
https://privateca.googleapis.com/v1/projects/$PROJECT_ID/locations/$REGION/caPools/$CA_POOL_NAME/certificateAuthorities/${CA_POOL_NAME}-root \
| jq --raw-output .pemCaCertificates[0] > root-ca-cert.pem

# convert root ca cert from PEM to DER
openssl x509 -in root-ca-cert.pem -out root-ca-cert.der -outform DER

# generate request certificate for LB (change subject accordingly)
openssl req -new -newkey rsa:2048 -nodes -keyout star.a.run.app.com.key -out star.a.run.app.com.csr \
-subj '/CN=*.a.run.app/O=Example LLC/C=US'

# generate certificate for LB with 3Y validity (extend it if needed)
gcloud privateca certificates create cloud-function-tls \
--project=$PROJECT_ID \
--issuer-pool=$CA_POOL_NAME \
--issuer-location=$REGION \
--csr=./star.a.run.app.com.csr \
--cert-output-file=./star.a.run.app.com.pem \
--validity=P3Y

By the end of this step, you will have a certified secure channel for all communication between Dialogflow CX and your Cloud Function, thanks to the newly configured Private Certification Authority and SSL certificate.

Step 6: Set Up HTTP Internal Load Balancer

An HTTP Internal Load Balancer acts as a control point for traffic within your Google Cloud project. It ensures that only authorized traffic can reach your Cloud Function and could be even more useful to decouple backend applications and use url maps to have multiple Cloud Functions or other backends behind (example: GKE or GCE application).

source: https://cloud.google.com/load-balancing/docs/negs/serverless-neg-concepts#regional-internal

In this step, we will configure an Internal Load Balancer to manage the requests sent from Dialogflow CX to the Cloud Function.

# create a backend for Cloud Function app
gcloud compute backend-services create $SERVICE_NAME-backend \
--global

# create Serverless NEG for the Cloud Function Gen2 (aka Cloud Run)
gcloud compute network-endpoint-groups create $SERVICE_NAME-neg \
--network-endpoint-type=serverless \
--region=$REGION \
--cloud-run-service=$SERVICE_NAME

# add Serverless NEG to Backend
gcloud compute backend-services add-backend $SERVICE_NAME-backend \
--region=$REGION \
--network-endpoint-group=$SERVICE_NAME-neg \
--network-endpoint-group-region=$REGION

# add url-map to Backend (in this case we keep it simple)
gcloud compute url-maps create $SERVICE_NAME-url-map \
--default-service=$SERVICE_NAME-backend \
--region=$REGION

# add private certificate
gcloud compute ssl-certificates create star-a-run-app \
--certificate ./star.a.run.app.com.pem \
--private-key ./star.a.run.app.com.key \
--region=$REGION

# create target proxy with certificate (aka Front End)
gcloud compute target-https-proxies create $SERVICE_NAME-target-proxy \
--ssl-certificates=star-a-run-app \
--url-map=$SERVICE_NAME-url-map \
--region=$REGION

# generate forwarding rule (aka internal load balancer)
gcloud beta compute forwarding-rules create $SERVICE_NAME-forwarding-rule \
--region=$REGION \
--load-balancing-scheme=INTERNAL_MANAGED \
--network=$VPC_NAME
--subnet=$SUBNET_NAME \
--subnet-region=$REGION \
--allow-global-access \
--target-https-proxy=$SERVICE_NAME-target-proxy \
--target-https-proxy-region=$REGION \
--ports=443 \
--service-directory-registration=projects/$PROJECT_ID/locations/$REGION/namespaces/$SERVICE_DIR_NS/services/$SERVICE_NAME


echo "Now you can set up the Webhook on Dialogflow CX:"
echo "Service Directory uri: projects/$PROJECT_ID/locations/$REGION/namespaces/$SERVICE_DIR_NS/services/$SERVICE_NAME"
echo "Url: $CF_HELLO_URL"
echo "Attach CA file: root-ca-cert.der (from current folder)"

The procedure at the end will automatically create the private Load Balancer endpoint in the Service Directory namespace previously created.

Step 7: Establish Webhook on Dialogflow CX

The final step in our setup is to configure Dialogflow CX with a webhook that points to our Internal Load Balancer. This allows Dialogflow CX to securely send requests to our Cloud Function when needed as part of the conversational process.

Let’s jump in the Dialogflow CX console and navigate to the agent for which you’re setting up the webhook. Go to the ‘Manage’ section and then to the ‘Webhooks’ tab.

In the Service Directory field you need to specify the complete URI of the service.
The webhook URL should contain the Cloud Function/Cloud Run url.

Remember to select “Access Token” (Thanks Leandro Siow!!!)

Scrolling down you’ll find the “Custom CA certificate” section and there you’ll need to upload the root-ca-cert.der certificate file you have generated and exported locally in the step 6.

the Custom CA certificate section in the Webhook page

Well Done, You Made It!

If everything has been done correctly Logs from Dialogflow CX should be visible in the Cloud Function console (or Cloud Logging).

Conclusion

As we wrap up this comprehensive guide, it’s clear that securing communication between Dialogflow CX and Cloud Functions involves a series of deliberate and technical steps. By following this guide, you have successfully created a secure, private channel for your cloud-based conversational applications, ensuring that your data and interactions are protected from unauthorized access.

Throughout this journey, we covered the essentials of environment setup, the deployment of a Serverless VPC Connector, the creation and registration of a ‘Hello World’ Cloud Function, the configuration of a Private Certification Authority for SSL encryption, the establishment of an HTTP Internal Load Balancer, and the crucial step of setting up a secure webhook in Dialogflow CX.

Further Learning and Resources

To enhance your knowledge and explore different perspectives on secure cloud communications, here are some valuable resources that complement the topics covered in this guide:

  1. Dialogflow CX & Cloud Function Webhook with VPC SC (Lorenzo Caggioni) : This article provides an in-depth look at integrating Dialogflow CX with Cloud Functions using VPC Service Controls, offering a more advanced layer of security beyond the scope of our guide.
  2. Dialogflow and Service Directory (Neil Kolban): Explore how Dialogflow can be integrated with the Service Directory for streamlined service management and discovery, a vital aspect of building efficient cloud architectures.

Thank you for following along with this guide. Your journey towards mastering secure cloud communications doesn’t end here; it’s just the beginning. Happy coding, and stay secure!

--

--

Giorgio Crivellari
Google Cloud - Community

Customer Engineer at Google Cloud | Ex-Red Hatter | Multi-Skilled Technical Architect | Driving Innovation in Hybrid & Multi-Cloud Architectures