Haproxy: Contents load after "timeout server" hit - timeout

I'm totally new in the world of haproxy. I'm using it as loadbalancer with the following setup:
loadablanacer (haproxy 2.2.9, deb11)
2x webserver with php-fpm and the Roundcube app (a webmail client, deb11)
db server (MariaDB, deb11)
I managed to set up all what I need for my test but when I open the website (Roundcube) using the loadbalancer some content remains in pending until the timeout server setting hit. After that, the content continue to load correctly (it could happens for more than one file, tipically .js).
The log, after the timeout, return the following lines:
Dec 30 15:54:27 lb haproxy\[513\]: [my.public.ip.addr:61687](https://my.public.ip.addr:61687) \[30/Dec/2022:15:54:21.210\] webmail-fe webmail-be/webmail1 0/0/1/3/6005 200 7804 - - sDVN 6/6/4/4/0 0/0 "GET /plugins/jqueryui/themes/elastic/jquery-ui.min.css?s=1658607433 HTTP/1.1"
That sDVN, if I got it right, means that the webserver didnt' reply with any data and then it got killed (https://cbonte.github.io/haproxy-dconv/1.8/configuration.html#8.5):
s : the server-side timeout expired while waiting for the server to send or receive data.
D : the session was killed by haproxy because the server was detected as down and was configured to kill all connections when going down.
On the webserver, the respective HTTP request:
webmail1.local - - [30/Dec/2022:15:54:21 +0100] "GET /plugins/jqueryui/themes/elastic/jquery-ui.min.css?s=1658607433 HTTP/1.1" 200 7848 "http://loadbalancer.local/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"
If I visit the website directly on the webserver everything works fine without waiting time.
All the server are connected via LAN and there are no latency or packet loss.
I'll appreciate any hint or help. If need more info I'll gather'em asap.
Here my configuration:
global
maxconn 5000
log /dev/log local0
log /dev/log local1 notice
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&server-version=2.0.3&config=intermediate
ssl-default-bind-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
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 3s
timeout client 6s
timeout server 6s
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 webmail-fe
bind *:80
mode http
default_backend webmail-be
backend webmail-be
balance leastconn
option forwardfor
cookie SERVERUSED insert indirect nocache
# http-request cache-use webmail-cache
# http-response cache-store webmail-cache
# http-request set-header X-Forwarded-Port %[dst_port]
# http-request add-header X-Forwarded-Proto https if { ssl_fc }
# option httpchk HEAD / HTTP/1.1\r\nHost:localhost
option httpchk HEAD /
default-server check maxconn 50
server webmail1 webmail1.local:8080 cookie webmail1
# server webmail2 192.168.0.53:8080 cookie webmail2
# listen stats
# bind :32700
# stats enable
# stats uri /
# stats hide-version
# stats auth someuser:password
#cache webmail-cache
# total-max-size 128
# max-object-size 1000
# max-age 14
No error log from webserver or any strage log on haproxy other thab the line already posted.
No hanging connection using ss or any clear problem seen using tcpdump
I expect that, with so few connection (it's a test env) and small webapp all the resources are loaded fast as directly visited skipping the loadbalancer.

Upgraded to haproxy 2.4 and now everything works fine!

Related

Configuring haproxy load balancer in front of ha artifactory cluster

I'm trying to configure an haproxy load balancer in front of our 2-node ha artifactory cluster. I'm using the page here as a guide:
https://jfrog.com/knowledge-base/how-to-configure-haproxy-with-artifactory/
but this was written years ago for a much older version of haproxy (I'm running 2.0.8) and a lot of the code is deprecated. The recommended configuration starts with errors. Here it is:
# version 1.0
# History
# https://jfrog.com/knowledge-base/how-to-configure-haproxy-with-artifactory/
# —————————————————————————
# Features enabled by this configuration
# HA configuration
# port 80, 443 Artifactory GUI/API
#
# This uses ports to distinguish artifactory docker repositories
# port 443 docker-virtual (v2) docker v1 is redirected to docker-dev-local.
# port 5001 docker-prod-local (v1); docker-prod-local2 (v2)
# port 5002 docker-dev-local (v1); docker-dev-local2 (v2)
#
# Edit this file with required information enclosed in <…>
# 1. certificate and key
# 2. artifactory-host
# 3 replace the port numbers if needed
# —————————————————————————-
global
log 127.0.0.1 local0
chroot /var/lib/haproxy
maxconn 4096
user haproxy
group haproxy
daemon
tune.ssl.default-dh-param 2048
stats socket /run/haproxy/admin.sock mode 660 level admin
defaults
log global
mode http
option httplog
option dontlognull
option redispatch
option forwardfor
option http-server-close
maxconn 4000
timeout connect 5000
timeout client 50000
timeout server 50000
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
listen stats
bind *:2016
mode http
stats enable
stats uri /haproxy
stats hide-version
stats refresh 5s
stats realm Haproxy\ Statistics
frontend normal
bind *:80
bind *:443 ssl crt /etc/ssl/artifactory/cert.pem
mode http
option forwardfor
reqirep ^([^ :]*) /v2(.*$) 1 /artifactory/api/docker/docker-virtual/v22
reqadd X-Forwarded-Proto: https if { ssl_fc }
option forwardfor header X-Real-IP
default_backend normal
# Artifactory HA Configuration
# Using default failover interval – rise = 2; fall =3 3; interval – 2 seconds
backend normal
mode http
balance roundrobin
option httpchk OPTIONS /
option httpchk GET /api/system/ping HTTP/1.1\r\nHost:haproxy\r\n
option forwardfor
option http-server-close
appsession JSESSIONID len 52 timeout 3h
server platform-artifactory-ha-01 172.17.1.71:80 check fall 3 inter 3s rise 2
server platform-artifactory-ha-02 172.17.1.122:80 check fall 3 inter 3s rise 2
If I run haproxy -f haproxy.cfg -c I get:
[WARNING] 121/054551 (11113) : parsing [haproxy.cfg:55] : The 'reqirep' directive is deprecated in favor of 'http-request replace-header' and will be removed in next version.
[ALERT] 121/054551 (11113) : parsing [haproxy.cfg:55] : 'reqirep' : Expecting nothing, 'if', or 'unless', got '/v2(.*$)'.
[WARNING] 121/054551 (11113) : parsing [haproxy.cfg:56] : The 'reqadd' directive is deprecated in favor of 'http-request add-header' and will be removed in next version.
[ALERT] 121/054551 (11113) : parsing [haproxy.cfg:56] : 'reqadd' : Expecting nothing, 'if', or 'unless', got 'https'.
[ALERT] 121/054551 (11113) : parsing [haproxy.cfg:68] : 'appsession' is not supported anymore since HAProxy 1.6.
[ALERT] 121/054551 (11113) : Error(s) found in configuration file : haproxy.cfg
[ALERT] 121/054551 (11113) : Fatal errors found in configuration.
I've been able to get artifactory to start up by commenting the following lines 64 and 65:
# reqirep ^([^ :]*) /v2(.*$) 1 /artifactory/api/docker/docker-virtual/v22
# reqadd X-Forwarded-Proto: https if { ssl_fc }
and adding:
http-request set-header X-Forwarded-Proto https if { ssl_fc }
to replace line 65
I also had to comment line 79 to get the haproxy service to start without errors:
# appsession JSESSIONID len 52 timeout 3h
But now it doesn't properly work in the case where folks are trying to push dockers into the regsitry.
I've got to figure the new way to write line 79 and line 64. But I'm having trouble finding the correct configuration directives in the documentation.
The reqirep keyword was spitted in several http-request directives.
You will need to use http-request replace-path.
My suggestion, untested
# reqirep ^([^ :]*) /v2(.*$) 1 /artifactory/api/docker/docker-virtual/v22
http-request replace-path /v2(.*$) /artifactory/api/docker/docker-virtual/v22\1
The appsession isn't anymore part of haproxy as the ALERT message shows.
My suggestion for the cookie sticky, untested.
backend normal
mode http
balance roundrobin
# this makes no sense option httpchk OPTIONS /
option httpchk GET /api/system/ping HTTP/1.1\r\nHost:haproxy\r\n
option forwardfor
option http-server-close
stick-table type string len 52 size 2m expire 3h
#appsession JSESSIONID len 52 timeout 3h
stick on cookie(JSESSIONID)
server platform-artifactory-ha-01 172.17.1.71:80 check fall 3 inter 3s rise 2
server platform-artifactory-ha-02 172.17.1.122:80 check fall 3 inter 3s rise 2

Haproxy path manipulation

In Google Cloud Platform we do have a haproxy which serves as proxy for our internal VPN and do URL redirects from GCP DNS to proxy.mycompany.com (our haproxy) where we do further manipulation.
Haproxy is from image 2.0.8-alpine
Nginx is based on image 1-alpine
Lately I have installed nginx which serves static files.
Nginx is setup for autoindex on, accesible on url static-files.mycompany.com, and configured to show root of a gcs-bucket. Therefore if you visit static-files.mycompany.com you will see content of gcs-bucket:
directory static-files
any file in a bucket.
static-files directory contains static website files which were generated by hugo.
Those files contain paths like /docu/file.md
I've managed to configure haproxy that it will forward any requests for url website.mycompany.com to static-files.mycompany.com/static-files and show the generated static website by configuring this in backend section of haproxy config:
acl p_root path -i /
http-request set-path /static-files/\1 if p_root
Every time I visit website.mycompany.com the path is redirected to website.mycompany.com/static-files (which is actually static-files.mycompany.com/static-files )
However, the site is broken:
css is not loading, file is located in / and the requests aims for website.mycompany.com/mycss.file, but the file can be found under website.mycompany.com/static-files/mycss.file due to path manipulation above
Every link on site dead, requests website.mycompany.com/my-link-to-file but file is under
website.mycompany.com/static-files/my-link-to-file
I have a limited possibility to configure haproxy, most probably I can add parameters to existing backend and frontend section, here I am providing parameters I cannot change:
global
log stdout format raw local0 info
maxconn 30000
tune.ssl.default-dh-param 2048
resolvers vpc
parse-resolv-conf
hold valid 120s
defaults
mode http
log global
option dontlognull
#option tcplog
option httplog
option forwardfor
option redispatch
maxconn 3000
retries 3
timeout connect 5s
timeout client 50s
timeout server 50s
timeout tunnel 1h
timeout client-fin 30s
timeout http-keep-alive 4s
balance roundrobin
default-server check resolvers vpc
default-server on-marked-down shutdown-sessions
default-server max-reuse 100
frontend http
bind *:80
redirect scheme https code 302 if !{ ssl_fc }
monitor-uri /_healtz
use_backend %[hdr(host),lower]
frontend https
bind *:443 ssl no-sslv3 crt /config/haproxy_certificate.pem alpn http/1.1,h2
http-response add-header Via 1.1\ %[env(HOSTNAME)]
http-request add-header Via 1.1\ %[env(HOSTNAME)]
http-request add-header X-Forwarded-Proto https
http-request capture req.hdr(Host) len 40
http-request capture req.hdr(User-Agent) len 120
use_backend %[hdr(host),lower]
backend proxy.mycompany.com
stats enable
stats uri /
I would like to achieve the following:
By visiting website.mycompany.com I will be forwarded to static-files.mycompany.com/static-files in the background, however the URL will remain as website.mycompany.com and therefore the generated static website paths remain working and so the static site. Or Am I mistaken ?
I am open to any reasonable suggestion.
thank you
Well, it was quite simple in the end
http-request replace-uri ([^/:]*://[^/]*)?(.*) \1/static-files\2
Did the trick.
edit:
to avoid and infinite loop of /static-files/static-files/
acl remove_static-files path_beg -i /architecture-diagrams
http-request replace-uri ([^/:]*://[^/]*)?(.*) \1/static-files\2 if !remove_static-files

HaProxy forward proxy works on HTTP but gives 503 on HTTPS

I'm expecting the following config to receive HTTPS requests, do the SSL offloading and send HTTP requests to my backends, however with HTTPS I get "503 service unavailable".
all ACLs work correctly on HTTP and the stats page shows them as online
The stats page works correctly on HTTPS
These are all in a docker compose file, docker is doing the name resolution to internal IP correctly
Perhaps I'm missing something obvious? Quite new to attempting this so any help is appreciated.
global
tune.ssl.default-dh-param 2048
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
backend certbot
option httpchk GET /
default-server init-addr libc,none
server certbot_server certbot check port 80
backend client
option httpchk HEAD /
server client_server client check port 80
backend api
option httpchk OPTIONS /api/healthcheck
server api_server api check port 80
frontend app
bind *:80
bind *:443 ssl crt /certs/productpedia.co.uk.pem
use_backend certbot if { path_beg -i /.well-known/acme-challenge/ }
use_backend api if { path_beg /api }
default_backend client
stats enable
stats uri /stats
stats refresh 10s
stats admin if LOCALHOST
EDIT:
Attached a wireshark trace of the the 503 request, looks like server is resetting the connection, but not sure where I can go from here, or what would be causing this?
After a full day of debugging, it looks like simply specifying port 80 did the trick, although I would have expected the default port to be 80 perhaps it carries through a default port of 443? I could also get rid of the port 80 after check after this change which was the original trigger hint that something might be off there.
backend certbot
option httpchk GET /
default-server init-addr libc,none
server certbot_server certbot:80 check
backend client
option httpchk HEAD /
server client_server client:80 check
backend api
option httpchk OPTIONS /api/healthcheck
server api_server api:80 check

HAProxy 2.0.6-1~bpo9+1 Health check for server failed, reason: Layer4 timeout

I upgraded the haproxy from version 1.7.5.2 to 2.0.6-1~bpo9+1 on debian; same config is showing servers down and log shows the following error:
"Health check for server failed, reason: Layer4 timeout"
If I remove the "check" option from the servers it works as expected, but the stats page show servers on gray color
Config file:
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
maxconn 50000
# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
tune.ssl.default-dh-param 2048
ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:D$
ssl-default-bind-options no-sslv3
defaults
mode http
log global
option httplog
option dontlognull
option log-health-checks
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
option forwardfor
retries 3
timeout http-request 60s
timeout queue 1m
timeout connect 60s
timeout client 10m
timeout server 10m
timeout http-keep-alive 60s
timeout check 60s
maxconn 30000
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
listen HAProxyLocalStats
bind *:2267 name localstats
mode http
stats enable
stats refresh 10
stats admin if TRUE
stats uri /haproxy
stats auth admin:admin
timeout client 5000
timeout connect 5000
timeout server 5000
frontend FrontNginx
bind *:80
mode http
log global
capture request header X-Forwarded-For len 500
maxconn 30000
default_backend nginxFarm
#-----------------------------------------------------------------------
backend nginxFarm
mode http
balance leastconn
timeout connect 30000
retries 3
option forwardfor
server WEB01 10.20.10.19:80 check inter 5000
server WEB02 10.20.10.20:80 check inter 5000

How to run stats in HAProxy 1.6.4 on same port as frontend?

I'm using HAProxy 1.6.4 and want to enable the stats. (/haproxy?stats)
Here is my cfg:
global
log 127.0.0.1 local2
daemon
maxconn 256
defaults
log global
timeout connect 5000
timeout client 10000
timeout server 10000
frontend http-in
bind *:8080
default_backend testb
backend testb
balance roundrobin
server s1 123.456.789.0:443 maxconn 32
server s2 123.456.789.1:443 maxconn 32
listen statistics
bind *:8080
mode http
stats enable
If I run statistics on other port than 8080 it works, but how can I run it on the same port as my frontend (8080), which is running in the default mode tcp?
You can do it by redirecting to your self and using access list like this:
global
log 127.0.0.1 local2
daemon
maxconn 256
defaults
log global
timeout connect 5000
timeout client 10000
timeout server 10000
listen stats :1936
mode http
stats enable
stats hide-version
stats realm Haproxy\ Statistics
stats uri /
stats auth myUser:myPassword
frontend http-in
bind *:8080
acl is_www hdr_end(host) -i www.mysite.com
acl is_stat hdr_end(host) -i stat.mysite.com
use_backend srv_www if is_www
use_backend srv_stat if is_stat
backend srv_www
balance roundrobin
server s1 123.456.789.0:443 maxconn 32
server s2 123.456.789.1:443 maxconn 32
backend srv_stat
server Local 127.0.0.1:1936
When going to your server with www, it takes you to the web server.
But using stat, it redirects you from your input port 8080 to 1936 whee stat is running
This is just an educated guess. You can't serve the stats page in tcp mode because it's proxying at layer4. In this mode, haproxy only knows IPs and ports from incoming packets and routes it accordingly based on defined rules.
Unlike http mode (layer7), it has more info to work on like HTTP headers where path is available and use it to know when to serve the /haproxy?stats page.
If you are happy to use a your path, it really easy.
this should work
global
log 127.0.0.1 local2
daemon
maxconn 256
defaults
log global
timeout connect 5000
timeout client 10000
timeout server 10000
frontend http-in
bind *:8080
stats enable
stats uri /stats
default_backend testb
backend testb
balance roundrobin
server s1 123.456.789.0:443 maxconn 32
server s2 123.456.789.1:443 maxconn 32
you can then access the haproxy stats from
http://< hostname >:8080/stats
(tested on haproxy 2.5.5)

Resources