Proxying TCP by hostname
I've got multiple game servers TCP ports on my single host machine. The goal is to have users be able to connect to server1.domain.net and have their directed based on that subdomain. My first instinct wrote the following but then I realised that TCP traffic isn't going to have any header to read. Using HAProxy 1.5.8. I tried doing this same thing using multiple backends, use_backend and full ACL lines but got the same result (understandably).
listen game-listener
bind x.x.x.x:22222
mode tcp
use-server server1 if { hdr(host) -i server1.domain.net }
use-server server2 if { hdr(host) -i server2.domain.net }
server server1 localhost:22201 check
server server2 localhost:22202 check
Is there a check like hdr(host) that I can use for TCP connections? Or am I doing it right and the game just isn't playing nice?
Thanks!
Solution 1:
As an alternative to proxying (where it depends on the application protocol if it's possible to do based on hostnames) you may want to check if the client software is SRV
aware, in which case you should be able to set this up in DNS alone.
An SRV
record has the following format:
_Service._Proto.Name TTL Class SRV Priority Weight Port Target
In your specific example where you mentioned multiple instances of Minecraft it should be possible to do this based on SRV
records and the records could look something like this:
_minecraft._tcp.foo.example.com. 86400 IN SRV 0 5 25565 server1.example.com.
_minecraft._tcp.bar.example.com. 86400 IN SRV 0 5 25566 server1.example.com.
Solution 2:
Dispatching connections to different backends depending on the hostname which the client connected to is impossible to do on the TCP layer.
You will have to either use separate IP addresses for each hostname or implement the proxying at the application layer with protocol specific code to detect the hostname.
Such a proxy is possible for certain protocols but not for others. Ranging from easy to impossible, the protocols I know enough about are:
- HTTP is easy to support. All HTTP/1.1 clients and most HTTP/1.0 clients will send a host header before expecting any answer answer from the server. Clients without host header are likely long gone, since many sites these days will not work without it.
- HTTPS can be supported for all clients with SNI support. SNI was standardized much more recently than HTTP/1.1, there are still a few clients around without SNI support.
- DNS is possible to support but slightly tricky. In most cases you'd just operate your authoritative DNS server directly on a public IP address instead of proxying.
- SMTP is possible to support but also tricky. A proxy for SMTP would likely end up looking more like an SMTP relay than like a proxy.
- SSH is impossible to support. The concept of hostnames simply doesn't exist in the protocol. The client may use DNS to resolve the IP address of the server, and it may associate stored host keys with their host names, but this is entirely an implementation detail on the client side. This would not be visible to the proxy, so it cannot be used to dispatch connections. Moreover no useful information is send in cleartext, and even the few pieces of information that are send in cleartext at the start of the SSH protocol are authenticated later in the protocol, so if you get any part of that wrong, the communication breaks. What makes this even more impossible to handle is the fact that the client does not send even one byte of payload until the server has send a banner back to the client. So the proxy would have literally no information to work with.
I am not aware of any other protocol with a hostname, which could be used for such purposes. So I am guessing that most protocols not on the above list are impossible to proxy by hostname.