If you use Dokku, you’re probably already familiar with the Let’s Encrypt plugin to enable HTTPS on your apps with a single command.
The plugin uses
certbot with the HTTP-01 challenge that requires that the app be accessible on the Internet. This
can’t work on apps that live in a private network.
At Bixoto, we use Tailscale and Headscale to manage our VPN. All machines have an address in a private IP range.
We have a domain dedicated to VPN usage that we’ll call
bx.vpn in the rest of the post; we bought it on OVH and
manage its DNS with OVH’s servers. It has one subdomain per
machine/app that points to its private address.
The DNS zone looks like this:
# IPv4 itchy 3600 IN A 100.xx.xxx.1 scratchy 3600 IN A 100.xx.xxx.2 snowball 3600 IN A 100.xx.xxx.3 # IPv6 itchy 3600 IN AAAA fd7a:xxxx:xxxx::1 scratchy 3600 IN AAAA fd7a:xxxx:xxxx::2 snowball 3600 IN AAAA fd7a:xxxx:xxxx::3 ...
That way, anyone can resolve
100.xx.xxx.1, but since it’s a private IP you have to be in the
private network to access the machine it points to.
Dokku apps are configured to listen only on the private interfaces:
# Bind to the private addresses of the server Dokku runs on dokku nginx:set $APP_NAME bind-address-ipv4 100.xx.xxx.3 dokku nginx:set $APP_NAME bind-address-ipv6 fd7a:xxxx:xxxx::3 dokku proxy:build-config $APP_NAME
That way, even if you know the public IP address of the server that hosts Dokku, you can’t access the apps. That’s good for our security, but if we want to have HTTPS on our private apps, we can’t use Let’s Encrypt as-is because it needs to access our apps over HTTP.
The solution is to use the DNS-01 challenge, whose support was added in the Dokku Let’s Encrypt plugin in January, 2023 for wildcard domains. The official docs only talk about wildcard domains, but this challenge can also be used for private addresses.
The DNS-01 is a DNS equivalent of the HTTP-01 challenge: instead of asserting that you own the server the domains points to, it checks that you have access to the DNS zone (= you own the domain). To do so, it needs access to your DNS provider to add a temporary subdomain that is then checked by Let’s Encrypt’s server.
This is the setup for OVH, but other providers are supported.
Go on OVH to create an API key. Fill the name and description, set the
validity to “Unlimited”, and in “rights”, make sure you allow
POST /domain/zone/* and
Then, copy your credentials and set them in Dokku:
dokku letsencrypt:set --global dns-provider-OVH_APPLICATION_KEY "..." dokku letsencrypt:set --global dns-provider-OVH_APPLICATION_SECRET "..." dokku letsencrypt:set --global dns-provider-OVH_CONSUMER_KEY "..." dokku letsencrypt:set --global dns-provider-OVH_ENDPOINT ovh-eu # or ovh-ca
Note: these variables can also be set at the application level.
Now, to make an application use DNS-01 instead of HTTP-01, set the
dokku letsencrypt:set $APP_NAME dns-provider ovh
If you did everything right, you should be able to run
dokku letsencrypt:enable $APP_NAME; Let’s Encrypt will use the
DNS-01 challenge and you’ll get HTTPS on your app even if it’s not accessible from the public Internet.