HAProxy Setup for ThingsBoard
This guide walks you through installing HAProxy with Let’s Encrypt as HTTPS termination for ThingsBoard. Applicable when ThingsBoard is hosted in the cloud with a valid DNS name assigned to the instance.
Prerequisites
Section titled “Prerequisites”A server with a valid DNS name. Ports 80 (HTTP) and 443 (HTTPS) must be reachable from the internet.
Ubuntu 20.04, 22.04, or 24.04. No additional firewall configuration is required for most cloud environments.
RHEL/CentOS 8/9. Open the required ports:
sudo firewall-cmd --zone=public --add-port=80/tcp --permanentsudo firewall-cmd --zone=public --add-port=443/tcp --permanentsudo firewall-cmd --reloadStep 1. Connect via SSH
Section titled “Step 1. Connect via SSH”ssh -i <PRIVATE-KEY> ubuntu@<PUBLIC_DNS_NAME>ssh -i <PRIVATE-KEY> ec2-user@<PUBLIC_DNS_NAME>Consult your cloud vendor for other SSH connection options.
Step 2. Install HAProxy
Section titled “Step 2. Install HAProxy”sudo apt install --no-install-recommends software-properties-commonsudo add-apt-repository ppa:vbernat/haproxy-2.9 -ysudo apt-get updatesudo apt-get install haproxy=2.9.\* opensslsudo systemctl enable haproxysudo dnf -y install haproxysudo systemctl enable haproxyStep 3. Install Certbot
Section titled “Step 3. Install Certbot”sudo apt-get install ca-certificates certbotsudo dnf -y install ca-certificates certbotStep 4. Install default self-signed certificate
Section titled “Step 4. Install default self-signed certificate”Create the certificate generation script:
cat <<EOT | sudo tee /usr/bin/haproxy-default-cert#!/bin/sh
set -e
HA_PROXY_DIR=/usr/share/tb-haproxyCERTS_D_DIR=\${HA_PROXY_DIR}/certs.dTEMP_DIR=/tmp
PASSWORD=\$(openssl rand -base64 32)SUBJ="/C=US/ST=somewhere/L=someplace/O=haproxy/OU=haproxy/CN=haproxy.selfsigned.invalid"
KEY=\${TEMP_DIR}/haproxy_key.pemCERT=\${TEMP_DIR}/haproxy_cert.pemCSR=\${TEMP_DIR}/haproxy.csrDEFAULT_PEM=\${HA_PROXY_DIR}/default.pem
if [ ! -e \${HA_PROXY_DIR} ]; then mkdir -p \${HA_PROXY_DIR}fi
if [ ! -e \${CERTS_D_DIR} ]; then mkdir -p \${CERTS_D_DIR}fi
# Check if default.pem has been createdif [ ! -e \${DEFAULT_PEM} ]; then openssl genrsa -des3 -passout pass:\${PASSWORD} -out \${KEY} 2048 &> /dev/null sleep 1 openssl req -new -key \${KEY} -passin pass:\${PASSWORD} -out \${CSR} -subj \${SUBJ} &> /dev/null sleep 1 cp \${KEY} \${KEY}.org &> /dev/null openssl rsa -in \${KEY}.org -passin pass:\${PASSWORD} -out \${KEY} &> /dev/null sleep 1 openssl x509 -req -days 3650 -in \${CSR} -signkey \${KEY} -out \${CERT} &> /dev/null sleep 1 cat \${CERT} \${KEY} > \${DEFAULT_PEM} echo \${PASSWORD} > \${HA_PROXY_DIR}/password.txtfiEOTMake it executable and run it:
sudo chmod +x /usr/bin/haproxy-default-certtouch ~/.rndsudo haproxy-default-certStep 5. Configure HAProxy
Section titled “Step 5. Configure HAProxy”Create the HAProxy configuration file:
cat <<EOT | sudo tee /etc/haproxy/haproxy.cfg#HA Proxy Configglobal ulimit-n 500000 maxconn 99999 maxpipes 99999 tune.maxaccept 500
log 127.0.0.1 local0 log 127.0.0.1 local1 notice
ca-base /etc/ssl/certs crt-base /etc/ssl/private
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS ssl-default-bind-options no-sslv3
defaults
log global
mode http
timeout connect 5000ms timeout client 50000ms timeout server 50000ms timeout tunnel 1h # timeout to use with WebSocket and CONNECT
default-server init-addr none
listen stats bind *:9999 stats enable stats hide-version stats uri /stats stats auth admin:admin@123
frontend http-in bind *:80 alpn h2,http/1.1
option forwardfor
http-request add-header "X-Forwarded-Proto" "http"
acl letsencrypt_http_acl path_beg /.well-known/acme-challenge/
redirect scheme https if !letsencrypt_http_acl { env(FORCE_HTTPS_REDIRECT) -m str true }
use_backend letsencrypt_http if letsencrypt_http_acl
default_backend tb-backend
frontend https_in bind *:443 ssl crt /usr/share/tb-haproxy/default.pem crt /usr/share/tb-haproxy/certs.d/ ciphers ECDHE-RSA-AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM alpn h2,http/1.1
option forwardfor
http-request add-header "X-Forwarded-Proto" "https"
default_backend tb-backend
backend letsencrypt_http server letsencrypt_http_srv 127.0.0.1:8090
backend tb-backend balance leastconn option tcp-check option log-health-checks server tb1 127.0.0.1:8080 check inter 5s http-request set-header X-Forwarded-Port %[dst_port]EOTcat <<EOT | sudo tee /etc/haproxy/haproxy.cfg#HA Proxy Configglobal ulimit-n 500000 maxconn 99999 maxpipes 99999 tune.maxaccept 500
log 127.0.0.1 local0 log 127.0.0.1 local1 notice
ca-base /etc/ssl/certs crt-base /etc/ssl/private
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS ssl-default-bind-options no-sslv3
defaults
log global
mode http
timeout connect 5000ms timeout client 50000ms timeout server 50000ms timeout tunnel 1h # timeout to use with WebSocket and CONNECT
default-server init-addr none
frontend http-in bind *:80 alpn h2,http/1.1
option forwardfor
http-request add-header "X-Forwarded-Proto" "http"
acl letsencrypt_http_acl path_beg /.well-known/acme-challenge/
redirect scheme https if !letsencrypt_http_acl { env(FORCE_HTTPS_REDIRECT) -m str true }
use_backend letsencrypt_http if letsencrypt_http_acl
default_backend tb-backend
frontend https_in bind *:443 ssl crt /usr/share/tb-haproxy/default.pem crt /usr/share/tb-haproxy/certs.d/ ciphers ECDHE-RSA-AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM alpn h2,http/1.1
option forwardfor
http-request add-header "X-Forwarded-Proto" "https"
default_backend tb-backend
backend letsencrypt_http server letsencrypt_http_srv 127.0.0.1:8090
backend tb-backend balance leastconn option tcp-check option log-health-checks server tb1 127.0.0.1:8080 check inter 5s http-request set-header X-Forwarded-Port %[dst_port]EOTStep 6. Configure Edge TLS communication (Optional)
Section titled “Step 6. Configure Edge TLS communication (Optional)”ThingsBoard supports securing the gRPC connection between the platform and Edge instances using TLS/SSL, with HAProxy as the TLS termination point.
Redirect the Edge RPC port to 7071 so HAProxy can listen on the default 7070:
sudo sh -c 'cat <<EOL >> /etc/thingsboard/conf/thingsboard.conf
export EDGES_RPC_PORT=7071EOL'Restart ThingsBoard to apply:
sudo systemctl restart thingsboardAppend the TLS frontend and backend to the HAProxy configuration:
sudo sh -c 'cat <<EOL >> /etc/haproxy/haproxy.cfg
# Edge gRPC TLS (optional)listen grpc_front bind *:7070 ssl crt /usr/share/tb-haproxy/default.pem crt /usr/share/tb-haproxy/certs.d/ ciphers ECDHE-RSA-AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM alpn h2,http/1.1 mode tcp
option clitcpka option tcplog
default_backend grpc_backend
backend grpc_backend mode tcp server grpc 127.0.0.1:7071EOL'Step 7. Configure Certbot with Let’s Encrypt
Section titled “Step 7. Configure Certbot with Let’s Encrypt”Set up the Let’s Encrypt directory structure:
sudo mkdir -p /usr/local/etc/letsencrypt \&& sudo mkdir -p /usr/share/tb-haproxy/letsencrypt \&& sudo rm -rf /etc/letsencrypt \&& sudo ln -s /usr/share/tb-haproxy/letsencrypt /etc/letsencryptCreate the Certbot configuration:
cat <<EOT | sudo tee /usr/local/etc/letsencrypt/cli.iniauthenticator = standaloneagree-tos = Truehttp-01-port = 8090non-interactive = Truepreferred-challenges = http-01EOTcat <<EOT | sudo tee /usr/local/etc/letsencrypt/cli.iniauthenticator = standaloneagree-tos = Truehttp-01-port = 8090tls-sni-01-port = 8443non-interactive = Truepreferred-challenges = http-01EOTCreate the HAProxy cert refresh script:
cat <<EOT | sudo tee /usr/bin/haproxy-refresh#!/bin/sh
HA_PROXY_DIR=/usr/share/tb-haproxyLE_DIR=/usr/share/tb-haproxy/letsencrypt/liveDOMAINS=\$(ls -I README \${LE_DIR})
# update certs for HA Proxyfor DOMAIN in \${DOMAINS}do cat \${LE_DIR}/\${DOMAIN}/fullchain.pem \${LE_DIR}/\${DOMAIN}/privkey.pem > \${HA_PROXY_DIR}/certs.d/\${DOMAIN}.pemdone
# restart haproxyexec service haproxy restartEOTCreate Certbot wrapper scripts:
cat <<EOT | sudo tee /usr/bin/certbot-certonly#!/bin/sh
/usr/bin/certbot certonly -c /usr/local/etc/letsencrypt/cli.ini "\$@"EOTcat <<EOT | sudo tee /usr/bin/certbot-renew#!/bin/sh
/usr/bin/certbot -c /usr/local/etc/letsencrypt/cli.ini renew "\$@"EOTMake the scripts executable:
sudo chmod +x /usr/bin/haproxy-refresh /usr/bin/certbot-certonly /usr/bin/certbot-renewStep 8. Install certificate auto-renewal cron job
Section titled “Step 8. Install certificate auto-renewal cron job”cat <<EOT | sudo tee /etc/cron.d/certbot# /etc/cron.d/certbot: crontab entries for the certbot package## Upstream recommends attempting renewal twice a day## Eventually, this will be an opportunity to validate certificates# haven't been revoked, etc. Renewal will only occur if expiration# is within 30 days.SHELL=/bin/shPATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 */12 * * * root test -x /usr/bin/certbot && perl -e 'sleep int(rand(3600))' && certbot -c /usr/local/etc/letsencrypt/cli.ini -q renew && haproxy-refreshEOTStep 9. Restart HAProxy
Section titled “Step 9. Restart HAProxy”sudo service haproxy restartStep 10. Generate certificate using Let’s Encrypt
Section titled “Step 10. Generate certificate using Let’s Encrypt”Replace your_domain and your_email before running:
sudo certbot-certonly --domain your_domain --email your_emailStep 11. Reload HAProxy
Section titled “Step 11. Reload HAProxy”sudo haproxy-refresh