In specific scenarios, having a personalized Certificate Authority (CA) is essential, especially when aiming for an optimal two-way SSL authentication setup in HAProxy.
Generate CA Key and Certificate
The journey to setting up a CA starts with its key creation:
openssl genrsa -out ca.key 4096
The subsequent step involves crafting the certificate. For our purposes, we’re choosing a self-signed variant:
openssl req -new -x509 -days 3650 -sha256 -key ca.key -out ca.crt -subj '/CN=root CA/O=Acme Corp'
Configure Your CA
Having secured our key and certificate, it’s time to delve into the CA configuration. Create a ca.conf
file, embedding it with the following details:
[ 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
An important security note to highlight: Using copy_extensions = copyall
can introduce security vulnerabilities, particularly if you sign Certificate Signing Requests (CSR’s) that you haven’t generated yourself. This approach is recommended only if you maintain full control over the CSR’s.
Proceed by initializing the certificate index file:
touch index.txt
Then, set up the serial database:
echo '01' > serial
Concluding the CA configuration, let’s generate the Certificate Revocation List (CRL):
openssl ca -config ca.conf -gencrl -keyfile ca.key -cert ca.crt -out crl.pem
At this point, the list remains empty as no certificates have been revoked. Ensure you relocate the ca.crt
and crl.pem
files to a location easily accessible by HAProxy, for example:
cp ca.crt /etc/haproxy/certs
cp crl.pem /etc/haproxy/certs
Create HAProxy Key and Certificate
Kick off by generating a private key:
openssl genrsa -out node.key 2048
Follow it up with 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 aforementioned CSR, we’ve designated that HAProxy will operate on a server node.example.com
(specified as the CN), while also maintaining a DNS entry proxy.example.com
.
Let’s bring our CA into play to sign this certificate:
openssl ca -batch -config ca.conf -notext -in node.csr -out node.crt
For added 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
Having acquired the HAProxy key and certificate, it’s time to compile a PEM file for HAProxy’s use:
openssl pkcs12 -in node.p12 -passin pass:mypassword -nodes -out haproxy.pem
Shift the PEM file to a directory for future HAProxy access:
cp haproxy.pem /etc/haproxy/certs
It’s important to note that our HAProxy certificate has been endorsed by our homemade CA. While this is vital for enabling two-way SSL authentication, it’s not a strict requirement. You can also opt for a certificate validated by a globally trusted CA. In this demonstration, however, we’re fully embracing our personal CA.
Launching HAProxy
Pen a file /etc/haproxy/haproxy.cfg
and populate it with:
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
To unleash the power of HAProxy with Docker, execute:
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
Constructing the Client Certificate
With HAProxy now actively exposing our WebDAV and Prometheus services, a client certificate is paramount for unhindered access.
Begin with the generation of a client key:
openssl genrsa -des3 -passout pass:myclientpw -out client.key 2048
Then, move to CSR creation:
openssl req -new -key client.key -passin pass:myclientpw -out client.csr -subj '/CN=My Client/O=Acme Corp'
Endorse the CSR to yield a certificate:
openssl ca -batch -config ca.conf -notext -in client.csr -out client.crt
For efficient distribution, compile the elements into a PKCS12 key store:
openssl pkcs12 -export -chain -CAfile ca.crt -in client.crt -inkey client.key -passin pass:myclientpw -passout pass:myclientpw > client.p12
Finally, integrate this key store into your browser’s certificate chain, ensuring effortless access to both WebDAV and Prometheus.
Feel free to contact me for any help.