I have an OpenWRT firewall which is configured to send all 80/443 to HAproxy.
I use pihole for local DNS/DHCP
I then use an HAproxy LXC to route the requests to other VM/LXC’s in my LAN (proxmox VE + a few pi’s).
I also want to restrict mysql access to specific whitelisted IP’s which are in a file (/etc/haproxy/whitelist.IPs) for specific CLI tasks. They connect to <public_IP>:33061 where HAproxy has a listener and routes to mysql:3306 if the srcIP is in the whitelist (the whitelist gets a DynDNS update using dig
as needed)
At the moment I use LE wildcard certs and nginx(SSL) for the https but that means I have multiple places to update certificates and configs, and the wildcard LE via DNS is messy, so what I’d like to do is switch it to HAproxy issued certs which also get updated as needed by the HAproxy machine.
I understand that only need to have a specific LE port change and a PEM concat for HAproxy, but what I am not getting right is the syntax for the FE/BE within haproxy.cfg
The one I can’t get right is the jellyfin one, so I did a workaround on port forwards and those requests go to a non-80/443 port which bypass HAproxy and gets sent straight to the JF:8096 machine’s nginx reverse proxy - I’d like to have pretty much all traffic going via HAproxy.
This sometime works and sometimes does’t even though the configs look the same to me (which also goes to show that I don’t really know what I am doing )
My config looks like below… and does (mostly) work - but feels like it’s horribly ugly/inefficient.
{sorry if it’s weirdly spaced because of the IDE I used}
What should I add/del/change to make it more robust and closer to better/best practices?
My aim…
- HAproxy manages all certs (auto updates as well as new and with A+ ssl ratings if possible)
- I’d like to be able to see/detect client IP’s at the nginx/httpd point
- nginx only needs to be set for the basic http:80 since the rest is done higher up
- fix the mangle for jellyfin so that it can come in via 80/443 and get to the JF-reverse-proxy correctly…and show the client IP in there too
- when I add in
check
for some of the BE’s they fail in the stats - I don’t understand where/why (I did try putting in hosts entries on the HAproxy machine, and also in the pihole-DNS for those but it didn’t help).
Thanks in advance. I don;t take offense so if I need to get flamed, that’s OK
global
log /dev/log local0
log /dev/log local1 notice
# https://www.haproxy.com/blog/introduction-to-haproxy-logging/
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
stats timeout 30s
user haproxy
group haproxy
daemon
# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
# See: https://ssl-config.mozilla.org/#server=haproxy&version=2.6&config=modern&openssl=1.1.1n&guideline=5.6
# modern configuration
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets
ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets
ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
tune.ssl.default-dh-param 2048
defaults
log global
option httplog
option dontlognull
# option forwardfor except 127.0.0.0/8
option redispatch
option http-server-close
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
# timeout check 10s
# maxconn 3000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
frontend stats
bind *:9000
mode http
stats enable
stats uri /stats
stats refresh 30s
stats auth admin:password
stats hide-version
stats realm HAproxy\ Statistics
frontend http_in
bind *:80 alpn h2,h2c,http/1.1
mode http
option forwardfor
http-request redirect scheme https unless { ssl_fc }
# All of the rules applying on port 80 (or any port) need to be specified on a single frontend (or a single listen) that is bound to port 80. [https://serverfault.com/questions/794943/haproxy-multiple-frontends-same-bind]
frontend https_in
bind *:443
mode tcp
option tcplog
tcp-request inspect-delay 5s
acl tls req_ssl_hello_type 1
tcp-request content accept if tls
# tcp-request content accept if { req_ssl_hello_type 1 } ### this line does the same as the 2 above it
option forwardfor header X-Real-IP
http-request set-header X-Real-IP %[src]
HSTS (63072000 seconds)
http-response set-header Strict-Transport-Security max-age=63072000
acl acl_jellyfin req_ssl_sni -i jellyfin.example.org
acl acl_nextcloud req_ssl_sni -i nextcloud.example.org
acl acl_httpd req_ssl_sni -i httpd.example.org
acl acl_serene req_ssl_sni -i serene.example.org
acl acl_serene req_ssl_sni -i serene.example.net
use_backend nextcloud if acl_nextcloud
use_backend httpd if acl_httpd
use_backend jellyfin if acl_jellyfin
use_backend serene if acl_serene
default_backend default
backend httpd
mode tcp
#option httplog
option tcp-check
option ssl-hello-chk
option httpchk GET /
http-check send hdr Host httpd.example.org
server httpd httpd.example.org:443 check-ssl verify none send-proxy-v2
backend nextcloud
mode tcp
# http-check expect status 200 #* when I specify the code 200, HAproxy reports "no backend server available" - seems that it's better to let it work out the code itself
option tcp-check
option ssl-hello-chk
option httpchk GET /
http-check send hdr Host nextcloud.example.org
server nextcloud nextcloud.example.org:443 check-ssl verify none check-sni nextcloud.example.org sni str(nextcloud.example.org) # send-proxy-v2
backend jellyfin
mode tcp
# http-check send hdr Host jellyfin.example.org
option tcp-check
option ssl-hello-chk
option httpchk GET /
server jellyfin jellyfin.example.org:443 check-ssl verify none send-proxy-v2
backend serene
mode tcp
option tcp-check
option ssl-hello-chk
option httpchk GET /
http-check send hdr Host serene.example.org
server serene serene.example.org:443 check-ssl verify none send-proxy-v2
backend default
mode tcp
option tcp-check
option ssl-hello-chk
option httpchk GET /
http-check send hdr Host wotd.example.org
server nginx wotd.example.org:443 check-ssl verify none send-proxy-v2
listen mysql
bind <public_IP>:33061
mode tcp
acl mysql_ip_OK src -f /etc/haproxy/whitelist.IPs
tcp-request connection reject if !mysql_ip_OK
balance roundrobin
# I am using 127.0.0.1/localhost as the "list" of servers for the sake of example - real world would be the IP list of actual server IPs
server mysql1 127.0.0.1:3306
server mysql2 localhost:3306