jbrahy
jbrahy

Reputation: 4415

Dynamic server name and header in HAProxy

I’m looking for the equivalent of this backend code block below for requests to www.example.com and example.com.

http-response set-header X-Target  example.com
server web-servers  site.example.com:80 check

I take all the requests to www.example.com but I want to serve them to site.example.com using haproxy. There are several variations of example.com so I would like to have a list of allowed domains and then if they're allowed I would like to have a backend code block like below where I could use %[req.hdr(Host)] as the value in the http-response X-Target statement.

http-response set-header X-Target  %[req.hdr(Host)]
server web-servers  site.%[req.hdr(Host),regsub(^www.,,)]:80 check

HA-Proxy version 2.1.4-273103-54 2020/05/07 - https://haproxy.org/

I’m getting this error when I try haproxy -c -f haproxy.test

[root@pm-prod-haproxy05 haproxy]# haproxy -c -f haproxy.test [ALERT] 259/180932 (16116) : parsing [haproxy.test:40]: ‘http-response set-header’: sample fetch may not be reliably used here because it needs ‘HTTP request headers’ which is not available here. [ALERT] 259/180932 (16116) : Error(s) found in configuration file : haproxy.test [root@pm-prod-haproxy05 haproxy]#

I've also tried this:

http-request set-header X-Target  %[req.hdr(Host)]
http-request set-header X-Hostname %[req.hdr(Host),regsub(^www.,site.,)]
http-request web-server do-lookup(hdr(X-Hostname))
server web-servers  web-server:80 check

This is my full configuration.

global
log         127.0.0.1 local2 debug
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    daemon
    stats socket /var/lib/haproxy/stats

defaults
    mode                    http
    option                  httplog
    log                     global
    option                  dontlognull
    option                  http-server-close
    option                  redispatch
    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

frontend frontend-http

    bind *:80
    bind *:443

    acl redirect path_beg -i /rd
    use_backend backend-tracking if redirect

default_backend backend-default

backend backend-default
    option forwardfor
    http-response set-header X-Publishing-system website
    http-response set-header X-Target  %[req.hdr(Host)]
    server web-servers  site.%[req.hdr(Host),regsub(^www.,,)]:80 check

backend backend-tracking
    option forwardfor
    http-response set-header X-Publishing-system redirect
    http-request set-uri %[url,regsub(^/rd,/,)]
    server web-hp www.trackingplatform.com:80 check

Upvotes: 0

Views: 8813

Answers (1)

Aleksandar
Aleksandar

Reputation: 2642

About Header manipulation

As the ALERT message say you can't use request header in the response. You should replace the following line.

Wrong line

http-response set-header X-Target  %[req.hdr(Host)]

Right Line

http-request set-header X-Target  %[req.hdr(Host)]

The Backend-Server should not remove this header. If you not want to send the Backend-Server the 'X-Target' host header then can you use a session variable to save the host header from the request to the response phase.

http-request set-var(txn.my_host) req.hdr(host),lower
http-response set-header X-Target %[var(txn.my_host)]

In the documentation are the set-var and set-header directive quite good explained.
http://cbonte.github.io/haproxy-dconv/1.8/configuration.html#4-http-request

About the server manipulation

This line could not work because haproxy tries to resolve the target server at start time.

server web-servers site.%[req.hdr(Host),regsub(^www.,,)]:80 check

In newer version of haproxy. like 2.1, can you dynamically resolve and set the destination hosts.
http://cbonte.github.io/haproxy-dconv/2.1/configuration.html#4.2-http-request%20do-resolve

I assume you want to change the host header for the target server that the right virtual server is used.
My suggestion to solve your issue is to change the host header and set the server name to a resolvable address.

backend backend-default
  option forwardfor

  http-response set-header X-Publishing-system website

  http-request set-header X-Target %[req.hdr(Host)]

  http-request replace-header Host ^www(.*) site.\1
  http-request set-header X-NewTarget %[req.hdr(Host),regsub(^www.,,)]

  server web-servers  site.example.com:80 check

This backend config is only syntax checked.

About dynamic backend server

The server should be resolved dynamically. For that solution is at least HAProxy 2.0 necessary.

I copy here some parts of the doc http-request do-resolve for this answer.

You will need to add a section resolvers to your config

resolvers mydns
  # use here your prefered DNS Servers
  nameserver local 127.0.0.53:53
  nameserver google 8.8.8.8:53
  timeout retry   1s
  hold valid 10s
  hold nx 3s
  hold other 3s
  hold obsolete 0s
  accepted_payload_size 8192

frontend frontend-http

  bind *:80
  bind *:443

  # define capture buffer for backend
  declare capture request len 60

  acl redirect path_beg -i /rd
  use_backend backend-tracking if redirect

  default_backend backend-default

# ... some more backends

backend backend-default
  option forwardfor

  http-response set-header X-Publishing-system website

  http-request set-header X-Target %[req.hdr(Host)]

  # replace www with site in host header
  http-request replace-header Host ^www(.*) site.\1

  # if necessary set X-NewTarget header
  http-request set-header X-NewTarget %[req.hdr(Host),regsub(^www.,,)]

  # do dynamic host resolving for dynamic 
  # server destination for 
  # the replaced Host Header above 
  http-request do-resolve(txn.myip,mydns,ipv4) hdr(Host),lower

  # print the resolved IP in the log
  http-request capture var(txn.myip) id 0

  # rule to prevent HAProxy from reconnecting to services
  # on the local network (forged DNS name used to scan the network)
  # add the IP Range for the destination host here
  http-request deny if { var(txn.myip) -m ip 127.0.0.0/8 10.0.0.0/8 }
  http-request set-dst var(txn.myip)

  server clear 0.0.0.0:0

Please take care about the note in the documentation

NOTE: Don't forget to set the "protection" rules to ensure HAProxy won't be used to scan the network or worst won't loop over itself...

Upvotes: 3

Related Questions