2

I'm currently building my own little cluster at home and I'm trying to get SSH routing (load balancing) to work with HAProxy. I have figured out how to route HTTP traffic but I'm struggling with SSH. I do not know enough about the TCP protocol to instantly know what I have to look for so that I can determine how to query connections for something like a host (not sure if that is even somewhere in the connection) or just anything that would identify which server I want to SSH into.

The documentation mentions SSH traffic over and over again so there might be something I missed. I do not want to use different ports and route back and forth using different ports because they might not be standard are therefore blocked on public networks or other stuff that would restrict me.

My current setup looks like this:

Request on my domains (or IP) -> Router -> NAT forward depending on port -> HAProxy listening on that port -> should now identify traffic that is dedicated for a certain server and proxy traffic towards it.

Edit: As Cha0s has cleared up this is simply not possible with SSH (or alike).

If you are interested in a different method Sven's answer give good insight in doing something similar but having different subdomains resolve to different public static IPs

3 Answers3

3

I believe you are mixing up terms.

Load balancing typically means you connect to one host (example.com) and let the load balancer distribute all connections to one or more backend hosts, usually without user control of which actual host you end up connecting to. This can easily be done for SSH in various ways.

Some protocols, like HTTP with the Host header, can give the load balancer more information to decide which backend servers the connection should be routed to (e.g. example.com gets routed to a and b, while example.org gets routed to c and d).

SSH doesn't have such a field and therefore, this can't be done with a load balancer and only one IP address. This makes an LB unsuitable for this task.

Typically, this is solved by using a host as an SSH proxy/gateway/jump host and instruct your SSH clients to use it as such. If you use OpenSSH, put the following into your clients ~/.ssh/config

Host  *.internal.lan
   ProxyCommand ssh -q -A  -x proxy.example.com -W %h:%p

Now, if you connect to the names listed in the Host line, your SSH client will first connect to proxy.example.com (which must point to your public IP address) and use it as a gateway to forward to the actual machine (use NAT port forwarding to port 22 of your actual proxy machine if applicable). Note that proxy.example.com must be able to resolve *.internal.lan. Either setup a DNS server or use the internal one offered by your router to achieve this.

After that, from an external network you connect to nas.internal.lan to reach your NAS (or raspi.internal.lan etc...), and from inside your local network you just use nas or raspi (so the connection is direct and not via the gateway).

Another way, given the fact you use NAT:

Create port forwardings in your NAT router, e.g. like

Port 2210 -> internal host1, port 22
Port 2211 -> internal host2, port 22

and then put the following into your clients ~/.ssh/config:

Host host1.example.com  
   Port 2210

Host host2.example.com 
   Port 2211

and make sure host1/2.example.com both point to your external IP address.

Then you can just connect via SSH to host1.example.com and OpenSSH will connect to port 2210, which gets forwarded to your interal host1 by the NAT gateway.

Both methods work in your scenario.

Sven
  • 1,669
0

So, generally speaking, yes, only HTTP can determin destination hostname in HAProxy... However, if the TCP client connecting to HAProxy sends the destination address (IP or FQDN), it is possible to scrap that information and use it for routing purposes.

I present my Minecraft shared-port poker dealer:

#starting from top of file, for completeness.
    global
       user haproxy
       group haproxy
#setting tcp mode here is required, won't work if in frontend area!
    defaults
       mode tcp
#setting up the HAProxy listener:
    frontend frontend-name
       bind :25565
       tcp-request inspect-delay 3s
#ACLs for 1.9 and newer clients:
        acl mojang req.payload(5,16) -m sub sub1.domain.tld1
        acl bungee req.payload(5,16) -m sub sub2.domain.tld1
        acl bungee req.payload(5,16) -m sub sub1.domain.tld2
        acl nukkit req.payload(5,16) -m sub sub3.domain.tld1
#ACLs for 1.8 and older clients, keeping pinger working:
        acl mojang req.payload(4,16) -m sub sub1.domain.tld1
        acl bungee req.payload(4,16) -m sub sub2.domain.tld1
        acl bungee req.payload(4,16) -m sub sub1.domain.tld2
        acl nukkit req.payload(4,16) -m sub sub3.domain.tld1
#Accept connections if matching an ACL above:
        tcp-request content accept if bungee
        tcp-request content accept if mojang
        tcp-request content accept if nukkit
#Route connection to appropriate backend server:
        use_backend minecraft.bungee if bungee
        use_backend minecraft.mojang if mojang
        use_backend minecraft.nukkit if nukkit
#backend servers - one bungee network, one notchian server, one nukkit server:
    backend minecraft.bungee
    #here we're directing the connection to bungee on 127.0.0.1:25566
    #We're also sending the proxy-protocol(v2) to bungee (check conf!)
       server bungee-server 127.0.0.1:25566 send-proxy-v2
    backend minecraft.mojang
    #Mojang servers can't use the proxy-procotol, so not sending it
       server mojang-server 127.0.0.2:25567
    backend mincraft.nukkit
    #nukkit will never work here, as Bedrock is based on UDP, not TCP!
       server sponge-server 127.0.0.3:25568
#EoF

This is a basic setup, and works. My in-productions setup is considerably different, using options my setup requires.

Now, it just comes down to - can you find the destination host in the packet so you can look for it with HAProxy's payload?

AeSix
  • 1
0

AFAIK you cannot do this based on the domain. HTTP can work like that because it has a special Host header which the browsers (an reverse proxies in this case) can understand and make decisions based on it.

On other TCP protocols I am not aware of anything similar. Definitely not on SSH.

Cha0s
  • 351