====== HAProxy ======
===== Installation =====
* add the GPG key for the repository for AWS-LC enabled HAProxy which is required for HTTP/3:sudo wget -qO /usr/share/keyrings/HAPROXY-key-community.asc https://pks.haproxy.com/linux/community/RPM-GPG-KEY-HAProxy
* add the HAProxy 3.4 performance repository for Debian 13 (Trixie) - note that the Debian 13 repository contains HAProxy 3.0:echo "deb [arch=amd64 signed-by=/usr/share/keyrings/HAPROXY-key-community.asc] https://www.haproxy.com/download/haproxy/performance/debian/ha34 trixie main" | sudo tee /etc/apt/sources.list.d/haproxy.list
* update the repository:sudo apt update && sudo apt upgrade -y
* Install HAProxy dedicated AWS-LC package:sudo apt install haproxy-awslc -y
* Check the installed version:haproxy -v
* Start and enable the service:sudo systemctl start haproxy
sudo systemctl enable haproxy
* Replace the contents of your ''/etc/haproxy/haproxy.cfg'' file with this [[haconfig#haproxy|HAProxy Configuration]] configuration.
* Add [[haconfig#whitelist|Whitelist]] and [[haconfig#blacklist|Blacklist]] files.
* Check the configuration file:sudo haproxy -c -f /etc/haproxy/haproxy.cfg
* Restart the proxy:sudo systemctl restart haproxy
* Open port 443 for TCP **and** UDP in your firewall. UDP is required for HTTP/3.
* If you run a mail server behind the proxy you might run into DNS/hairpin NAT issues. You best solve this by adding your mail host to the host overrides table. In [[pfsense:pfsense|pfSense firewall]] this is at //Services --> DNS Resolver --> General Settings//, scroll down to the bottom of the page.
* If you moved a web site from an old to a new server behind HAProxy you must check persistent or long-lived connections
* Check whether the web site is still accessed (occasionally) on the old server:tail -f /var/log/apache2/your-site-access.log
* Remove the site configuration file:rm /etc/apache2/sites-enabled/0xx-your-site.conf
* Check your HAProxy ''timeout http-keep-alive'' or ''timeout client/server'' settings
* Temporarily add ''option http-server-close'' to the backend server definition in ''/etc/haproxy/haproxy.cfg'' during transition
===== SSL certificates =====
* Install and configure [[acme|acme.sh]] to set up automatic [[acme|Let's Encrypt]] certificate generation
* If you moved the certificate from a backend web server to HAProxy disable certificate renewal in the backend web server or delete the certificate:sudo certbot delete --cert-name yourdomain.com
/etc/letsencrypt/renewal/mv yourdomain.com.conf /etc/letsencrypt/renewal/yourdomain.com.disabled
* During transition from an old web server not behind HAProxy to a new proxied server you might need to still be able to renew certificates on the old server. Create the following alternative ''haproxy.cfg.certbot'' config file with a modified rule //frontend public_inbound_http// and a new backend rule //backend http_server_certbot// and copy it to ''haproxy.cfg'':frontend public_inbound_http
bind *:80
mode http
default_backend http_server_certbot
backend http_server_certbot
mode http
cookie SERVERID insert indirect nocache
server calypso 192.168.3.101:80 check
* Reload the proxy rules:sudo systemctl reload haproxy
* SSH into your old server and run ''sudo certbot renew''
* After you have successfully renewed the certificates restore the production ''haproxy.cfg'' and reload the proxy rules
* Note that access to your web sites is always guaranteed through HTTPS, but you create a small window where your sites could be accessed through HTTP directly. If your transition period last longer than a few days you might consider to automate this //hack// to automatically renew certificates on the old server.
===== Apache2 config =====
* Apache requires the //mod_remoteip// module to intercept the header and overwrite the connection IP. Enable the module:sudo a2enmod remoteip
* Configure the module. Create or edit ''/etc/apache2/conf-available/remoteip.conf'' and define your HAProxy server as a trusted internal proxy:RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 192.168.3.50 # this is your HAProxy private IP
* Enable the configuration:sudo a2enconf remoteip
* Apache's default log format uses %h (remote host), which still logs HAProxy's IP. You must change it to %a (client IP address).Edit your main Apache configuration or your VirtualHost file. Look for the LogFormat lines and modify %h to %a:LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
* Test your config and restart Apache:sudo apache2ctl configtest
sudo systemctl restart apache2
* If HAProxy is forwarding raw TCP streams (e.g., passing SSL directly to Apache), it cannot inject an HTTP header. You must use the PROXY protocol instead. Append ''send-proxy'' or ''send-proxy-v2'' to your backend server line:backend passthrough_server
mode tcp
server web1 192.168.3.90:443 send-proxy check
* **If not evaluated properly this will break access to sites on such a passthrough server**. Since I block access and check IP addresses at the proxy server at the earliest possible moment (e.g. ''tcp-request connection reject'') I chose a different approach. I linked a temporary domain name to the backend server and transfer HTTP to access restricted sub-pages like phpmyadmin. For the configuration follow the settings used for //backuppc//.
* Restart HAProxysudo systemctl restart haproxy
* On your apache server ensure //mod_remoteip// is enabled. Open your Apache configuration and define the ''RemoteIPProxyProtocol'' directive:
ServerName example.com
# Enable Proxy Protocol interpretation
RemoteIPProxyProtocol On
RemoteIPProxyProtocolExceptions 127.0.0.1
# ... your SSL and document root settings ...
* Update your LogFormat to use %a instead of %h as described above, then restart Apache.
* To be able to restrict access to domains with query string, which is not possible in HAProxy, you need to add IP addresses to a apache config file, for example ''/etc/apache2/conf-available/bco-trusted-ip.conf'':
# Local subnet
Require ip 192.168.1
# Fixed ip (if you have any)
Require ip my.fixed.ip.addr
* To add an IP address for a remote computer not in one of your trusted IP ranges create an empty file ''/etc/apache2/conf-available/bco-current-ip.conf''.
* Add an ''Include'' to every directory you want to restrict access to in your file:...
Include conf-available/bco-trusted-ip.conf
Include conf-available/bco-current-ip.conf
...
* On the computer you need to get access for add the following command to update and remove your current IP address:IP=$(curl -s https://api.ipify.org) && ssh -p 50322 your.domain.name "echo \"Require ip $IP\" > /etc/apache2/conf-available/bco-current-ip.conf && sudo /usr/bin/systemctl reload apache2"
ssh -p 22 your.domain.name "echo \"\" > /etc/apache2/conf-available/bco-current-ip.conf && sudo /usr/bin/systemctl reload apache2"
* To make this work you need to add to file ''apache'' in ''/etc/sudoers.d'' so can reload the apache config as root without password:vim /etc/sudoers.d/apache
ALL=(root) NOPASSWD: /usr/bin/systemctl reload apache2
* Update the IP from another linux computer or WSL with the following file:#!/bin/bash
# bco, 2026-09-20
# update my current IP address in Apache config
#
IP=$(curl -s https://api.ipify.org)
# Regex pattern matching 0-255 for individual octets
octet='(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'
pattern="^${octet}\.${octet}\.${octet}\.${octet}$"
if [[ $IP =~ $pattern ]]; then
ssh -p 22 your.domain.name "echo \"Require ip $IP\" > /etc/apache2/conf-available/bco-current-ip.conf && sudo /usr/bin/systemctl reload apache2"
else
echo "Invalid IP address"
sleep 3
exit 1
fi
exit 0
* Put a shortcut on the Windows Desktop for easy access:C:\Windows\System32\wsl.exe bash ~/batch/updateApacheIP
* You should also put a shortcut on the Windows Desktop to remove the IP address after you don't need access anymore or move somewhere else.
* This way you can add your computer to the trusted IP list (whitelist) no matter what is the current IP address.
==== Links ====
* [[https://localtonet.com/blog/what-is-a-reverse-proxy|What Is a Reverse Proxy and Why Do You Need One?]]
* Configuration is explained in [[https://www.haproxy.com/blog/the-four-essential-sections-of-an-haproxy-configuration|HAProxy configuration file: global, defaults, front and backend]]