Dialogflow and Service Directory

Neil Kolban
Google Cloud - Community
7 min readAug 28, 2023

When using Dialogflow CX we often wish to invoke a Webhook backend to either dynamically retrieve some information or perform an action as part of our conversation flows. Dialogflow allows us to specify the URL for the Webhook and expects the Webhook to be reachable over the Internet on a public IP address. From a security perspective, this might be less than desirable. Almost by definition, a Dialogflow Webhook is an internal piece of infrastructure that only has value when called by your own Dialogflow agents. While we can protect the Webhook with security permissions and source IP address restrictions, there is still an opening here that is simply not needed. If an error is made in configuration of the Webhook then there is an attack surface (Internet accessibility) that could be problematic.

Here is a diagram for a sample Webhook that we exposed from a Compute Engine:

In the above, a chat user interacts with a Dialogflow CX agent. The agent flow then invokes the webhook using its public URL. This is routed to the Webhook that is exposed through the Internet. The REST request then arrives at the Webhook and service and is executed.

What we instead ideally want is (logically) the following:

In this diagram, the Webhook (Compute Engine) has no public (Internet) accessibility. There will be a VPC network defined to which the Compute Engine will be attached and the Compute Engine will not be assigned a public IP address. The only way to reach the Webhook service is through Google internal routing.

The remainder of this paper will describe how to achieve this outcome.

First we should note that Dialogflow does not run in any of our Google Cloud projects. It runs in a Google managed environment. This means that we don’t have control over such items as shared VPCs or VPC peering (directly) to hook-up connectivity between Dialogflow and our VM hosting project. Fortunately, Google has provided an alternative answer.

Within Google Cloud there is a component called Service Directory. The notion behind Service Directory is that service providers (i.e. applications in one of your Google Cloud projects that provide services) can advertise themselves to potential consumers. Think of Service Directory like the old yellow pages where a service can make itself known (advertise) and a consumer of the service can then consult Service Directory to find the location of the desired service. In our story, we can now have the Webhook advertise itself as an endpoint within the Service Directory and Dialogflow can ask Service Directory to find the Webhook.

By itself, this doesn’t appear to add any value … all we would have done is exchange the hard-coded Webhook endpoint URL with a logical name as known to Service Directory … this doesn’t appear to have changed our routing/network policy. However … Service Directory has some additional magic. Some Google products (such as Dialogflow) have a special relationship with Service Directory. If the advertised endpoint exists in a private VPC network and Service Directory is told that the caller is authorized, then Service Directory will arrange to route the request to the endpoint even though the caller has no obvious explicit path to the target. Again, this capability is only available to a set of Google supplied service callers of which Dialogflow is one.

From an architectural perspective, we end up with:

The way to interpret this is that we create our Compute Engine attached to our VPC and then create a Service Directory definition that provides a named definition of our logical service. Dialogflow is then instructed to use Service Directory to access the Webhook. Dialogflow is authorized to be a user of Service Directory and granted the ability to invoke the Webhook.

There are a lot of parts in this story. We have Compute Engines, VPCs, Dialogflow, Service Directory, Cloud DNS and more. Let’s walk through a demonstration of actually setting up the above story.

Walkthrough

In this walkthrough we will end up with an environment where a Dialogflow agent makes a Webhook call that terminates in a Compute Engine that has no public IP address.

  1. Create a new Google Cloud Project. I called mine kolban-df-webhook. This will be the project used to configure Dialogflow, the VPC, the Compute Engine, Service Directory and Cloud DNS.
  2. We are going to be using a set of Cloud Services in our environment and need to enable the following APIs:
  • Compute Engine
  • Dialogflow
  • Service Directory
  • Cloud DNS

3. Create a new VPC network. This is the network to which a Compute Engine will be attached that will run the Webhook service. I called mine services-vpc:

4. Create a NAT so that we have a path outbound from the VPC to the Internet. This should only be required to install Compute Engine requirements such as Node.

5. Create a firewall rule allowing Google Private Network Access

6. Create a Compute Engine attached to the services-vpc network. I called my compute engine server1. For networking, I ensured that there was no public IP address and that we allowed HTTPS inbound traffic:

Once created we made a note of its allocated IP address:

Which in this case is 10.128.0.2.

7. We now logged into the Compute Engine using SSH.

8. Next we build an environment that would run JavaScript … this is because we wanted our Webhook application to be written in JavaScript. The recipe I used was:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
logout # and log in again
nvm install v18.17.1
node -v

9. Create an SSL certificate for our use

openssl genrsa -out server.key 2048
openssl req -nodes -new -sha256 -newkey rsa:2048 -key server.key -subj "/CN=df-webhook-service.service-directory.example.com" -out server.csr
openssl x509 -req -days 3650 -sha256 -in server.csr -signkey server.key -out server.crt -extfile <(printf "\nsubjectAltName='DNS:df-webhook-service.service-directory.example.com'")
openssl x509 -in server.crt -out server.der -outform DER

Download the server.der file that contains the generated certificate. We will use it later when we create the Dialogflow webhook.

10. Create a service directory namespace

11. Register a Service Directory service

12. Register an endpoint

13. Create a Cloud DNS zone associated with the service directory

we should be able to test this with:

ping df-webhook-service.service-directory.example.com

14. Create a Dialogflow agent

We created an agent called df-webhook-test. Since this was the first agent in the project, it took some time. Be patient.

We created an intent called “invoke webhook” with training phrases:

  • call webhook
  • invoke webhook

Define the webhook that we want to invoke

The URL us:

https://df-webhook-service.service-directory.example.com:3000

Note that we need to upload the server.der certificate.

Create a route in the default start page to invoke the webhook

15. Code and run the webhook

On the Compute Engine VM, code the following in a file called index.js:

const express = require('express');
const fs = require('fs')
const https = require('https')
const PORT = 3000
var options = {
key: fs.readFileSync('./server.key'),
cert: fs.readFileSync('./server.crt'),
};
// Webhook handler
const app = express();
app.use(express.json());
app.post('/', async (req, res) => {
console.log(" - - - - - - - -")
console.dir(req.body, { depth: 5 }); // Debug … log the input parameters
const response = {
sessionInfo: {
parameters: {
"my_webhook_response": "Hello World!"
}
}
}
res.send(response);
return
});
const server = https.createServer(options, app).listen(PORT, function(){
console.log("Express server listening on port " + PORT);
});

with a package.json file of:

{
"name": "webhook-handler",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "4.18.2"
}
}

16. Give permissions to the Dialogflow Service Account to be able to invoke Service Directory.

17. Test by running the Dialogflow agent

And that’s it. We see that it worked because we got the value of Hello World! back which could only have come from our Webhook.

To accompany this article, here is a YouTube recording/walkthrough of the previous steps.

References

--

--

Neil Kolban
Google Cloud - Community

IT specialist with 30+ years industry experience. I am also a Google Customer Engineer assisting users to get the most out of Google Cloud Platform.