Using HAProxy Docker with acmetool installed on Docker Host

Install acmetool and configure it as a redirector on port 80 of the host machine.

acmetool quickstart
# choose redirector option
# enable redirector service
# enable renew cron job

Modify the acme-tool defaults to force generation of HAProxy files

$ vi /etc/default/acme-reload
# Space separated list of services to restart after certificates are changed.
# By default, this is a list of common webservers like apache2, nginx, haproxy,
# etc. You can append to this list or replace it entirely.
SERVICES="$SERVICES"
HAPROXY_ALWAYS_GENERATE=yes

Run HAProxy docker container and link it to /var/lib/acme. I have also linked /data/haproxy where I keep my maintenance.http files and a custom haproxy.cfg file.

docker run --name haproxy --net mynetwork \
  -v /var/lib/acme:/var/lib/acme \
  -v /data/haproxy:/data/haproxy \
  -v /data/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro \
  -p 443:443 \
  --restart=always -d haproxy

Update the haproxy.cfg file with path to acmetool ssl certs use an appropriate SSL config generator: https://mozilla.github.io/server-side-tls/ssl-config-generator/

global
    # get default parameters to the modern configuration 
    # using https://mozilla.github.io/server-side-tls/ssl-config-generator/

frontend https-in
  mode http
  bind *:443 ssl crt /var/lib/acme/haproxy/

  # HSTS (15768000 seconds = 6 months)
  http-response set-header Strict-Transport-Security max-age=15768000

  reqadd X-Forwarded-Proto:\ https
  acl is_domain_one hdr_end(host) -i domainone.com.au
  acl is_domain_two hdr_end(host) -i domaintwo.com.au
  use_backend domain_one if is_domain_one
  use_backend domain_two if is_domain_two

backend domain_one
  redirect scheme https if !{ ssl_fc }
  option forwardfor
  option http-server-close
  option httpchk
  server domainone container-one:8085 maxconn 50
  errorfile 500 /data/haproxy/errorfiles/serverdown.http
  errorfile 503 /data/haproxy/errorfiles/maintenance.http

backend domain_two
  redirect scheme https if !{ ssl_fc }
  option forwardfor
  option http-server-close
  option httpchk
  balance source
  mode http
  server domainotwo container-two:8085 maxconn 50
  errorfile 500 /data/haproxy/errorfiles/serverdown.http
  errorfile 503 /data/haproxy/errorfiles/maintenance.http

Last add a custom hook to cause haproxy docker container to reload its config (still under testing).

$ vi /var/lib/acme/hooks/reload-haproxy-docker
#!/bin/sh

# This file reloads haproxy docker when the preferred certificate for a hostname
# changes. By default it assumes your docker container name is haproxy. 
#
# Configuration options:
#   /etc/{default,conf.d}/acme-reload
#     Sourced if they exist. Specify variables here.
#     Please note that most of the time, you don't need to specify anything.
#
#   $SERVICES
#     Space-separated list of daemons to reload.
#     Append with CONTAINERS="$CONTAINERS haproxy"

###############################################################################
set -e
EVENT_NAME="$1"
[ "$EVENT_NAME" = "live-updated" ] || exit 42

CONTAINERS="haproxy"
[ -e "/etc/default/acme-reload" ] && . /etc/default/acme-reload
[ -e "/etc/conf.d/acme-reload" ] && . /etc/conf.d/acme-reload
[ -z "$ACME_STATE_DIR" ] && ACME_STATE_DIR="/var/lib/acme"

for x in $CONTAINERS; do
  docker kill --signal=HUP "$x"
done

Leave a Reply