Port Forwarding with HAProxy
Sat 11 August 2018 by Thejaswi PuthrayaI recently acquired a beefy bare-metal server and wanted to run a bunch of services within VMs based on KVM managed by libvirt. Only ports 80 and 443 for these VMs would be exposed and the rest of the ports (say SSH) visible only from the internal network.
Initially, I just thought of going with Nginx because I was familiar with it. But I had heard a lot about HAProxy and wanted to give it a shot.
I had libvirt assign a static IPv4 address for each VM and then used that to correctly forward the terminated TLS connection at HAProxy.
Here's the libvirt network configuration:
<network> <name>default</name> <uuid>{uuid}</uuid> <forward mode='nat'/> <bridge name='virbr0' stp='on' delay='0'/> <mac address='52:54:00:34:12:10'/> <ip address='192.168.122.1' netmask='255.255.255.0'> <dhcp> <range start='192.168.122.2' end='192.168.122.254'/> <host mac='52:54:00:a9:2c:0b' name='vm1' ip='192.168.122.2'/> <host mac='52:54:00:a9:2c:0c' name='vm2' ip='192.168.122.3'/> ... </dhcp> </ip> </network>
I then created VMs with the above host mac addresses and the corresponding IP address got attached.
The HAProxy config was as below:
frontend http bind :::80 v4v6 bind :::443 v4v6 ssl crt-list /home/fedora/ssl-list.txt mode http option forwardfor option http-server-close redirect scheme https if !{ ssl_fc } use_backend vm1_example if { req.ssl_sni -i vm1.example.com } use_backend vm1_example if { hdr(Host) -i vm1.example.com } use_backend vm2_example if { req.ssl_sni -i vm2.example.com www.vm2.example.com } use_backend vm2_example if { hdr(Host) -i vm2.example.com www.vm2.example.com } backend vm1_example server vm1 192.168.122.2:80 maxconn 32 backend vm2_example server vm2 192.168.122.3:80 maxconn 32
HAProxy forwards the HTTP connections either based on the SNI (if supported by your SSL certificate provider) or the HTTP Header.
The ssl-list.txt file held the location of the SSL certificates (thanks LetsEncrypt):
/etc/letsencrypt/live/vm1.example.com/full_cert.pem vm1.example.com /etc/letsencrypt/live/vm2.example.com/full_cert.pem vm2.example.com www.vm2.example.com
Later on, I ran an SMTP server within it's own VM and port-forwarded to it as well. The HAProxy configuration file had the following lines appended to it:
frontend smtpd bind :::25 v4v6 mode tcp no option http-server-close timeout client 1m log global option tcplog default_backend smtp backend smtp mode tcp no option http-server-close log global option tcplog timeout server 1m timeout connect 5s server postfix 192.168.122.4:25 send-proxy