7

I am exploring the use of HAProxy as a balancer in front of a set of web apis that run on IIS.

  • node1.myapp.mycompany.com
  • node2.myapp.mycomapny.com

We are currently using Host Headers to correctly resolve the right web application on IIS. For example, we may have otherapp.mycompany.com running on the same server/port, but using Host Headers IIS knows which one to serve up.

My initial attempt at HAProxy left me with 404 errors, because I was not including the host headers in my request, and so IIS was serving back the default site bound to the given IP, and not resolving by host name.

The fix left me with something like this:

frontend localnodes
    bind *:80
    mode http
    default_backend nodes

backend nodes
    mode http
    balance roundrobin
    option forwardfor
    reqirep ^Host: Host:\ node1.myapp.mycompany.com
    server web01 node1.myapp.mycompany.com:80

This works great for a single backend, but I am left scracthing my head on how to include the correct host headers depending on which backend is serving up the request:

backend nodes
    mode http
    balance roundrobin
    option forwardfor
    reqirep ^Host: Host:\ node1.myapp.mycompany.com
    server web01 node1.myapp.mycompany.com:80
    server web02 node2.myapp.mycompany.com:80

The above does not work whenever node2 is used because it doesn't resolve correctly in IIS (node2 in this case is on a different server, and therefore different IP. In fact, if they were running on the same server/ip I suspect the request work "work", but they would all be served by node1 per the host header).

  • Is it possible to set the host based on the url of the backend used to serve up the request?
  • If so, this implies my checks also need to provide the correct host header based on backend, how would that be done?

Edit: Am using HA-Proxy version 1.7.9 2017/08/18

Let me give this just a little more background. We used to have just myapp.mycompany.com serving up api requests. We need to expand it. Our clients will still call myapp.mycompany.com, but that will just point to the HAProxy, which will balance it out to node1.myapp.mycompany.com and node2.myapp.mycompany.com. Our current server configurations have us binding a single IP address on each server, so we use host headers to resolve.

Matt
  • 3,171
  • 9
  • 28
  • 33
  • Which version are you using? – gxx Oct 04 '17 at 15:36
  • @gf_ 1.7.9 from 2017/08/18 – Matt Oct 04 '17 at 15:40
  • Do I understand correctly, that `node{1,2}.myapp.mycompany.com` are the `HTTP host` headers, the clients are using? If so, why is `node{1,2}` relevant, why is it not only `myapp.mycompany.com`? This would simplify things a lot, IMHO. – gxx Oct 04 '17 at 15:43
  • So, from the clients perspective, they will be calling myapp.mycompany.com. However, the webapps themselves are hosted with the additional subdomain. This is hold over from the current networking structure where we bind multiple apps to the same IP/port and need the headers to differentiate. Does that answer your question? – Matt Oct 04 '17 at 15:51
  • @gf_ added some additional clarifying info in the question at the bottom – Matt Oct 04 '17 at 15:57
  • Thanks, that helped. Still, I'm not sure why there is this need, because, IMHO, it's quite possible to bind multiple vhosts to the same IP / port. This is at least true for the webservers I'm dealing with, which is `Nginx` normally, so I guess this is true and applies to `IIS`. – gxx Oct 05 '17 at 08:43

2 Answers2

15

I'm not sure if the following will work, and can't test right now, but maybe this is still helpful. (I'll have a look again later with some more time):

Solution 1:

backend nodes
    mode http
    balance roundrobin
    option forwardfor
    http-request set-header Host node1.myapp.mycompany.com if { srv_id 1 }
    http-request set-header Host node2.myapp.mycompany.com if { srv_id 2 }
    server web01 node1.myapp.mycompany.com:80
    server web02 node2.myapp.mycompany.com:80

Solution 2:

Note This one is officially not recommended. Read the comment on http-send-name-header: it has been reported that this directive is currently being used as a way to overwrite the Host header field in outgoing requests; while this trick has been known to work as a side effect of the feature for some time, it is not officially supported and might possibly not work anymore in a future version depending on the technical difficulties this feature induces... [Credits to rustyx.]

backend nodes
    mode http
    balance roundrobin
    option forwardfor
    http-send-name-header Host
    server node1.myapp.mycompany.com node1.myapp.mycompany.com:80
    server node1.myapp.mycompany.com node2.myapp.mycompany.com:80
gxx
  • 5,483
  • 2
  • 21
  • 42
  • 1
    Thanks, your 2nd example did the trick for me. Couldn't get the first one to work, though I don't remember what the error was right off hand (I think something to do with the conditional expression). – Matt Oct 16 '17 at 18:49
  • @Matt Thanks.. could you still get the "proper" error message? I'm curious.. – gxx Oct 16 '17 at 20:40
  • 1
    @gf_ none of these solutions work for me. In the first solution, it never sets the header. If I remove de `if` then it works but I have two backend servers so I need the if. The second solution does not add the Host and I get a 404 because IIS cannot find the right binding. Could you make Solution 1 work? – Rubanov Jul 06 '18 at 15:25
  • @Rubanov I'm pretty sure this worked, for the question raised back then. If it doesn't for you, there might be differences. Please ask a new question giving details etc. – gxx Jul 06 '18 at 15:58
  • @gf_ the first solutions works if there is only one server. So when I do `http-request set-header Host node1.myapp.mycompany.com` I do not get a 404 and IIS correcty binds to right host. It is the `srv_id` variable that is not working. It is never equal to 1 or 2. I don't know what it is equal to but the acl is always false because it does not do the set-header. If you have no idea what might be wrong with `srv_id` then I will ask a new question. – Rubanov Jul 06 '18 at 16:32
  • @Rubanov Which version are you using? – gxx Jul 07 '18 at 00:43
  • @gf_ I've decided to follow your advice and I created a new question here https://serverfault.com/questions/919914/haproxy-using-iis-on-same-port-with-different-host-headers – Rubanov Jul 07 '18 at 12:58
  • 1
    @gf_ turns out that `http-send-name-header Host` did work. I just didn't configure it correctly, I was using `server mysite1Server node1.mysite.com:443 check ssl verify none` and it should have been `server node1.mysite.com node1.mysite.com:443 check ssl verify none`. Sorry for wasting your time. Might be a good idea to remove solution 1 from the answer because that one does not seem to be working. – Rubanov Jul 07 '18 at 18:20
  • @Rubanov Great you were able to get it to work. I won't remove solution 1 as it works for me. – gxx Dec 15 '18 at 17:14
  • This is an awesome answer - so precise and thorough. You deserve a medal! – A X Aug 10 '19 at 17:54
  • 1
    Solution 2 is officially not recommended. Read the comment on [http-send-name-header](https://cbonte.github.io/haproxy-dconv/2.3/configuration.html#4-http-send-name-header): *it has been reported that this directive is currently being used as a way to overwrite the Host header field in outgoing requests; while this trick has been known to work as a side effect of the feature for some time, it is not officially supported and might possibly not work anymore in a future version depending on the technical difficulties this feature induces...* – rustyx Jun 28 '21 at 08:49
  • @rustyx Thanks for the addition, I'll add this to the answer to make it more visible. – gxx Jul 12 '21 at 14:42
1

I want to complement gf_ answer. So the idea of his answer is to add a custom Host header which value is the source hostname itself. In solution one, basically, he set the header manually with a condition from srv_id thing.

The second on shortcut the first solution by sending the name of the host as a Host header.

But in his solution, he doesn't mention that it only set-header, if the header is already there it can't replace the existing header, so if your case need to replace Host header, you can do somethinghing like this

backend nodes
    mode http
    balance roundrobin
    option forwardfor
    http-request replace-header Host node1.myapp.mycompany.com node1.myapp.mycompany.com if { srv_id 1 }
    http-request replace-header Host node2.myapp.mycompany.com node2.myapp.mycompany.com if { srv_id 2 }
    server web01 node1.myapp.mycompany.com:80
    server web02 node2.myapp.mycompany.com:80
abmap
  • 11
  • 2
  • "if the header is already there it can't replace the existing header" not true. See https://cbonte.github.io/haproxy-dconv/2.2/configuration.html#4.2-http-request%20set-header and there is "the header name is first removed if it existed". – Andrey Regentov Jul 23 '21 at 04:24