In some situations it is useful to set up your own Certificate Authority (CA) for signing certificates that HAProxy will use for two-way SSL authentication.
Generate CA Key and Certificate
First step to creating a CA is to create its key:
openssl genrsa -out ca.key 4096
Next we create the certificate. We will create a self-singed certificate:
openssl req -new -x509 -days 3650 -sha256 -key ca.key -out ca.crt -subj '/CN=root CA/O=Acme Corp'
Configure CA
Now that we have our key and certificate we can create a CA configuration file. Create a ca.conf
file with the following content:
[ ca ]
default_ca = acme_ca
[ crl_ext ]
authorityKeyIdentifier=keyid:always
[ acme_ca ]
DIR = ./ # Default directory.
unique_subject = no
certificate = ${DIR}/ca.crt
database = ${DIR}/index.txt
new_certs_dir = ${DIR}/
private_key = ${DIR}/ca.key
serial = ${DIR}/serial
default_md = sha256
name_opt = ca_default
cert_opt = ca_default
copy_extensions = copyall
default_days = 730
default_crl_days = 730
policy = acme_policy
[ acme_policy ]
C= optional # Country
ST= optional # State or province
L= optional # Locality
O= supplied # Organization
OU= optional # Organizational unit
CN= supplied # Common name
Note that we will use copy_ext<span id="rmm">e</span>nsions = copyall
. This carries security risks if you will sign certificates requests (CSR’s) that you did not generate yourself! You should probably only use this if you also have full control over the CSR’s that will be signed.
Next create the certificate index file:
touch index.txt
And create the serial database:
echo '01' > serial
Finally, create the Certificate Revocation List (CRL):
openssl ca -config ca.conf -gencrl -keyfile ca.key -cert ca.crt -out crl.pem
This list is obviously “empty” for now as we haven’t revoked any certificates yet.
Copy over the ca.crt
and crl.pem
files to a location where HAProxy can get it later, e.g.:
cp ca.crt /etc/haproxy/certs
cp crl.pem /etc/haproxy/certs
Create HAProxy Key and Certificate
Create a private key:
openssl genrsa -out node.key 2048
Create a CSR:
openssl req -new -key node.key -out node.csr -subj '/CN=node.example.com/O=Acme' -addext 'subjectAltName = DNS:proxy,DNS:proxy.example.com'
In the above CSR we define that HAProxy will run on a server node.example.com
(the CN), and that it will also have a DNS entry proxy.example.com
.
Let’s now sign this certificate with our CA:
openssl ca -batch -config ca.conf -notext -in node.csr -out node.crt
For convenience package the key and certificate into a PKCS12 key store:
openssl pkcs12 -export -chain -CAfile ca.crt -in node.crt -inkey node.key -passout pass:mypassword > node.p12
Now that we have our HAProxy key and certificate, create a PEM file HAProxy can use:
openssl pkcs12 -in node.p12 -passin pass:mypassword -nodes -out haproxy.pem
Now copy over the PEM file we created to a location where HAProxy can access it later, e.g.:
cp haproxy.pem /etc/haproxy/certs
Worth noting here is that we have signed the HAProxy certificate with our own CA, but this is actually not required for enabling two-way SSL authentication. You can just as well use a certificate signed by a proper globally trusted CA instead. In this case though, we just use our own CA.
Run HAProxy
Create a file /etc/haproxy/haproxy.cfg
with the following content:
global
log 127.0.0.1 local0 info
spread-checks 5
max-spread-checks 15000
maxconn 50000
tune.ssl.default-dh-param 2048
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:!aNULL:!MD5:!DSS
ssl-default-bind-options no-sslv3 no-tlsv10 no-tls-tickets
ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:!aNULL:!MD5:!DSS
ssl-default-server-options no-sslv3 no-tlsv10 no-tls-tickets
defaults
log global
retries 3
backlog 10000
maxconn 10000
timeout connect 3s
timeout client 30s
timeout server 30s
timeout tunnel 3600s
timeout http-keep-alive 1s
timeout http-request 15s
timeout queue 30s
timeout tarpit 60s
option dontlognull
option http-server-close
option redispatch
frontend http_in
bind *:80
mode http
redirect scheme https code 301
frontend https_in
bind *:443 ssl crt /etc/certs/haproxy.pem ca-file /etc/certs/ca.crt verify required crl-file /etc/certs/crl.pem
mode http
use_backend webdav if { path -i -m beg /shared }
use_backend prometheus if { path -i -m beg /prometheus }
backend prometheus
mode http
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
reqirep "^([^ :]*)\ /prometheus/?(.*)" "\1\ /\2"
server prometheus prometheus:9090
backend webdav
mode http
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
server webdav webdav:80
Run the HAProxy docker image with the above configuration and certificate files:
docker run -d -p 80:80 -p 443:443 \
-v /etc/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg \
-v /etc/haproxy/certs:/etc/certs \
haproxy:2.0-alpine
Create Client Certificate
Now that we have HAProxy up and running exposing our WebDAV and prometheus services, let’s create a client certificate so we can access it.
First create a client key:
openssl genrsa -des3 -passout pass:myclientpw -out client.key 2048
Next create the CSR:
openssl req -new -key client.key -passin pass:myclientpw -out client.csr -subj '/CN=My Client/O=Acme Corp'
Sign this CSR so we generate a certificate:
openssl ca -batch -config ca.conf -notext -in client.csr -out client.crt
Create a PKCS12 key store so things are easier to distribute:
openssl pkcs12 -export -chain -CAfile ca.crt -in client.crt -inkey client.key -passin pass:myclientpw -passout pass:myclientpw > client.p12
Now you can import this key store into your browser’s certificate key chain and access both WebDAV and Prometheus.