aethercert
Documentation

Replace an SSL Certificate on HAProxy Without Downtime

Swap an HAProxy PEM bundle without downtime: build and check it, reload or hot-update it through the Runtime API, keep disk and memory in sync, verify SNI and roll back.

HAProxy usually loads one PEM file per certificate that contains the server certificate, the intermediate certificates and the private key. A replacement becomes active either with a reload or - without any reload - through the Runtime API.

Before you begin

  • Find the certificate paths HAProxy uses: the crt arguments of the bind ... ssl lines in haproxy.cfg. A crt can name a single file, a directory (every file in it is loaded) or a crt-list file (a list of certificate files with per-SNI options).
  • Back up the current file: cp -p /etc/haproxy/certs/site.pem /root/site.pem.bak.
  • For the Runtime API route: the admin socket must be enabled with admin level in the global section, for example stats socket /run/haproxy/admin.sock mode 660 level admin, and socat must be installed.

Build and check the bundle

cat leaf.pem intermediates.pem private.key > site.pem
chmod 0600 site.pem
openssl x509 -in site.pem -noout -subject -enddate
openssl pkey -in private.key -pubout | sha256sum
openssl x509 -in leaf.pem -noout -pubkey | sha256sum

The order is server certificate, intermediates, private key. The two hashes must match.

Option 1: replace the file and reload

  1. Install the new file next to the old one and move it into place (a rename on the same file system replaces it in one step):

    install -m 0600 site.pem /etc/haproxy/certs/site.pem.new
    mv /etc/haproxy/certs/site.pem.new /etc/haproxy/certs/site.pem
  2. Check the configuration - this also loads and checks the new certificate - then reload:

    haproxy -c -f /etc/haproxy/haproxy.cfg && systemctl reload haproxy

    With the usual systemd setup (master-worker mode), a reload starts new workers with the new certificate while the old ones finish their existing connections.

For a certificate directory or crt-list, replace exactly the file of the certificate you are renewing and keep its file name, so the SNI selection and crt-list options stay the same.

Option 2: hot-update through the Runtime API

set ssl cert opens a transaction with the new content, commit ssl cert activates it, abort ssl cert cancels it. The path must be the one HAProxy loaded the certificate from:

printf 'set ssl cert /etc/haproxy/certs/site.pem <<\n%s\n\n' "$(cat site.pem)" | \
  socat stdio /run/haproxy/admin.sock
echo "commit ssl cert /etc/haproxy/certs/site.pem" | socat stdio /run/haproxy/admin.sock

Runtime API changes live only in memory. Also install the file on disk - step 1 of option 1, without the reload - or the next restart brings the old certificate back. A certificate file HAProxy has not loaded yet first needs a slot with new ssl cert and must be referenced by a bind line or crt-list.

Verify the new certificate

echo "show ssl cert /etc/haproxy/certs/site.pem" | socat stdio /run/haproxy/admin.sock
openssl s_client -connect lb.example.com:443 -servername app.example.com -showcerts </dev/null

show ssl cert prints the loaded certificate's subject, serial number and Not After date - it must be the new one. Then check over the network, with each hostname as -servername. With several HAProxy nodes, update and check each one.

Rollback

  • After a reload: move /root/site.pem.bak back into place, haproxy -c, reload.
  • After a Runtime API update: run the same set/commit with the backup file's content, and restore the file on disk too.

Common problems

  • The bundle lacks the private key, or the parts are in the wrong order.
  • The Runtime API was updated, but not the file on disk, so a restart brings back the old certificate.
  • A different certificate is selected by SNI from a directory or crt-list.
  • The admin socket is not enabled with level admin.

Automating certificate replacement with aethercert

The aethercert HAProxy target builds the combined PEM bundle. It can use set ssl cert and commit ssl cert through a Runtime API socket for a live update, or fall back to the configured reload command.

See Deploy targets for the fields this preset takes, what it needs on the host, and how far it has been verified.

Sources

On this page