HAProxy Configuration – Add URL Parameter to ACL for Different Ports

access-control-listconfigurationhaproxyPROXYreverse-proxy

I have several services that run on different ports, each of which use the same URI paths. For example:

New York Housing Service
127.0.0.1:8080/homes
127.0.0.1:8080/prices

Las Vegas Housing Service
127.0.0.1:8081/homes
127.0.0.1:8081/prices

This has been fine so far, but I now need to set up haproxy to load balance the services. As such, I obviously need to be able to differentiate them for content switching. What I imagine I would do is add a parameter to the path in the ACL to differentiate between the two backends, in this case by having a url parameter in the ACL, which would be followed by the actual path parameters for the application:


frontend http
  maxconn 2000
  bind 0.0.0.0:5000  

  acl new-york path_reg -i /newyork.*
  use_backend nyc-server if new-york

  acl las-vegas path_reg -i /lasvegas.*
  use_backend lv-server if las-vegas

backend nyc-server
  server www.test.com 127.0.0.1:8080 maxconn 100

backend lv-server
  server www.test.com 127.0.0.1:8081 maxconn 100

In this setup, going to 127.0.0.1:5000/newyork/home will take me to 127.0.0.1:8080/home, while 127.0.0.1:5000/lasvegas/home takes me to 127.0.0.1:8081/home. My attempts so far have simply returned a 404 error. I've been been going through the documentation, but I did not see anything quite matching my use case, so any help would be greatly appreciated.

EDIT:
I forgot to mention that I'm using haproxy 1.5.18

Best Answer

The backend services are answering with HTTP 404 errors because your HAPROXY configuration is currently forwarding URL's path as they are. For example, a HTTP request for http://127.0.0.1:5000/newyork/home/wtc.png is being forwarded to nyc-server backend as http://127.0.0.1:8000/newyork/home/wtc.png.

$ wget -4O /dev/null http://localhost:5000/newyork/homes/wtc.png
--2020-02-01 13:00:09--  http://localhost:5000/newyork/homes/wtc.png
Resolving localhost (localhost)... 127.0.0.1, 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:5000... connected.
HTTP request sent, awaiting response... 404 File not found
2020-02-01 13:00:09 ERROR 404: File not found.
$ python -m http.server 8080
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...
127.0.0.1 - - [01/Feb/2020 13:00:09] code 404, message File not found
127.0.0.1 - - [01/Feb/2020 13:00:09] "GET /newyork/homes/wtc.png HTTP/1.1" 404 -

HAproxy should be configured to translate URLs' paths by stripping the first path component while fetching resources from backends. I would suggest you to add a reqrep directive into frontend's definition, which is meant to manipulate the first line of the HTTP request header and translate something like GET /newyork/homes/wtc.png HTTP/1.1 into GET /homes/wtc.png HTTP/1.1.

frontend http
  reqrep ^([A-Z]+)\ /+[^/]+(/+.*)?$  \1\ \2

However, it won't work because HAproxy evaluates reqrep directives before use-backend ones, thus breaking backend evaluation:

$ wget -4O /dev/null http://localhost:5000/newyork/homes/wtc.png
--2020-02-01 13:54:55--  http://localhost:5000/newyork/homes/wtc.png
Resolving localhost (localhost)... 127.0.0.1, 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:5000... connected.
HTTP request sent, awaiting response... 503 Service Unavailable
2020-02-01 13:54:55 ERROR 503: Service Unavailable.

A second working approach is rewriting URL path in backend definitions. However, depending on the number of backends in service, writing reqrep directives and configuring each backend to perform URL rewrite are prone to error.

backend nyc-server
  server www.test.com 127.0.0.1:8080 maxconn 100
  reqrep ^([A-Z]+)\ /+[^/]+(/+.*)?$  \1\ \2

backend lv-server
  server www.test.com 127.0.0.1:8081 maxconn 100
  reqrep ^([A-Z]+)\ /+[^/]+(/+.*)?$  \1\ \2
$ wget -4O /dev/null http://localhost:5000/newyork/homes/wtc.png
--2020-02-01 14:02:29--  http://localhost:5000/newyork/homes/wtc.png
Resolving localhost (localhost)... 127.0.0.1, 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:5000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 456892 (446K) [image/png]
Saving to: ‘/dev/null’

/dev/null        100%[===============>] 446.18K  --.-KB/s    in 0s      

2020-02-01 14:02:29 (1.83 GB/s) - ‘/dev/null’ saved [456892/456892]

$ python -m http.server 8080
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...
127.0.0.1 - - [01/Feb/2020 14:02:29] "GET /homes/wtc.png HTTP/1.1" 200 -
Related Topic