How to enable TLS for Hasura GraphQL Engine in an Azure Container Instance with Caddy

AuthorKanishk Singh

June 04, 2020

azurehasuragraphql

How to enable TLS for Hasura GraphQL Engine in an Azure Container Instance with Caddy

We recently had to deploy the Hasura GraphQL Engine on Azure. Hasura GraphQL Engine provides GraphQL APIs over PostgreSQL databases along with realtime event triggers and webhooks.

When we started out, we found an ARM (Azure Resource Manager) template by Hasura for deploying Hasura Engine with Azure Database for PostgreSQL server. However, it lacked TLS support.

Our frontend is hosted over Azure Static Web Apps, which receives TLS from Azure by default. If our Hasura GraphQL endpoint isn’t served over HTTPS too, modern web browsers refuse to send requests or load content to/from the endpoint citing mixed-content errors. So it was vital to have TLS for our graphQL endpoint.

Our Options

There are a number of ways to make a web app running in an ACI (Azure Container Instance) have TLS. The two prominent ones we came across were:

  1. Using an Nginx sidecar container to serve self-signed or domain validated certificates.
  2. Having your container groups inside a Virtual Network and using an Azure Application Gateway, Azure API Management, or a Azure Function proxy for the TLS termination.

However, these approaches have some drawbacks. If we use the first method, automatic renewal of the certificate becomes troublesome.

In the second method, you can specify a certificate to Application Gateway or API Management from Azure Key Vault. Be that as it may, the only two CA providers supported by Azure as of writing this are GlobalSign and DigiCert. And even though Key Vault can auto-renew the certificates, the certificates themselves aren’t cheap.

Using self-signed certificates would allow our frontend to access the graphQL endpoint, but visiting the Hasura Engine’s web console would have shown warnings on most browsers since self-signed certificates aren’t trusted anymore. We just needed a Domain Validated certificate and LetsEncrypt certificates are free, so we were pushing for those.

Caddy for Automatic HTTPS

We came across Caddy, a web server written in Go with automatic HTTPS. Caddy automatically provisions TLS certificates from LetsEncrypt and keeps them renewed. This is very convenient, and exactly what we wanted. So we wrote a ARM template for our setup, iterating on the template created by Hasura, and made changes to include Caddy.

Caddy is available as a Docker container image. So using it was as simple as defining a container in our ARM template and specifying the command and the ports for it. We could also specify our config for Caddy by mounting a Caddyfile into the container as a volume, but our usecase was very simple and a reverse proxy can be made with a simple command. Here’s the command we used

caddy reverse-proxy --from hasura.example.com --to localhost:8080

Notice that we’re forwarding requests made to our domain to port 8080 inside the container group, as port 8080 is where the Hasura GraphQL Engine is listening for requests. The reason we’re not running Hasura on port 80 is so that Caddy can use the port to forward all requests from port 80 to 443, so all requests will be using HTTPS.

Here are the three components of our setup:

  1. Hasura GraphQL Engine: To provide the GraphQL API.
  2. Caddy: To generate LetsEncrypt certificates and auto-renew them.
  3. Azure Database for PostgreSQL server: The database used by Hasura.

Given below is the architecture diagram of what we’re doing.

Architecture Diagram showing Hasura GraphQL engine deployed on Azure Container Instances and Caddy for TLS/SSL

The Hasura GraphQL Engine and Caddy are inside a Container Group while the PostgreSQL server is a managed instance from Azure. All three of them are inside a common Resource Group.

One-Click Deployment Templates

We’ve created a one-click deployment template you can use for quickly checking out this setup. The one below is for deploying Hasura Engine with a new PostgreSQL DB instance.

TIP: Use Ctrl+Click to open the link in new tab.

Click to Deploy

On the Custom Deployment screen, fill the details as shown in the below screenshot. Hover on the info icon for more details about the fields.

NOTE: Make sure the region you choose for your Resource Group has ACI and Azure DB for PostgreSQL avaiable. Check their availability here. Availability in a region may also depend on your subscription.

Remember the value you put in Hasura Console Domain field. It will be necessary after deployment is finished.

Details for Hasura with new Postgres DB

Using an existing PostgreSQL instance for Hasura Engine

If you have an existing PosgreSQL database you’d like to use with Hasura, use the below template and specify the database’s parameters like the host, port, username, password and the DB name.

Click to Deploy

Fill the details like shown in the below screenshot

Details for Hasura with existing Postgres

Deploying

Once you’re done filling the details, click “Purchase” and it’ll start the deployment process.

Go to your notifications (bell icon) in Azure portal and click the “Deployment in Progress” link.

Once the page says “Your Deployment is Complete”, go to “Outputs” section from the left sidebar, and copy the IP Address, as shown below.

Outputs

You must add this IP Address to an A record in your domain’s DNS settings. Go to your domain’s DNS settings, and add an A Record with the Hasura Console Domain string (hasura.example.com in our case) in the HOST field and the IP Address in the VALUE field.

Now, go back to your Resource Group, select the deployed Container Group, and select “Restart” to restart Caddy and GraphQL Engine. Upon restarting, Caddy will perform the TLS-ALPN-01 challenge and obtain the certificates. Wait for a minute or two and try visiting the URL you entered in your A Record from your browser.

If everything went fine, you should see your Hasura GraphQL Engine page, asking for your admin secret. Enter the secret you specified while deploying and you should be in. You can check the certificates for the domain too.

Debugging

If something goes wrong for some reason, and things don’t work out as expected, you can go to your Container Groups, select “Containers” from the left sidebar, and check the logs for Caddy and Hasura Engine containers.

Wrapping up

We hosted Hasura GraphQL Engine on an Azure Container Instance, along with Azure Database for PostgreSQL server. We used Caddy with automatic HTTPS for our GraphQL endpoint.

You can also check out Hasura’s documentation on how to setup Hasura in an ACI to get more information on how Hasura Engine works in Azure.

The templates used in this post can be found on our GitHub repository. This architecture can also be modified to provision TLS certificates for almost any webapp running in ACI, since the Hasura Engine is just a web application here.

For questions and suggestions, feel free to reach out to us on Twitter.