HAProxy TCP Proxy (Minecraft)

Vorwort

Ich habe mich vor kurzen von einem meiner NGINX Reverse Proxy’s verabschiedet und diesen gegen einen HAProxy ersetzt.

In letzter Zeit habe ich versucht, das Deployment von meinen Minecraft Servern zu vereinfachen.
Seit einiger Weile benutze ich hier einen auf das Spiel zugeschnittenen Proxy. Dieser funktioniert leider nicht mit allen Versionen.

HAProxy besitzt zwei grundlegende Modi, in denen der Proxy arbeiten kann. HTTP und TCP.
Minecraft nutzt für die Kommunikation mit dem Client (im Gegensatz zu vielen anderen Spielen) das TCP Protokoll.

Da HAProxy TCP Paketdaten analysieren kann, können wir damit, auf Basis des Payloads, Pakete weiterleiten (oder auch blockieren)

TCP Analyse

Als Erstes müssen wir einen Punkt im TCP Paket finden, mit dem wir erkennen, welchen Server der Client versucht zu erreichen.

Hierfür führe ich auf meinem Server entsprechend den TCPDUMP Befehl aus: (Wireshark auf dem Client funktioniert auch entsprechend)

sudo tcpdump -i enp6s0.2 -Q in -w ./tcpdump.pcap 'tcp port 61400'

Mein Server läuft hier auf Port 61400.
Meine Netzwerkkarte den Namen „enp6s0.2“
Der Output von TCPDUMP wird unter dem aktuellen Pfad als tcpdump.pcap gespeichert.

Die PCAP-Datei können wir dann in Wireshark öffnen.

Bei Minecraft sendet der Client eine Anfrage an den Port mit dem angefragten FQDN im Payload (Payload = Data).

Das ist perfekt, da das Paket nur einer Session angehören muss, damit wir die gesamte Session umleiten können.

Anlegen der HAProxy Konfiguration

Anhand des Pakets habe ich nun eine Konfiguration für HAProxy angelegt.

frontend minecraft
   mode tcp #Den Modus auf TCP setzen
   bind :25565 #Minecraft nutzt standardmäßig den Port 25565

   tcp-request inspect-delay 100ms #Verzögert die Zustellung zum Backend, damit Pakete analysiert werden können
   acl is_test req.payload(5,0) -m sub test.mc.sleepeehead.club #Siehe unter dieser box
   tcp-request content accept if WAIT_END #Akzeptiert das TCP Paket, wenn der inspect-delay erreicht ist
   use_backend mc-test if is_test #Nutzt das backend "mc-test" wenn die ACL "is_test" OK zurück gibt

Das Sample req.payload hat 2 Werte, die wir ihm übergeben können. Der erste ist das Offset Byte aka ab welcher Stelle der Wert beginnt mit dem wir vergleichen. Der zweite gibt die Länge an. 0 = Maximale Länge
Ich empfehle, die Länge entsprechend zu wählen. Das beschleunigt die Verarbeitung.

In meinem Payload sind die Daten wie folgend:
1f00f80518746573742e6d632e736c6565706565686561642e636c756263dd01

Mit dem Offset 5, beginnt es ab dem Byte nr. 5 (aka 6. Stelle). Also sieht der Wert mit dem HAProxy die ACL bearbeitet so aus:
746573742e6d632e736c6565706565686561642e636c756263dd01

In ASCII sieht das dann so aus: (manche Kontrollzeichen fehlen)
test.mc.sleepeehead.clubcÝ

Der Wert wird von HAProxy automatisch in ASCII umgewandelt und kann direkt mit -m sub verglichen werden.
Wer das Vergleichen in HEX machen möchte, kann das so:
acl is_test req.payload(5,0),hex -m sub 746573742e6d632e736c6565706565686561642e636c7562

Weitere Erklärung gibts hier:
https://cbonte.github.io/haproxy-dconv/2.6/configuration.html#req.payload
und hier:
https://cbonte.github.io/haproxy-dconv/2.6/configuration.html#7.1.3

Das Backend können wir ganz simpel anlegen:

backend mc-test
  mode tcp
  server mc-test 192.168.4.20:61400

Falls der Server im Backend das Proxy-Protokoll spricht, kann sogar die eigentliche Client-IP übertragen werden.
Bei Minecraft kann das der Velocity Proxy.

Das kann aktiviert werden, indem man send-proxy der Server Zeile hinzufügt:
server mc-test 192.168.4.20:61400 send-proxy

Dann wieder die Konfiguration prüfen

sudo haproxy -f /etc/haproxy/haproxy.cfg -c

und dann den HAProxy reloaden

sudo systemctl reload haproxy

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert