Einleitung:
Beim Hosting von Diensten im Intranet kann die richtige Konfiguration von Network Address Translation (NAT) entscheidend sein. Dieser Artikel erklärt das Konzept des NAT Loopback (hairpin NAT oder auch NAT reflection) und wie iptables verwendet wird, um sicherzustellen, dass der Verkehr von internen Hosts reibungslos zum internen Server zurückkehren kann.
Das Problem:
In obigem Szenario, ist ein Proxmox server direkt am Internet angeschlossen. innerhalb des Proxmox Servers gibt es ein eigene Netzwerk über die Bridge Schnittstelle vmbr0. Diese stellt das Netz 10.0.0.0/24 zur verfügung.
Die LXC-Container bekommen eine IP-Adresse innerhalb dieses Netzes. Hier 10.0.0.10 als Beispiel.
In unserem Fall, haben wir in einem der Docker Container einen Webserver am laufen. Dieser ist über die öffentliche Domain mydomain.com erreichbar. Damit dies funktioniert, muss jedoch eingehender Traffic auf Port 80 sowie 443 an den LXC weitergeleitet werden. Dies geschieht über eine iptable rule.
iptables -t nat -A PREROUTING -i ens18 -s * -d 173.200.190.20/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.0.0.10:80
iptables -t nat -A PREROUTING -i ens18 -s * -d 173.200.190.20/32 -p tcp -m tcp --dport 443 -j DNAT --to-destination 10.0.0.10:443
Nun ist der Traffic weitergeleitet. Was ist jedoch, wenn nun versucht wird, von innerhalb des LXC oder einem der Docker Container aus eine Verbindung zu mydomain.com aufzubauen?
- domain auflösen
- domain aufgelöst zu 173.200.190.20
- Verbindungsaufbau zu 173.200.190.20
- Verbindung abgelehnt.
Die Verbindung wird abgelehnt, da das gateway auf 173.200.190.0/24 ein Packet mit source 10.0.0.10 erhält.Und die Antwort somit an 10.0.0.10 gehen würde. Dieses Ziel ist jedoch im 173.200.190.0/24 Netz unbekannt.
Damit dies funktioniert, muss ein sogenanntes hairpin NAT oder NAT reflection implementiert werden. Dies sorgt dafür, dass interner Traffic abgefangen wird, bevor er das Netzwerk verlässt.
Dazu sind zwei Regeln notwendig:
iptables -t nat -A PREROUTING -i vmbr0 -s 10.0.0.10/32 -d 173.200.190.20/32 -p tcp -m multiport --dport 80,443 -j DNAT --to-destination 10.0.0.10
Die erste Regel sorgt dafür, dass Traffic welcher vom Interface vmbr0 kommt mit port 80 oder 443 und als Quelle unseren LXC hat (10.0.0.10) und als Ziel unsere Public IP (173.200.190.20) an das Ziel 10.0.0.10:80 umgeleitet wird.
Nun ergibt sich jedoch ein Problem. Denn die Anwendung im LXC welche mydomain.com aufruft, sendet ein Packet an das Ziel: 173.200.190.20 und erwartet eine Antwort von der Quelle 173.200.190.20 mit Ziel 10.0.0.10.
Nach der Umleitung des Traffics sieht das Packet jedoch so aus:
Quelle: 10.0.0.10
Ziel: 10.0.0.10
Damit wird das Packet verworfen und die Anfrage läuft in ein Timeout. Um dies zu lösen, wird eine zweite iptables rule benötigt. Jene für Source-NAT (snat)
iptables -t nat -A POSTROUTING -o vmbr0 -s 10.0.0.10/32 -d 10.0.0.10/32 -p tcp -m multiport --dport 80,443 -j SNAT --to-source 173.200.190.20
Mit dieser Regel wird nun jeglichem Traffic (Paketen) mit Quelle aus dem 10.0.0.0/24 Netz auf port 80 welcher über das interface vmbr0 ausgehen soll und als Ziel ebenfalls 10.0.0.10/32 hat eine neue quelle gegeben. Hier: 173.200.190.20.
Damit sieht es für den Client z.B. wget wieder so aus, als wäre das Paket tatsächlich von der erwarteten Quelle (173.200.190.20) gekommen und die Verbindung kann korrekt aufgebaut werden.
Zu beachten ist, dass die Regeln sowohl für Port 80 wie auch Port 443 erstellt werden sollten oder wie in diesem Beispiel multiport verwendet wird.
vmrb0 und andere Bridges
Wird auf dem System eine Bridge eingesetzt wie in diesem Beispiel, so kann es erforderlich sein, dass hier hairpin innerhalb der Bridge aktiviert wird. Dazu gibt es folgenden befehl:
brctl hairpin vmbr0 on
wobei <interace> durch das entsprechende Interface bzw. dessen Name ersetzt werden muss. Dies erlaub es der Bridge, traffic der gleichen Quelle wieder an diese zurückzuleiten. Andernfalls würde der Traffic von der Bridge nicht bearbeitet werden.
Persistent
Damit die Einstellungen auch nach einem Neustart erhalten bleiben, sollten diese in den Einstellungen gespeichert werden. Typischerweise ist dies /etc/network/interfaces
auto vmbr0
iface vmbr0 inet static
address 10.0.0.1/24
bridge-ports none
bridge-stp off
bridge-fd 0
post-up echo 1 > /proc/sys/net/ipv4/ip_forward
post-up iptables -t nat -A POSTROUTING -s '10.0.0.0/24' -o ens18 -j MASQUERADE
post-up iptables -t nat -A PREROUTING -i ens18 -p tcp ! -s 10.0.2.0/24 --dport 80 -j DNAT --to 10.0.0.10:80
post-up iptables -t nat -A PREROUTING -i ens18 -p tcp ! -s 10.0.2.0/24 --dport 443 -j DNAT --to 10.0.0.10:443
post-up iptables -t nat -A PREROUTING -i vmbr0 -s 10.0.0.0/24 -d 173.200.190.20/32 -p tcp -m multiport --dport 80,443 -j DNAT --to-destination 10.0.0.10
post-up iptables -t nat -A POSTROUTING -o vmbr0 -s 10.0.0.0/24 -d 10.0.0.10/32 -p tcp -m multiport --dport 80,443 -j SNAT --to-source 173.200.190.20
post-up brctl hairpin vmbr0 veth100i0 on
post-down iptables -t nat -D POSTROUTING -s '10.0.0.0/24' -o ens18 -j MASQUERADE