Load balancing with HAProxy
HAProxy is an open-source software capable of processing Layer 4 and Layer 7 traffic. It supports high availability, load-balancing, and proxying traffic to one or more backend servers.
Installation
Requirements
HAProxy can be installed and run on any platform that HAProxy supports. In this article, we focus on the installation of HAProxy on Alma Linux 9 and RHEL.
If possible, install HAProxy on a dedicated server for security and performance reasons. However, you can also install it on the same server where a censhare Server is running. In this case, make sure that the following ports are not in use so that HAProxy can bind to them:
80 - Plain HTTP. It is redirected to port 443.
443 - Default HTTPs port.
30546 - By default, censhare RMI uses port 30546. Therefore, when running HAProxy on a dedicated server, we recommend using port 30546 here as well. However, when running HAProxy on the same machine as a censhare Server, for example, the Master server, use another port such as 30545. Also, ensure that your hosts.xml contains the correct port defined to use with HAProxy.
8080 - HAProxy statistics.
How to verify that the ports are available:
ss -tulnp | egrep '80|443|30546|8080'
Installation command
To install HAProxy on Alma Linux 9 7 and RHEL 7 and later versions, install the package from the distribution repository. Run the following command:
yum install haproxy
Configuration
Note: For a complete guide on how to configure HAProxy, see the Configuration Manual for the Community Edition.
Find an example of a configuration file for HAProxy that you can use for load-balancing and high-availability for censhare. The configuration file defines an environment with two censhare Servers, one of them as the Master server.
First, identify the appropriate custom values for your environment for the following items. Then adapt the example configuration file:
If the application has a custom context path that is different from the default /censhare5/client, check all entries of /censhare5/client in the configuration file. Replace the entries with the appropriate context path. For example, if the base URL is:
CODEhttps://customer.example.com/censhareapp
The custom context path is censhareapp. Then, set the http-request redirect line in the configuration file:
CODEhttp-request redirect code 301 location https://%[hdr(host)]/censhareapp
Upload a PEM-formatted SSL certificate file to the server. This file should contain both the private key and the certificate chain bundle. This means: When a certificate is signed by an intermediate certificate authority (CA), also include the certificate of the intermediate CA in addition to the actual certificate.
Example of a PEM-format certificate:
CODE-----BEGIN ENCRYPTED PRIVATE KEY----- MBQGCCqGSIb3DQMHBAgD1kGN4ZslJgSCBMi1xk9jhlPxPc 9g73NQbtqZwI+9X5OhpSg/2ALxlCCjbqvzgSu8gfFZ4yo+ A .... MANY LINES LIKE THAT .... X0R+meOaudPTBxoSgCCM51poFgaqt4l6VlTN4FRpj+c/Wc blK948UAda/bWVmZjXfY4Tztah0CuqlAldOQBzu8TwE7WD H0ga/iLNvWYexG7FHLRiq5hTj0g9mUPEbeTXuPtOkTEb/0 GEs= -----END ENCRYPTED PRIVATE KEY----- -----BEGIN CERTIFICATE----- MIIDXTCCAkWgAwIBAgIJAJC1HiIAZAiIMA0GCSqGSIb3Df BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVx aWRnaXRzIFB0eSBMdGQwHhcNMTExMjMxMDg1OTQ0WhcNMT A .... MANY LINES LIKE THAT .... JjyzfN746vaInA1KxYEeI1Rx5KXY8zIdj6a7hhphpj2E04 C3Fayua4D RHyZOLmlvQ6tIChY0ClXXuefbmVSDeUHwc8Yu B7xxt8BVc69rLe HV15A0qyx77CLSj3tCx2IUXVqRs5mlSbvA== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIElDCCA3ygAwIBAgIQAf2j627KdciIQ4tyS8+8kTANBgkqhkiG9w0 BAQsFADBhMQswCQYDVQQGEwJVUzEVMBMGA 1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 A .... MANY LINES LIKE THAT .... YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= -----END CERTIFICATE-----
Additionally, for security reasons, the PEM-format certificate file and the directory where the file is located should not be readable by non-root users. For example:CODEdrwxrw----. 2 root root 26 Apr 23 15:38 /etc/haproxy/ssl -r-------- 1 root root 7086 Mar 6 12:08 /etc/haproxy/ssl/certificatefile.pem
censhare Server IP addresses or domain/hostnames and HTTPs ports: In the configuration example below, the servers are:
CODEcust-prodns-css01 port 8082 cust-prodns-css02 port 8082
censhare Server IP addresses or domain/hostnames and RMI ports: In the configuration example below, the servers are
CODEcust-prodns-css01 port 30546 cust-prodns-css02 port 30546
Here is the complete /etc/haproxy/haproxy.cfg example which includes the items discussed above:
global
log 127.0.0.1:514 local0 info
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon tune.ssl.default-dh-param 2048
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
defaults
log global
mode http
balance leastconn
#option tcplog
log-format {"type":"haproxy","request_id":"%rt","timestamp":%Ts,"http_status":%ST,"http_request":"%r","remote_addr":"%ci/%cp","bytes_read":%B,"upstream_addr":"%si","bytes_uploaded":%U,"upstream_response_time":"%Tr","session_duration":"%Tt"}
option dontlognull
option redispatch
option log-health-checks
retries 3
maxconn 2000
timeout connect 60000
timeout client 60000
timeout server 60000
default-server inter 10s
###################### RMI Protocol Start ######################
frontend rmi30546-in
mode tcp
bind *:30546
#option tcplog
#option http-server-close
default_backend rmi30546-out
backend rmi30546-out
mode tcp
stick-table type ip size 1m expire 30m
stick on src
balance leastconn
default-server inter 1s downinter 3s rise 15 fall 15
server cust-prodns-css01 cust-prodns-css01:30546 check
server cust-prodns-css02 cust-prodns-css02:30546 check
###################### RMI Protocol Finish ######################
###################### HTTP/S Protocol Start ######################
frontend web80-in
bind *:80
http-request redirect code 301 location https://%[hdr(host)]/censhare5/client
frontend web443-in
mode http
bind *:443 ssl crt /etc/haproxy/ssl/cert_file.pem
#option httplog # not needed !!!
# reqadd X-Forwarded-Proto:\ https
default_backend cloud-gateway
# Direct requests to REST interface directly to censhare-Server backend
acl is_rest_interface path_beg /ws
acl is_temp_download path_beg /tempDownload
use_backend web443-out if is_rest_interface
use_backend web443-out if is_temp_download
option forwardfor
timeout client 300m
timeout http-keep-alive 10s
timeout http-request 5s
timeout tarpit 60s # KRED TODO Disable oder extend
acl is_websocket path_beg /censhare5/client/
acl is_websocket hdr(Upgrade) -i WebSocket
acl is_websocket hdr_beg(Host) -i ws
acl path_auth path -m beg /auth
use_backend keycloak if path_auth
acl is_root path /
capture request header Host len 64
http-request redirect scheme https code 301 if !{ ssl_fc }
http-request redirect code 301 location https://%[hdr(host)]/censhare5/client if is_root
backend web443-out
cookie SESSION prefix nocache
option forwardfor
balance leastconn
option ssl-hello-chk
option httpchk GET /monitoring/health
http-check expect rstring ^{\"status\": \"UP\"}$
# http-check expect rstring ^{\"status\":\ \"UP\"}$ # to use with alma linux
default-server inter 1s downinter 3s rise 15 fall 15
timeout check 1s
timeout server 60s
timeout tunnel 3600s
timeout queue 30s
timeout connect 5s
#option http-server-close # KRED es muss disabled
http-request add-header X-Forwarded-Proto https if { ssl_fc }
redirect scheme https if !{ ssl_fc }
redirect prefix https://censhare.company.com/cihub code 301 if { hdr(host) -i https://censhare.company.com:9000 } # cihub to bypass cgw
http-response add-header Strict-Transport-Security max-age=31536000;\ includeSubdomains
http-response add-header X-Content-Type-Options nosniff
http-response add-header X-XSS-Protection 1;\ mode=block
http-response add-header Referrer-Policy no-referrer
http-response set-header X-Frame-Options SAMEORIGIN
http-response add-header Permissions-Policy accelerometer=(),\ ambient-light-sensor=(),\ autoplay=(),\ camera=(),\ display-capture=(),\ document-domain=(),\ fullscreen=(self),\ execution-while-not-rendered=(),\ execution-while-out-of-viewport=(),\ gyroscope=(),\ magnetometer=(),\ microphone=(),\ midi=(),\ payment=(),\ picture-in-picture=(),\ publickey-credentials=(),\ sync-xhr=(),\ usb=(),\ wake-lock=()
server cust-prodns-css01 cust-prodns-css01:9443 weight 5 check ssl verify none cookie s1
server cust-prodns-css02 cust-prodns-css02:9443 weight 5 check ssl verify none cookie s2
backend keycloak
cookie SESSION prefix nocache
option forwardfor
balance leastconn
default-server inter 1s downinter 3s rise 15 fall 15
timeout check 1s
timeout server 60s
timeout tunnel 3600s
timeout queue 30s
timeout connect 5s
#option http-server-close # KRED es muss disabled
http-request add-header X-Forwarded-Proto https if { ssl_fc }
redirect scheme https if !{ ssl_fc }
server keycloak-server keycloak-server:9443 check cookie s1
backend cloud-gateway
cookie SESSION prefix nocache
option forwardfor
balance leastconn
option httpchk
http-check expect status 302
default-server inter 1s downinter 3s rise 15 fall 15
timeout check 1s
timeout server 60s
timeout tunnel 3600s
timeout queue 30s
timeout connect 5s
#option http-server-close # KRED es muss disabled
http-request add-header X-Forwarded-Proto https if { ssl_fc }
redirect scheme https if !{ ssl_fc }
http-response add-header Strict-Transport-Security max-age=31536000;\ includeSubdomains
http-response add-header X-Content-Type-Options nosniff
http-response add-header X-XSS-Protection 1;\ mode=block
http-response add-header Referrer-Policy no-referrer
http-response set-header X-Frame-Options SAMEORIGIN
http-response add-header Permissions-Policy accelerometer=(),\ ambient-light-sensor=(),\ autoplay=(),\ camera=(),\ display-capture=(),\ document-domain=(),\ fullscreen=(self),\ execution-while-not-rendered=(),\ execution-while-out-of-viewport=(),\ gyroscope=(),\ magnetometer=(),\ microphone=(),\ midi=(),\ payment=(),\ picture-in-picture=(),\ publickey-credentials=(),\ sync-xhr=(),\ usb=(),\ wake-lock=()
server cust-prodns-css01 cust-prodns-css01:8082 weight 5 check cookie s1
server cust-prodns-css02 cust-prodns-css02:8082 weight 5 check cookie s1
###################### HTTP/S Protocol Finish ######################
###################### Statistics ######################
frontend statistics-8000
bind *:8000
mode http
#option httplog # not needed !!!
stats enable
stats uri /haproxy
stats auth admin:password
stats admin if TRUE
###################### Statistics Finish ######################
Validate the configuration file:
haproxy -f /etc/haproxy/haproxy.cfg -c
Fix any issues, then start HAProxy and make sure that it starts on server reboot:
systemctl enable haproxy && systemctl start haproxy
Route forwarding for censhare
If you use censhare with a load balancer, such as NGINX or HAProxy, set the following redirects:
Path "/auth/" to Keycloak ( http://authentication.your-company.com:8080 )
Path "/login/" to Cloud Gateway ( http://cloud-gateway.your-company.com:8082 )
Path "/oauth2/" to Cloud Gateway ( http://cloud-gateway.your-company.com:8082 )
Path "/censhare5/client/" to Cloud Gateway ( http://cloud-gateway.your-company.com:8082 )
Path "/ws/" to censhare-Server REST ( http://censhare.your-company.com:9000 )
Path "/tempDownload/" to censhare-Server REST ( http://censhare.your-company.com:9000 )
Path "/cihub" to censhare-Server REST ( https://censhare.your-company.com:9000 )
/tempDownload/ and /cihub should be routed from the proxy directly to the censhare Server. As they do not have separate mappings in the Cloud Gateway setup and do not use the usual login session.
Details and options
The following section contains details and explanations about the haproxy.cfg configuration file. Keep these configuration options, and customize them only if you are certain of the implications of the changes!
Load-balancing algorithms
HAProxy supports a number of load-balancing algorithms to determine how to distribute traffic to multiple backend servers.
The algorithm used above for HTTPs is leastconn: The backend server with the least number of connections is used, and then for all servers with the same amount of connections, a round-robin is performed. The configuration entry:
balance leastconn
Currently, RMI cannot handle leastconn. Hence, sessions must be sent to the same backend server and be tracked by the client IP addresses. The configuration entry:
stick-table type ip size 1m expire 30m stick on src
Other algorithms are round-robin or static-rr. For more information on all supported load-balancing algorithms, see the HAProxy Configuration Manual.
Sticky sessions
For the leastconn load-balancing algorithm, cookies are used to ensure that clients are always sent to the same backend server. The following lines in the backend section of the configuration file define this:
cookie SRVID insert indirect nocache maxidle 30m maxlife 1h
...
server cust-prodns-css01 cust-prodns-css01:8082 weight 5 check ssl verify none cookie s1
server cust-prodns-css02 cust-prodns-css01:8082 weight 5 check ssl verify none cookie s2
Please also note the following parameters, which manage the timeout between the client (browser) and HAProxy, and HAProxy and the backend servers (censhare).
timeout client 60m
...
timeout server 60m
WebSocket
WebSocket support is required. The following configuration entries define this:
acl is_websocket path_beg /censhare5/client/
acl is_websocket hdr(Upgrade) -i WebSocket
acl is_websocket hdr_beg(Host) -i ws
HAProxy statistics
The use of HAProxy statistics is optional but recommended. This is helpful for troubleshooting web traffic issues and for capacity planning.
To encrypt traffic for the statistics URL, use the following lines instead:
###################### Statistics ######################
fontend statistics-8443
bind *:8443 ssl crt /etc/haproxy/ssl/certificatefile.pem
mode http
#option httplog
stats enable
stats uri /haproxy
stats auth admin:password
stats admin if TRUE
###################### Statistics Finish ######################
This uses the same PEM-encoded SSL certificate and the port 8443 that must be available.
CSP Headers
For security, you can also add Content-Security-Policy HTTP headers. For example:
http-response add-header Content-Security-Policy default-src\ 'none';\
http-response add-header Content-Security-Policy connect-src\ 'self'\ wss://%[capture.req.hdr(0)]\
https://nominatim.openstreetmap.org;\
http-response add-header Content-Security-Policy script-src\ 'self'\ 'unsafe-eval'\
https://connect.facebook.net\
https://platform.twitter.com\ 'sha256-mutaUHp7a6TAHBx7UkKbOet7lfkvyPk131k92oPYqEQ=';\
http-response add-header Content-Security-Policy img-src\ 'self'\ data:\
https://*.tile.openstreetmap.org;\
http-response add-header Content-Security-Policy worker-src\ 'self'\ blob:;\
child-src\ 'self'\ blob:;\ style-src\ 'self'\ 'unsafe-inline';\ frame-src\ *;\
media-src\ 'self';\ base-uri\ 'self';\ frame-ancestors\ 'self';\ form-action\
'none';\ font-src\ 'self'\ data:;\ upgrade-insecure-requests
Restart HAProxy:
systemctl restart haproxy
SSL protocol versions
The HAProxy example configuration only allows HAProxy to accept TLSv1.2. The following entry in the configuration file removes the support for the SSL versions SSLv3, TLSv1.0 and TLSv1.1:
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
Note on TLS 1.3
HAProxy 1.8 and newer introduce support for 0-RTT, also known as TLS 1.3. If a customer requires TLS 1.3 support, check if the current OS provides a compiled version of HAProxy 1.8. For example, you can check if the RPM repository contains this version. By default, RHEL/Alma Linux 9 7 do not include version 1.8 in their base repositories, but RHEL/Alma Linux 9 8 do.
If version 1.8 is not available in the base RPM repository, you must compile HAProxy from source.
Cipher suites
Supported cipher suites:
ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS