ACME client configuration

Trying to configure the new ACME client in HAProxy 3.2.1, but not getting haproxy.cfg to validate.

The relevant documentation is here: HAProxy version 3.2.1-2 - Configuration Manual and here is the announcement: Announcing HAProxy 3.2

With the HAProxy’s ACME client I’m trying to replace acme.sh via which I install certificates like bellow, so I get the private key as a file with .key extension:

./acme.sh --install-cert -d yourdomain.tld --ecc \
--fullchain-file /etc/haproxy/certs/yourdomain.tld.pem.ecdsa \
--key-file       /etc/haproxy/certs/yourdomain.tld.pem.ecdsa.key

That way I obtain both ECC and RSA cert for the site to serve them in a so called dual cert way with HAProxy:

# ls -lha /etc/haproxy/certs/
...
drwxr-xr-x 3 1000 1000 4.0K Jun 15 11:29 .
drwxr-xr-x 4 root root 4.0K Jun 15 10:13 ..
-rw-r--r-- 1 1000 1000 4.4K Jun 14 10:34 yourdomain.tld.pem.ecdsa
-rw------- 1 1000 1000  288 Jun 14 10:34 yourdomain.tld.pem.ecdsa.key
-rw-r--r-- 1 1000 1000 7.2K Jun 14 10:31 yourdomain.tld.pem.rsa
-rw------- 1 1000 1000 3.2K Jun 14 10:31 yourdomain.tld.pem.rsa.key

Now I’d like to renew these two certs with HAProxy. Here’s what how I’ve tried to configure HAProxy to use it’s ACME client:

global
    log /dev/log local0
    stats socket /var/run/haproxy.sock mode 660 expose-fd listeners level admin
    ...
    expose-experimental-directives
    httpclient.resolvers.prefer ipv4

defaults
    ...

acme le_rsa
    directory https://acme-staging-v02.api.letsencrypt.org/directory
    account-key /etc/haproxy/certs/letsencrypt.account.key
    contact john.doe@example.com
    challenge HTTP-01
    keytype RSA
    bits 4096
    map virt@acme

acme le_ecdsa
    directory https://acme-staging-v02.api.letsencrypt.org/directory
    account-key /etc/haproxy/certs/letsencrypt.account.key
    contact john.doe@example.com
    challenge HTTP-01
    keytype ECDSA
    curves P-384
    map virt@acme

crt-store cert_files
     crt-base /etc/haproxy/certs/
     key-base /etc/haproxy/certs/
     load crt yourdomain.tld.pem.rsa key yourdomain.tld.pem.rsa.key alias site_rsa acme le_rsa domains yourdomain.tld,www.yourdomain.tld,new.yourdomain.tld,dev.yourdomain.tld,blog.yourdomain.tld
     load crt yourdomain.tld.pem.ecdsa key yourdomain.tld.pem.ecdsa.key alias site_ecdsa acme le_ecdsa domains yourdomain.tld,www.yourdomain.tld,new.yourdomain.tld,dev.yourdomain.tld,blog.yourdomain.tld

frontend web-https
    bind :80
    bind :443 ssl alpn h2,http/1.1 npn h2,http/1.1
    bind quic4@:443 ssl alpn h3
    bind quic6@:443 ssl alpn h3
    http-request return status 200 content-type text/plain lf-string "%[path,field(-1,/)].%[path,field(-1,/),map(virt@acme)]\n" if { path_beg '/.well-known/acme-challenge/' }
    ssl-f-use crt "@cert_files/site_rsa" acme le_rsa
    ssl-f-use crt "@cert_files/site_ecdsa" acme le_ecdsa
    http-request redirect scheme https if !{ ssl_fc }
    ...

When I specify key options on load crt lines I get the configuration error:

 # haproxy -c -V -f /etc/haproxy/haproxy.cfg
[NOTICE]   (733) : haproxy version is 3.2.1-f4d1a4e
[NOTICE]   (733) : path to executable is /usr/local/sbin/haproxy
[ALERT]    (733) : config : '@cert_files/site_ecdsa' in crt-list '/etc/haproxy/haproxy.cfg' line 42, is already defined with incompatible parameters:
 - different parameter 'key' : previously 'yourdomain.tld.pem.ecdsa.key' vs '(null)'
.

When I remove those key options I get a different configuration error:

# haproxy -c -V -f /etc/haproxy/haproxy.cfg
[NOTICE]   (728) : haproxy version is 3.2.1-f4d1a4e
[NOTICE]   (728) : path to executable is /usr/local/sbin/haproxy
[ALERT]    (728) : config : error processing line 0 in file '@web-https' : unable to load SSL private key into SSL Context '@cert_files/site_ecdsa': passed a null parameter.
.

Since the config isn’t valid it’s not possible to renew the cert manually:

# echo "acme renew @cert_files/site_ecdsa" | socat stdio /var/run/haproxy.sock
No ACME configuration defined for file '@cert_files/site_ecdsa'.

Can anybody see what is going wrong?

Thank you.

If I understand your question right we have face a similar issue in the past.
The certificate format from - in our case, using certbot - letsencrypt (crt and key separated files) isn’t the same the haproxy is expecting (crt and key on the same file).

So we append a renew_hook just like this to the letsencrypt renewal .conf file:

renew_hook = /usr/bin/cat /etc/letsencrypt/live/example.com/privkey.pem /etc/letsencrypt/live/example.com/fullchain.pem > /etc/haproxy/ssl/example.com.pem && sleep 20 && /usr/bin/systemctl reload haproxy

Smoothly working for years! :slight_smile:

Hello,

That kind of concatenation of privkey and fullchain cert files used to be necessary for a long time with haproxy. Nowadays haproxy automatically recognizes separate privkey and full chain files even without being too specific about those files. Haproxy can even be pointed at certs folder and figure out the certs automatically.

The post here is about the new experimental ACME client in haproxy 3.2 that is supposed to renew privkey and fullchain such that haproxy essentially replaces certbot, acme.sh and the likes.