Heute bauen wir einen hochverfügbaren Socks-Proxy. Dazu brauchen wir vier VMs:

2 x Loadbalancer
2 x Socks Proxy

In diesem Beispiel nehme ich dafür Debian LXC Container unter Proxmox VE. LXC Container sind tatsächlich wenn man das richtig macht einfacher zu handhaben als Docker-Container und der Ressourcenverbrauch ist ähnlich gering. Die Maschinen laufen verteilt in einem Proxmox VE Cluster Verband. Wie man das macht, schreib ich nochmal, da gibts aber auch echt brauchbare Youtube-Tutorials.

Die verwendeten Programme gibts alle als Debian-Paket. Ohne zwingende Gründe selbst compilieren ist nicht zu empfehlen, denn das erhöht den Verwaltungsaufwand erheblich.

Innerhalb der Container lässt man am besten unattended-upgrades laufen und ja, man kann die Maschinen auch (scheduled) booten lassen, wenn sie das z.B. nach nem Kernel-Update müssen.

Konfiguration Loadbalancer

Der Loadbalancer ist ein VRRP-Cluster mit keepalived. Der hat eine VIP (192.168.12.14), auf der ein haproxy als Frontend läuft. Den haproxy lässt man am besten (siehe unten) vom keepalived notify-Skript starten und stoppen, wenn der Cluster Member zum Master bzw. Backup wird. Das vermeidet unnötige systemd-Fehler. 😉

keepalived.conf

Das ist die Keepalived Konfiguration des Master-Knotens:

global_defs {
        router_id proxy
        script_user root
        enable_script_security
}
vrrp_instance proxy {
	state MASTER
	interface eth0
	virtual_router_id 51
	priority 100
	advert_int 1
	authentication {
		auth_type PASS
		auth_pass safe234
	}
	virtual_ipaddress {
		192.168.12.14
	}
	notify /etc/keepalived/notify.sh
}

Die Konfiguration des Backup-Knotens ist bis auf diese zwei Ausnahmen mit der Master-Konfiguration identisch: Der Backup-Knoten kriegt den initialen Status “BACKUP” und eine niedrigere Priorität, so dass er wirklich erst aktiv wird, wenn er keinen Master mehr sieht.

	state BACKUP
	priority 90

Hinweis: Dies ist eine ganz simple Beispiel-Konfiguration. In einer produktiven Umgebung sollte man unter Anderem andere Authentisierungsmechanismen verwenden und man könnte den VRRP-Traffic auch mit iptables/nftables filtern. Da gibts Einiges zu “feilen”.

notify.sh

Das Notify-Skript wird bei einem Status-Wechsel aufgerufen, also zum Beispiel wenn der Backup-Knoten zum Master wird etc.

#!/bin/bash

ENDSTATE=$3
NAME=$2
TYPE=$1

case $ENDSTATE in
    "BACKUP") # Perform action for transition to BACKUP state
              systemctl stop haproxy
              echo $ENDSTATE > /var/run/keepalived.state
              exit 0
              ;;
    "FAULT")  # Perform action for transition to FAULT state
              echo $ENDSTATE > /var/run/keepalived.state
              exit 0
              ;;
    "MASTER") # Perform action for transition to MASTER state
              systemctl start haproxy
              echo $ENDSTATE > /var/run/keepalived.state
              exit 0
              ;;
    *)        echo "Unknown state ${ENDSTATE} for VRRP ${TYPE} ${NAME}"
              echo $ENDSTATE > /var/run/keepalived.state
              exit 1
              ;;
esac

Das Sichern des aktuellen Status ist optional, ich frage den mit CheckMK ab und das geht schneller, wenn man da einfach eine Datei ausliest anstatt die kompletten Runtime Daten mit kill -USR1 $(cat /var/run/keepalived.pid) in eine Datei zu ballern und die da wieder rauszugreppen.

haproxy.cfg

Die haproxy-Konfiguration ist auf beiden Knoten identisch, denn der läuft eh nur immer auf dem aktiven Knoten und lauscht auf die Cluster-VIP.

frontend socks
        bind 192.168.12.14:1080
        mode tcp
        option tcplog
        default_backend sockies

[...]

backend sockies
        option tcp-check
        tcp-check connect
        tcp-check send-binary 050100
        tcp-check expect binary 0500
        balance source
        hash-type consistent
        server pix1 192.168.12.21:1080 check inter 30s weight 100
        server pix2 192.168.12.22:1080 check inter 30s weight 0`

Die tcp-check Zeilen machen einen Layer7-Check auf die Backend-Systeme, bei dem geprüft wird, ob der Socks-Proxy unauthentisierte Zugriffe unterstützt (0x050100), was dieser - wenn ja - mit einem 0x0500 beantwortet.

Ob der Zugriff funktioniert, kann vom Load Balancer geprüft werden:

curl --insecure --socks5 102.168.12.21:1080 https://www.google.de/

Konfiguration Socks Proxy

Auf den Proxy VMs (tatsächlich läuft da noch ein Http Proxy, aber das ist eine andere Geschichte) ist der Socks Proxy dante installiert, welcher im Standard-Repository von Debian enthalten ist.

Fun fact: Die Firma, die dante gebaut hat, heisst “Inferno Nettverk A/S”.

danted.conf

Die Konfiguration des Socks Proxy liegt in /etc/danted.conf und unterscheidet sich einzig im Punkt “external”, da muss die lokale IP-Adresse der jeweiligen Maschine rein.

logoutput: stderr

internal: 0.0.0.0 port = 1080
external: 192.168.12.21

socksmethod: none
clientmethod: none

user.privileged: proxy
user.unprivileged: nobody
user.libwrap: nobody

client pass {
        from: 0.0.0.0/0 port 1-65535 to: 0.0.0.0/0
        log: connect disconnect error
}
socks pass {
        from: 0.0.0.0/0 to: 0.0.0.0/0
        protocol: tcp udp
        log: connect disconnect error
}

Dokumentation siehe Dante Website.

Die Konfiguration ist nur ein Beispiel und sehr “offen”. Im produktiven Betrieb würde man mindestens via “pass” Regeln die Zugriffe einschränken sowie eventuell noch irgendeine Art von Authentisierung verwenden.

Wer sich jetzt fragt. wozu man denn einen Socks Proxy braucht: Den braucht man, wenn man andere (Socks-Fähige!) Applikationen als Http(s) durch einen Proxy leiten will. Zum Beispiel, wenn mir irgendein Compliance-Mist vorschreibt, dass mein Traffic immer durch ein “Application Level Gateway” gehen muss. Erläuterungen dazu hat Wikipedia parat.