Introduction
Letsencrypt allows you to get SSL certificates for your domain for free. Here I'll show you how to obtain such a certificate using the acme client and use it in haproxy.
Install acme client
The Acme client is used to request a ssl certificate from letsencrypt.
# installs acme client to ~/.acme.sh
curl -s https://get.acme.sh | sh
# creates symbolic link to acme client make it accessible
ln -s ~/.acme.sh/acme.sh /usr/local/bin/acme
Prepare acme and haproxy
Create folder for your certificates
mkdir /etc/ssl/acme
Add certificate path to haproxy.cfg
...
frontend http-in
...
bind *:443 ssl crt /etc/ssl/acme
Register letsencrypt account
acme --register-account
In the output you'll see the ACCOUNT_THUMBPRINT. You will need that later to verify your domain.
Install cronjob to revalidate your certificates periodically
acme --install-cronjob
Script for creating certificate in haproxy compatible format
Haproxy requires certificates in a special format. They have to contain the full chain as well as the key. Therefore we create a script, which will be executed after issuing a certificate to create these files.
Add the file /usr/local/bin/create_cert with the following content:
#!/bin/sh
cat $LE_WORKING_DIR/$1/fullchain.cer $LE_WORKING_DIR/$1/$1.key > /etc/ssl/acme/$1.pem;
Make it executable:
chmod +x /usr/local/bin/create_cert
Create a service which verifies your domains
Method 1: With docker container
I already created a docker image, which includes the script below. You can start it via:
docker run -d -e TOKEN='insert the ACCOUNT_THUMBPRINT here' --name ssl_verify -p '9090:8080' mightyplow/stateless-ssl-verify
Method 2: Build it yourself
I use nodejs here to create that service.
Create the file /usr/local/bin/verify_domain with the following content:
#!/usr/bin/env node
const token = [insert the ACCOUNT_THUMBPRINT here];
const port = 9090;
const http = require('http');
const path = require('path');
const server = http.createServer((req, res) => {
const requestToken = path.basename(req.url);
res.end(requestToken + '.' + token);
});
server.listen(port);
Make the script executable
chmod +x /usr/local/bin/verify_domain
Start the service in background
verify_domain &
Add the route to the verification service to your haproxy.cfg
frontend http-in
...
acl host_verify path_beg -i /.well-known/
...
use_backend verify-ssl if host_verify
...
backend verify-ssl
server node1 localhost:9090
Reload the haproxy config:
systemctl reload haproxy
Create and install certificate for domain
acme --issue --standalone -d [DOMAIN] --httpport [ANY_FREE_PORT] --post-hook "create_cert [DOMAIN]" --reloadcmd "systemctl reload haproxy"
The port is required when you're running another service on port 80, which is usually a web server or the haproxy. You can choose any free port.
Done
That's it. For any new domain, you can repeat the step for creating and installing the certificate.