Custom CA for SSL Certificates

Hi, I'd like to connect to my custom CA (Step-CA) for SSL certificates using the ACME protocol. For this, I am looking for documentation for the acme/uacme package configuration options. Is there any?

(At least uacme supports alternate CAs but what I have gleaned from the scripts that option is not exposed by UCI at the moment.)

Out of the box, ACME in OpenWrt will not talk to your local ACME Server (i.e., StepCA). You can change it by amending the script under /usr/lib/acme/run-acme. Find the subroutine called issue_cert(). In 19.07 it looked like below:

issue_cert()
{
    local section="$1"
    ...

In that subroutine you need to add about 9 lines:

    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"

With these 9 lines you're extending the acme_args with content from your etc/config/acme config file. Make sure you add those 9 lines before the acme_args are being used. I put mine right after this line:

    [ "$DEBUG" -eq "1" ] && acme_args="$acme_args --debug"

Now, open the /etc/config/acme config file and set enabled to 1. At the same time, set the 3 new options you parsed into acme_args with your 9 lines above:

        option enabled		'1'
        option server		'https://your.local.acme.server.url:8443/acme/weekly/directory'
        option cabundle		'/etc/ssl/certs/yourcabundle.pem'
        option renewal_days	'3'

The ACME package sets up a cron job (/etc/crontab/root) that runs every midnight. In case it did already run, please delete all contents underneath /etc/acme/ and re-run the script:

root@OpenWrt:~# rm -rf /etc/acme/*
root@OpenWrt:~# /usr/lib/acme/run-acme 

Now, change the config at /etc/config/uhttpd so that redirection of all HTTP traffic to HTTPS is enabled and so that uhttpd listens to port 443:

        option redirect_https   '1'
        option listen_https     '0.0.0.0:443'

Finally, reload the uhttpd daemon:

root@OpenWrt:~# /etc/init.d/uhttpd reload
1 Like

Thank you. Since I wanted to use TLS-ALPN-01, I've instead modified the run-uacme script of the uacme package in the following way:

--- run-uacme.orig	2023-05-13 17:40:15.000000000 +0200
+++ run-uacme	2023-07-18 21:33:08.000000000 +0200
@@ -38,6 +38,8 @@
 UPDATE_UHTTPD=0
 UPDATE_HAPROXY=0
 USER_CLEANUP=
+ACME_URL=
+ACME_STAGING_URL=
 
 . /lib/functions.sh
 
@@ -213,6 +215,7 @@
     local failed_dir
     local webroot
     local dns
+    local tls
     local user_setup
     local user_cleanup
     local ret
@@ -230,6 +233,7 @@
     config_get keylength "$section" keylength
     config_get webroot "$section" webroot
     config_get dns "$section" dns
+    config_get tls "$section" tls
     config_get user_setup "$section" user_setup
     config_get user_cleanup "$section" user_cleanup
 
@@ -242,15 +246,26 @@
 
     if [ "$APP" = "uacme" ]; then
 	[ "$DEBUG" -eq "1" ] && debug="--verbose --verbose"
+	[ "$tls" -eq "1" ] && HPROGRAM=/usr/share/uacme/ualpn.sh                                      
     elif [ "$APP" = "acme" ]; then
 	[ "$DEBUG" -eq "1" ] && acme_args="$acme_args --debug"
     fi
     if [ "$use_staging" -eq "1" ]; then
 	STATE_DIR="$STAGING_STATE_DIR";
+
+	# Check if we should use a custom stagin URL
+        if [ "$APP" = "uacme" -a -n "$ACME_STAGING_URL" ]; then
+        	ACME="$ACME --acme-url $ACME_STAGING_URL"
+	else
 	staging="--staging";
+	fi
     else
 	STATE_DIR="$PRODUCTION_STATE_DIR";
 	staging="";
+
+	if [ "$APP" = "uacme" -a -n "$ACME_URL" ]; then
+		ACME="$ACME --acme-url $ACME_URL"   
+	fi                                                   
     fi
 
     set -- $domains
@@ -260,7 +275,7 @@
 	log "Running user-provided setup script from $user_setup."
 	"$user_setup" "$main_domain" || return 2
     else
-	[ -n "$webroot" ] || [ -n "$dns" ] || pre_checks "$main_domain" || return 2
+	[ -n "$webroot" ] || [ -n "$dns" ] || [ -n "$tls" ] || pre_checks "$main_domain" || return 2
     fi
 
     log "Running $APP for $main_domain"
@@ -269,6 +284,7 @@
 	if [ ! -f  "$STATE_DIR/private/key.pem" ]; then
 	    log "Create a new ACME account with email $ACCOUNT_EMAIL use staging=$use_staging"
 	    $ACME $debug --confdir "$STATE_DIR" $staging --yes new $ACCOUNT_EMAIL
+            log "DONE WITH USER"
 	fi
 
 	if [ -f "$STATE_DIR/$main_domain/cert.pem" ]; then
@@ -315,6 +331,13 @@
 	    return 2
 #	    uacme_args="$uacme_args --dns $dns"
 	fi
+    elif [ -n "$tls" ]; then
+        if [ "$APP" = "uacme" ]; then                   
+            log "Using TLS mode"
+        else       
+            log "TLS not supported by $APP"
+            return 2                 
+        fi
     elif [ -z "$webroot" ]; then
 	if [ "$APP" = "acme" ]; then
 	    log "Using standalone mode"
@@ -479,6 +502,8 @@
     STAGING_STATE_DIR=$PRODUCTION_STATE_DIR/staging
     ACCOUNT_EMAIL=$(config_get "$section" account_email)
     DEBUG=$(config_get "$section" debug)
+    ACME_URL=$(config_get "$section" acme_url)
+    ACME_STAGING_URL=$(config_get "$section" acme_staging_url)
 }
 
 if [ -z "$INCLUDE_ONLY" ]; then

These modifications assume the following extra keys in the acme section:

    option acme_url <CA_DIRECTORY_URL>
    option acme_staging_url <CA_DIRECTORY_URL>

as well as an additional key in the specific certifcate section:

    option tls 1

to enable TLS-ALPN-01.

This is not really ideal, I think the challenge type should be an explicit key combining all the different modes, but I didn't want to change the OpenWrt default too much.

In addition to those changes, I've also added a ualpn to rc.local and set up uhttpd to only listen on the localhost interface:

ualpn -c 127.0.0.1@443 -d -b <my_lan_ip>@443 -P 0

The important part is -P 0 to disable the proxy protocol, uhttpd does not like it.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.