There is an acme package available for OpenWrt which allows to obtain SSL certificates from Let’s Encrypt. Wouldn’t it be nice if we could have OpenWrt obtain SSL certificates from our own private ACME server (within our local network)? This blog post explains the easy steps in order to achieve just that.
What’s wrong with the default ACME package?
Actually, there is nothing wrong with the default package. However, it doesn’t allow for any ACME server URL other than Let’s Encrypt’s URL. These are our requirements:
- make use of the –server parameter so that we can supply the URL of our internal ACME server
- make use of the –ca-bundle parameter so that we can verify the SSL certificate of our internal ACME server
- make use of the –days parameter so that we can set automatic renewal of certificates to something (way) less than the default 60 days
What needs to change?
There is one config file that we have to customize and there is a script that we need to extend. Also, we need to install our internal root certificate so that acme.sh can verify the SSL certificate of our internal ACME server.
/etc/config/acme
As soon as you install the acme package, a default configuration file will be created. Within the acme section we need to supply our credentials (our email address). There is one cert section that we can assign a name to it, although the name doesn’t really matter as in most cases we will be ok with just having the one cert section anyway. The last 3 lines in that configuration file are our own and will not be parsed by the default acme package (we will handle that in a minute). All 3 options correspond to the command line parameters of the official acme.sh script.
config acme
option state_dir '/etc/acme'
option account_email 'john.doe@example.com'
option debug '0'
config cert 'localserver'
option keylength '2048'
option update_uhttpd '1'
list domains 'openwrt.local'
list domains 'openwrt'
option use_staging '0'
option enabled '1'
option server 'https://acme.local:8443/acme/weekly/directory'
option cabundle '/etc/ssl/certs/tinkivity.pem'
option renewal_days '3'
/usr/lib/acme/run-acme
While OpenWrt’s acme package calls an unmodified acme.sh script, it does so via a wrapper script. The name of that wrapper script is run-acme and we need to insert 9 lines of code to handle our own configuration parameters.
Find the issue_cert() subroutine and go to the first line that modifies the acme_args parameter. Now insert the following lines (see marked in red below) into the script.
[ "$enabled" -eq "1" ] || return
[ "$DEBUG" -eq "1" ] && acme_args="$acme_args --debug"
local server
local cabundle
local renewal_days
config_get server "$section" server
config_get cabundle "$section" cabundle
config_get renewal_days "$section" renewal_days
[ -n "$cabundle" ] && acme_args="$acme_args --ca-bundle $cabundle"
[ -n "$server" ] && acme_args="$acme_args --server $server"
[ -n "$renewal_days" ] && acme_args="$acme_args --days $renewal_days"
set -- $domains
main_domain=$1
Our first 3 custom lines (lines 5, 6 and 7 above) declare a local variable within the shell script. The next 3 lines (lines 8, 9 and 10 above) use OpenWrt’s functional API to get our custom parameters from the /etc/config/acme configuration file. The last 3 lines (lines 11, 12 and 13 above) append our custom configuration values to the command line parameters that will be handed over to acme.sh.
Test run
The only thing left to do is to test our modifications…
root@OpenWrt:~# /usr/lib/acme/run-acme acme: Running pre checks for openwrt.local. 4+0 records in 4+0 records out Generating a RSA private key ..........................+++++ ......................+++++ writing new private key to '/etc/acme/openwrt.local/openwrt.local.key.new' acme: Running ACME for openwrt.local acme: Using standalone mode Standalone mode. Create account key ok. Registering account Registered ACCOUNT_THUMBPRINT='2dYMwVSlI3Ewk69JJIpdhDa63uYdmoRiAWrBP64O8zg' Creating domain key The domain key is here: /etc/acme/openwrt.local/openwrt.local.key Single domain='openwrt.local' Getting domain auth token for each domain Getting webroot for domain='openwrt.local' Verifying: openwrt.local Standalone mode server Success Verify finished, start to sign. Lets finalize the order, Le_OrderFinalize: https://acme.local:8443/acme/weekly/order/mdApyrB1mSTDfrG0oJP97hCfo9MapD4g/finalize Download cert, Le_LinkCert: https://acme.local:8443/acme/weekly/certificate/zT0aO2NdTXhIJZXE8DsSGdw1d6dw0Pbs Cert success. -----BEGIN CERTIFICATE----- MIIFrDCCA5SgAwIBAgIQbFGdrtEr8eQq2McxMc2+ojANBgkqhkiG9w0BAQsFADCB ... << REDACTED >> ... DPnJoXMPK0WR2dkRHtpZDQ== -----END CERTIFICATE----- Your cert is in /etc/acme/openwrt.local/openwrt.local.cer Your cert key is in /etc/acme/openwrt.local/openwrt.local.key The intermediate CA cert is in /etc/acme/openwrt.local/ca.cer And the full chain certs is there: /etc/acme/openwrt.local/fullchain.cer acme: Running post checks (cleanup).
