Haproxy 2.2.9 not passing down the full url to kubernetes ingress

dietpi@jump02:~$ cat /etc/hosts localhost myapp.myproject.local jump02
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

Step 1: Confirm ingress routes are working as expected
Here is something important about how my ingress rules are working, it is important that the user inputs the URL url path to hit the correct server:

dietpi@jump02:~$ curl myapp.myproject.local
404 page not found
dietpi@jump02:~$ curl myapp.myproject.local/desktop
<head><title>301 Moved Permanently</title></head>
<center><h1>301 Moved Permanently</h1></center>
dietpi@jump02:~$ curl myapp.myproject.local/desktop/
dietpi@jump02:~$ curl myapp.myproject.local/mobile/

Step 2: try to access the ingress routes through haproxy

$ curl http://mypersonalsite.duckdns.org:12434/desktop/
404 page not found
$ curl http://mypersonalsite.duckdns.org:12434/mobile/404 page not found

Ok so the good news is that the haproxy is listening on port 12434 and I know for sure that the 404 error is actually being returned by the Traefic ingress (due to experience) - unfortunately I cannot view any logs from Traefic due to a misconfiguration on the latest implementation of Kubernetes done by K3s…

I checked the logs of the servers running nginx and the request never makes it to them. This suggests that there is a configuration issue in haproxy, the rest of the url (the “/desktop/” part) is not being sent from haproxy to the kubernetes ingress backend, and the kubernetes backed responds to haproxy with a 404 not found. How do I get the full url including the /desktop/ part of the url that I would type into the browser to flow thru haproxy?


My haproxy.cfg:

global chroot /var/lib/haproxy user haproxy group haproxy daemon stats socket /run/haproxy/admin.sock mode 660 level admin log /dev/log local0 log /dev/log local1 notice ssl-default-bind-options no-sslv3 no-tls-tickets no-tlsv10 no-tlsv11 ssl-default-bind-ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS ssl-default-server-options no-sslv3 no-tls-tickets no-tlsv10 no-tlsv11 ssl-default-server-ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS tune.ssl.default-dh-param 2048

#Ansible managed
mode http
log /dev/log local1 notice
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
option httpclose
option forwardfor except
option redispatch
option abortonclose
option httplog
option dontlognull
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

#Ansible managed
backend be-myproject
server be-myproject myapp.myproject.local:80
#Ansible managed
frontend fe-myproject
mode http
maxconn 3000
acl acl-myproject hdr(host) -i mypersonalsite.duckdns.org:12434
use_backend be-myproject if acl-myproject

here is what my ingress for the /desktop looks like, its the same thing for mobile

apiVersion: networking.k8s.io/v1
kind: Ingress
  name: webserver-desktop
    kubernetes.io/ingress.class: "traefik"
    #Leaving this as a reference comment in case I want to rewrite urls in the future
    traefik.ingress.kubernetes.io/rewrite-target: /desktop
    - host: "myapp.myproject.local"
          - pathType: Prefix
            path: /desktop
                name: webserver-desktop
                  number: 80

Traefik almost certainly requires the host header to be myapp.myproject.local as oppossed to mypersonalsite.duckdns.org:12434 which curl will by default send here.

So, in backend be-myproject rewrite the host header to that:

http-request set-header Host myapp.myproject.local

Thanks Lukas, you’re a reverse proxy genius.

1 Like