Travis Tran

Travis Tran

Keep Doing Better

Two-Way SSL with HAProxy

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_extensions = 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.

X
Glad to have you at travistran.me
Welcome to TravisBot
wpChatIcon