Generating SSL certificates for SaaS customers without the hefty price tag
Tags: ssl, webdev, proxy, caddy
Published: 2023-11-25 10:02:49

## Introduction
A few years ago, I created a SaaS (software as a service) that allowed game server owners to monetize their player base. In a nutshell, this meant that game server owners could use my platform to create an online store for their server and sell intangible/virtual goods to their players. One of the many hurdles that I faced along my development journey was allowing merchants that use my platform to securely host their store on their domain rather than just on one of my platform's subdomains. If you've attempted to introduce such a feature, you would know that generating SSL certificates on-demand for domains that you do not own can be pretty cumbersome. This is a topic and a problem that I spent many weeks researching, and I often arrived at solutions that were not practical, too expensive (Cloudflare Enterprise anyone?), or too complicated.

The following blog post aims to better educate developers on how they might introduce such a feature into their platform.

## The problem
If your web application provides web hosting to your customers, granting them a subdomain on your application's domain that can be secured over HTTPS is very simple. A wildcard certificate can be issued that extends encryption to all subdomains; this is known by many and is very simple to achieve with many cloud hosting providers such as Amazon Web Services.

However, an issue arises when you want to allow your customers to securely host their sites on a domain that you do not control and have never had exposure to. In my first attempt at implementing this feature, I tried mimicking a customer and added a CNAME record that pointed to the relevant subdomain on my application's domain. Unfortunately, this solution produced an "SSL certificate mismatch" error, and my anti-virus software blocked me from viewing the page.

## Solution
The solution that I propose takes advantage of the open-source web server Caddy to generate SSL certificates on-demand. Using this method, however, is prone to attacks, as will be discussed. The following solution is how I achieved on-demand TLS with my web application being hosted by Amazon Web Services.

The network configuration that I created consisted of two AWS EC2 instances, one for hosting my Laravel web application and the other for hosting a Caddy reverse proxy responsible for provisioning SSL certificates. It is not a requirement that you run multiple instances; however, this was my configuration.

## Installing Caddy on Amazon Linux
I ran the following commands over SSH to install Caddy on my AWS EC2 instance running Amazon Linux 2.
```
sudo yum install yum-plugin-copr -y
sudo yum copr enable @caddy/caddy
sudo yum install caddy
```

## Configuring Caddy
Next, I configured the Caddy webserver to act as a reverse proxy and to also provision SSL certificates on-demand. The configuration file (Caddyfile) that I used was as follows:

```
{
        on_demand_tls {
                ask <API_ENDPOINT>
                interval 2m
                burst 5
        }
}

https:// {

        tls {
                on_demand
        }

        reverse_proxy {
                to <LARAVEL_SERVER_ADDRESS>
        }
}
```

There are two placeholders in the caddyfile:

- "API_ENDPOINT" Is the address to an API endpoint that checks if an SSL certificate can be provisioned for the domain. The ask setting is not required however your system will be prone to attacks as the rate at which SSL certificates can be provisioned by an authority is heavily rate limited. This endpoint should return a HTTP status of 200 (OK) if the requested domain can have a certificate provisioned, any other response will have the request ignored.
- "LARAVEL_SERVER_ADDRESS" Is the address of the Laravel web-server, this might be the address of an internal server or load balancer.

## Instructing Customers
Once the Caddy server had been configured and hardened my customers were able to use CNAME records to point their domains to their assigned subdomain on my application and avoid SSL certificate mismatch errors. When the first request from a new domain hit the Caddy server it would send a request to the API ask endpoint to check if an SSL certificate could be provisioned for that particular customer's domain. If the API endpoint returned a HTTP status of 200 (OK) Caddy would generate a certificate for the domain.

## Drawbacks & Limitations
While this is a great workaround for smaller SaaS products that don't have the money for products such as Cloudflare Enterprise, this workaround does have some drawbacks and limitations.

1. Scaling this solution can become complex as the issue of where exactly certificate data is stored amongst multiple proxy servers arises.
2. The number of services you must maintain increases.
3. Your customer's secure website relies on the successful operation of the Certificate Authority and Caddy.