Considering how fast setting up valid certificates for an OpenWrt router was, I really don’t know why I settled with that https warning on our router’s web page for as long as I did.
The process is well documented over on OpenWrt.org . Here’s are the steps I took, from memory:
Dependencies
Install a couple of packages
- acme
- the scripts that perform the setup in the background
- acme-dnsapi
- the specifics needed to verify you own your domain, through a Cloudflare DNS challenge
- luci-app-acme
- to be able to click-ops it all in the luci web interface
Setup
Navigate to Services -> ACME Certs in Luci and create a new Certificate Configuration, e.g. router_example_com
.
On General settings; Tick the Enabled box, choose a key size, put in a domain, e.g. router.example.com
, tick Use for httpd to adopt it automatically once we’re done.
On Challenge Validation; Choose DNS validation. Pick the right one from the list of supported providers
- that I spent way to much time reading on before I found the correct DNS API name - for Cloudflare it should be dns_cf
, and not cloudflare.
Over on dash.cloudflare.com
, create a token for your DNS Zone on your Cloudflare account, ensuring Edit zone
permissions are set only for the Zone that holds your domain name. Bring it back to Luci alongside the DNS Zone id to set these two fields
CF_Zone_ID=<uuid>
CF_Token=<token>
On Advanced settings, optionally set days until renewal. It will renew automatically after the amount of days has passed.
Click Save and apply and wait for your new certificate to be fetched. See Debug below to shed light on the process.
Resolve DNS locally
The domain I chose is currently only used locally, so I chose to set the fqdn (fully qualified domain name), e.g. example.com, to always resolve locally in the routers dns server. You could also use a subdomain for this, e.g. .home.example.com. This was done from Network -> DHCP and DNS -> Resolve these locally: /lan/example.com/
. This way DNS queries for it will never reach the internet.
Set the Local domain to be the domain suffix of the domain you own, example.com, that you will now aquire a certificate for. This way, if your router’s hostname is “router”, navigating to router.example.com should just work, and the certificate address will match the browser address you end up at, making your certificate valid.
What’s nice about this, is that it will require no hard coding of host names to ip addresses, and will work for all future network devices you might aquire certificates for, as long as their <hostname>.<domain suffix> match the certificate address.
If you don’t want to do this, you could also add a static hostname for router.example.com to point to the ip of your router.
How
This whole process works by Let’s Encrypt asking you to prove that you own the domain you request a certificate for, having you expose a dns TXT record containing a text of their asking. It is done automatically through the Cloudflare DNS API for your DNS domain zone, using the credentials you provide.
This way Cloudflare needs no record of your public ip while the process is ongoing - nothing on your router needs to be exposed the internet throughout this whole process. In fact, your domain’s DNS record over at Cloudflare could point to a completely different address than that of your house where the router is located, as the router will resolve every DNS query for this domain locally.
Still, the certificate will be valid, as the browser ends up seeing the expected web address.
Debug
If the certificate doesn’t appear, check the logs over on Status -> System Log for hints, or try restarting the service from System -> Startup -> acme -> Restart to make it try again.
📜☑️
Edit: [2025-02-15 Sat] Clarifications