Install HAProxy & CertBot (LetsEncrypt)
HAProxy
Install HAProxy
Install HAProxy v2.9 with the following commands:
sudo add-apt-repository -y ppa:vbernat/haproxy-2.9
sudo apt update
sudo apt install -y haproxy
Install WhoIs for Basic Password Protection of Statistics Page (Optional)
Install WhoIs (which includes the mkpasswd utility) with the following command:
sudo apt install whois
Generate an encrypted password for the config file for accessing the statistics with the following command:
echo <password> | mkpasswd --stdin --method=sha-256
Configure HAProxy
Edit the HAProxy configuration with this command:
sudo nano /etc/haproxy/haproxy.cfg
Inside the global section, add the following lines to the end of the section to allow easier loading of certificates and to force stopping the old process on a reload (which will drop all long-standing connections):
ssl-load-extra-files all
ssl-load-extra-del-ext
# This forces open connections to close after reloading HAProxy - Use with caution in production
hard-stop-after 30s
Leave the defaults section as they are but add a frontend for stats and normal web access:
userlist StatsUsers
user <username> password <salted_password>
frontend http_fe
bind *:80
bind *:443 ssl crt /etc/ssl/private
# Set some default forwarded headers
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request add-header X-Forwarded-Host %[req.hdr(Host)]
# Capture additional/longer info for logs
http-request capture req.hdr(Host) len 30
capture request header User-Agent len 200
capture request header Referer len 800
capture request header X-Forwarded-For len 20
# Test URI to see if it's a LetsEncrypt request
acl letsencrypt_acl path_beg /.well-known/acme-challenge/
# Local Stats Authorization
acl stats_acl path_beg /hastats
acl stats_auth http_auth(StatsUsers)
http-request auth realm Stats if stats_acl !stats_auth
# Common Redirects
acl <redirect_acl_name> hdr_beg(host) -i <domain_prefix>
http-request redirect location <url> if <redirect_acl_name>
# The following redirect for non-https traffic breaks if used with Cloudflare Flexible Encryption
redirect scheme https code 301 if !{ ssl_fc }
# Local Stats requests
use_backend stats_be if stats_acl stats_auth
# LetsEncrypt Requests
use_backend certbot_be if letsencrypt_acl
# <Name of website or any comment - Create these lines for every domain/backend>
acl <acl_name> hdr_beg(host) -i <domain_prefix>.
use_backend <backend_name> if <acl_name>
# Default backend if no ACL rules are matched (Can be deleted for no default)
default_backend <default_backend_name>
# <Name of the website or any comment - Create this section for every domain/backend>
backend <backend_name>
balance roundrobin
option httpchk
http-check send meth HEAD uri /
default-server check maxconn 20
server <server_name> <server_ip>:<server_port>
# Local stats
backend stats_be
stats enable
stats uri /hastats
stats refresh 10s
stats show-modules
# Certbot for LetsEncrypt
backend certbot_be
server certbot 127.0.0.1:8888
Here's an example using an Ignition Server running on 192.168.1.10
using a domain name beginning with scada.
for remote access. This also enables HAProxy stats on any domain name using the path /hastats using a login of hastats
and a password of password
:
userlist StatsUsers
user hastats password $5$cQEGcl6Wb$nG3i246htOnuW5LTYo3O73aa7ve7i.ePiQ4v6EoMW44
frontend http_fe
bind *:80
bind *:443 ssl crt /etc/ssl/private
# Set some default forwarded headers
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request add-header X-Forwarded-Host %[req.hdr(Host)]
# Capture additional/longer info for logs
http-request capture req.hdr(Host) len 30
capture request header User-Agent len 200
capture request header Referer len 800
capture request header X-Forwarded-For len 20
# Test URI to see if it's a LetsEncrypt request
acl letsencrypt_acl path_beg /.well-known/acme-challenge/
# Local Stats Authrization
acl stats_acl path_beg /hastats
acl stats_auth http_auth(StatsUsers)
http-request auth realm Stats if stats_acl !stats_auth
# Ignition Perspective Redirect to Project URL
acl scada hdr_beg(host) -i <redirected-subdomain>.
http-request redirect location https://<subdomain>.<domain>.com/data/perspective/client/<ProjectName> if scada
# The following redirect for non-https traffic breaks if used with Cloudflare Flexible Encryption
redirect scheme https code 301 if !{ ssl_fc }
# Local Stats requests
use_backend stats_be if stats_acl stats_auth
# LetsEncrypt requests
use_backend certbot_be if letsencrypt_acl
# Ignition
acl ignition hdr_beg(host) -i <subdomain>.
use_backend ignition_be if ignition
# Ignition
backend ignition_be
balance roundrobin
option httpchk
http-check send meth HEAD uri / ver HTTP/1.1 hdr host 127.0.0.1
http-request set-header Connection "Upgrade"
default-server check maxconn 1000
server iguana 192.168.1.10:8088 check inter 10s
# Local stats
backend stats_be
stats enable
stats uri /hastats
stats refresh 10s
stats show-modules
# Certbot for LetsEncrypt
backend certbot_be
server certbot 127.0.0.1:8888
For a very simple HAProxy in front of a local Ignition Edge server, use the following config:
userlist StatsUsers
user hastats password $5$cQEGcl6Wb$nG3i246htOnuW5LTYo3O73aa7ve7i.ePiQ4v6EoMW44
frontend http_fe
bind *:80
# Set some default forwarded headers
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request add-header X-Forwarded-Host %[req.hdr(Host)]
# Capture additional/longer info for logs
http-request capture req.hdr(Host) len 30
capture request header User-Agent len 200
capture request header Referer len 800
capture request header X-Forwarded-For len 20
# Local Stats Authrization
acl stats_acl path_beg /hastats
acl stats_auth http_auth(StatsUsers)
http-request auth realm Stats if stats_acl !stats_auth
# Local Stats requests
use_backend stats_be if stats_acl stats_auth
# Ignition
use_backend ignition_be
# Ignition
backend ignition_be
balance roundrobin
option httpchk
http-check send meth HEAD uri / ver HTTP/1.1 hdr host 127.0.0.1
http-request set-header Connection "Upgrade"
default-server check maxconn 1000
server ignition 127.0.0.1:8088 check inter 10s
# Local stats
backend stats_be
stats enable
stats uri /hastats
stats refresh 10s
stats show-modules
Any time you modify the configuration for HAProxy, you can check the configuration for errors with the following command:
sudo haproxy -f /etc/haproxy/haproxy.cfg -c
If the configuration file is valid (warnings are OK) you can reload HAProxy with the following command:
sudo service haproxy reload
Upgrade HAProxy
If you ever need to upgrade the version to a newer version, check the following site to see if the newer version exists:
https://launchpad.net/~vbernat/+ppa-packages
If it does, use the above commands substituting in the appropriate version to add the newer version's repository and upgrade HAProxy. Once HAProxy is upgraded and verified working, use the following command to remove the old version (again substituting the old version number):
sudo add-apt-repository --remove ppa:vbernat/haproxy-2.8
Certbot (LetsEncrypt)
Install CertBot (LetsEncrypt)
Install Certbot with the following commands:
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo snap set certbot trust-plugin-with-root=ok
Create a new certificate using HTTP authentication
We'll need to get a new certificate for the domain name we want using the following command:
sudo certbot certonly --standalone -d <domain_name> --non-interactive --agree-tos --email <admin_email> --http-01-port=8888 --preferred-chain "ISRG Root X1"
If this fails, you'll need to comment out the following 2 lines in your HAProxy configuration so that it only listens on port 80 and not on port 443:
# bind *:443 ssl crt /etc/ssl/private
# redirect scheme https code 301 if !{ ssl_fc }
Don't forget to reload HAProxy after making this change and once you've created the certificate, you can un-comment these lines and reload HAProxy again.
Create a new certificate using DNS authentication
A list of DNS services that are supported along with the plugin names can be found here: https://certbot.eff.org/docs/using.html#dns-plugins
We'll need to install a plugin for the DNS service we will be using with the following command:
sudo snap install certbot-dns-<plugin>
Next you'll create your credentials file using the following command and putting the credentials needed as documented in the plugin documentation link from above:
sudo nano /root/.secrets/certbot/<dns_service_name>.ini
Next you'll make these credentials read-only for the root user so that any other user cannot see them:
sudo chmod 0400 /root/.secrets/certbot/<dns_service_name>.ini
Now we'll get our new certificate for the domain name we want using the following command (adjust as necessary according to the plugin documentation in the link above):
sudo certbot certonly --non-interactive --agree-tos --email <admin_email> --dns-<dns_service_name> --dns-<dns_service_name>-credentials /root/.secrets/certbot/<dns_service_name>.ini -d <domain> --preferred-chain "ISRG Root X1"
If wanting a wildcard certificate, you'll need to use the following command instead (adjust as necessary according to the plugin documentation in the link above):
sudo certbot certonly --non-interactive --agree-tos --email <admin_email> --dns-<dns_service_name> --dns-<dns_service_name>-credentials /root/.secrets/certbot/<dns_service_name>.ini -d <domain> -d *.<domain> --preferred-chain "ISRG Root X1"
Preparing the certificate for HAProxy
To prepare the certificate in a format HAProxy can use, we'll create symbolic links to the 2 files using the following commands:
sudo ln -s /etc/letsencrypt/live/<domain>/fullchain.pem /etc/ssl/private/<domain>.crt
sudo ln -s /etc/letsencrypt/live/<domain>/privkey.pem /etc/ssl/private/<domain>.key
Schedule auto-renewal of certificates
We'll create a script which we'll run on a schedule to renew the certificate:
sudo nano /opt/update-certs.sh
Put the following text (update as needed) in the file:
#!/usr/bin/env bash
# Renew the certificate using TLS authentication on prot 8888 (comment out this line if using DNS authentication)
certbot renew --force-renewal --https-port=8888 --preferred-chain "ISRG Root X1"
# Renew the certificate using DNS authentication (uncomment if using DNS authentication)
#certbot renew --force-renewal --preferred-chain "ISRG Root X1"
# Reload HAProxy
service haproxy reload
We'll edit the certbot cron scheduler file to schedule this script:
sudo nano /etc/cron.d/certbot
This schedule will be used to run our renewal script on the 1st day of the month every month at midnight (add it to a new line of the file and comment out the existing schedule using a # sign):
0 0 1 * * root bash /opt/update-certs.sh
Removing or Cancelling Renewing of Certificates
If you ever need to stop renewing a certificate that is no longer used, you can do so with the following command:
sudo certbot delete --cert-name <domain>
No Comments