Now that we are able to synch TLS certs and the Caddy configuration across various points-of-prescense, we now need to ensure that Caddy will only request certificates for approved names. As we are doing this for Gitea Pages, we won’t know what the domain names are in advanced and will have to create a validation service to handle this. This approach can also be abstracted to any SaaS that provides the ability for customers to bring their own domain.

Caddy has a built-in way to request if a certain domain name is approved or not. The way this process works, is that in the Caddy configuration you’ll define an endpoint that will receive a request for a domain name, and run against your custom validation rules which could be a static list, a database call, or something else. If the name is approved, then the endpoint will return a 200 status code, otherwise it will deny the request with a non-200 status code.

The benefits of ensuring that domains are processed against this validation service include not requesting certs for domains outside of your control which could use up your Let’s Encrypt quota, and it also prevents malicious actors from pointing their names at your infrastructure and receiving valid TLS certs.

Following the previous tutorial where the Caddy configuration is dynamically generated, the customers domain will be added to the config file, but we don’t want users to be able to add any domain of their choice, we want to ensure that they have control over the domain, and that it also doesn’t point to a nefarious location.

Using the following configuration as an explaination, we can see how this would work:

{
    on_demand_tls {
        ask https://validation.service.tld/endpoint
    }
}

example.com {
    tls {
        on_demand
    }
    respond "Hello World!"
}

In this example, we are telling Caddy to ask the validation service at https://validation.service.tld/endpoint if the domain name is approved for requesting TLS certificates. If the domain name is approved, then Caddy will request a certificate for the domain name, and serve the content as defined in the configuration, otherwise no certificates will be requested, protecting your Let’s Encrypt rate-limit.

The validation service should be as fast as possible, to limit the time it takes for Caddy to request the certificate and respond to the initial request to that domain. The way Caddy will interact with that validation URL would be to make a GET request with a query string of ?domain=example.com where example.com is the domain name that the user is requesting a certificate for. If the validation service returns a 2xx status code, then Caddy will request the certificate, otherwise any other status code will prevent Caddy from making a request for a certificate.

The validation service could be as simple as a Go application that reads a file of approved domain names, or as complex as a database lookup. The important part is that it responds quickly and accurately to the domain name requests. Ideally, if you are running external checks on the domain, such as ensuring that it points to your loadbalancer, or that it doesn’t attempt to use a nefarious domain, and then saving that status to the database, instead of making those checks every time a request to the validation service is made.

I will leave the completion of the validation service up to the reader, as there are many possible ways to implement this, and even many possible use-cases. There is no requirement for which language is used, or how the validation server be setup. You could use a standard http server that responds 200 to every request, to an entire monolithic application that integrates with your billing system, and everything in between.

In this series, we looked at various different topics, and how I used them as building blocks for a DIY CDN, however these topics can each be used independently of each other, and in different ways. I hope you’ve enjoyed this series, and I look forward to writing more in the future. If I find that something else in building this CDN interesting, I’ll be sure to add yet another post in this series.