UFW Firewall und Docker Container

donnerwolke

Cadet 4th Year
Registriert
Aug. 2013
Beiträge
75
Guten Abend miteinander,

ich habe folgendes Anliegen und hoffe hier Hilfe zu finden.

Seit einigen Jahren habe ich einen VPS bei Hetzner. Die Preise sind aber heftig angezogen.
Nun habe ich bei Netcup im Black Week Sale einen VPS gekauft.

Bei Hetzner hatte ich im Webinterface immer die Möglichkeit meine Firewall zu konfigurieren. Ich habe nur den Port 80/443, 22 (für meine IP) geöffnet, da ich einen NGINX ProxyReverse im Einsatz hatte.
Bei Netcup gestaltet sich das ganze ein wenig schwieriger, da der Hoster keine Möglichkeit bereitstellt die Freigaben der Ports zu konfigurieren.
Hier muss ich auf die IPtables bzw UFW zurück greifen. An sich eigentlich nicht schwer.
Ich mach hier beispielsweise einfach nur
Code:
sudo ufw allow from 192.168.0.123 to any port 22
Alles, was auf den VPS an sich geht ist auch absolut kein Thema.
Jedoch rennt der NGIX zb. bei mir als Docker und ist dann auch auf den Port 80/443 erreichbar.
Wenn ich jetzt aber den Port 80/443 öffne, ist dieser NICHT erreichbar.

Ich habe mich nun eingelesen und hatte auch schon Themen wie ufw-docker, die einem das Leben angeblich einfacher machen sollen. Jedoch hat das bisher nicht funktioniert.
Aktuell ist die FW deaktiviert und alle Ports von aussen erreichbar. Auch die Ports, die ich eigentlich nicht freigeben will, da der Traffic durch den NGINX gehen soll. (zb. Portainer mit dem Port 9443)

Hat sich jemand schon mal mit dem Thema auseinander gesetzt und kann mich hier in der Einrichtung unterstützen?
Wenn irgendwelche Konfigs benötigt werden, einfach melden.

Eckdaten:
  • Die Netzwerkkarte ist bei mir auf dem Server die "eth0"
  • BS: Ubuntu 22.04
  • Ich habe keine GUI installiert (ich habs bisher einfach nicht gebraucht und frisst meiner Meinung nach nur unnötig Speicher)
  • Ich habe auf dem Host zwei Dockernetzwerke erstellt.
Bild anbei:
1669564716530.png


Ich hoffe, ich finde hier des Problems Lösung.

Vielen Dank und einen schönen ersten Advent euch allen! :)
 
Zuletzt bearbeitet:
Danke Andy, dass du mir schreibst.

Ich dachte eigentlich, dass das die Ports sind, die ich in der Docker-Compose.yaml mitgegeben habe.

Beispiel: Mein Apache Webserver:
Hier verwende ich Hostseitig den Port 8088.
1669567934053.png

Ergänzung ()

Aaaah nee, warte mal. Der bindet den Port 8088 nun an die IP 172.20.0.22, richtig? Und ich muss eine sogenannte Portweiterleitung machen. Aber das habe ich auch schon versucht.
Der Befehl dafür lautete beispielhaft:
Code:
ufw route allow proto tcp from any to 172.20.0.3/32 port 443
Die IP 172.20.0.3 hat mein NGINX ProxyReverse. Dem wollte ich den Port 443 weitergeben.
 
Zuletzt bearbeitet:
Das erlaubt aber nur das Forwarding und macht noch kein NAT, oder?

Das Problem an Docker ist, dass es sofort furchtbar in den Firewall Regeln herumfuhrwerkt. Alle anderen Tools wie ufw bekommen anschließend Probleme, weil man nicht mehr beliebig Regeln hinzufügen und entfernen kann. Das ist der Grund, warum Docker bei mir nur in einem lxd Container läuft. Da kann es mit der Firewall machen, was es will, und ich kümmere mich auf dem Host darum, dass nur das bei Docker ankommt, was auch ankommen soll.
 
  • Gefällt mir
Reaktionen: Unnu
Zuhause mache ich das genau so.
Da hab ich dann die Macht darüber.
Allerdings bin ich hier halt auf einem VPS. Hier muss ich dann eben was anderes machen.
Ich versuche mich jetzt mal in Nating in Verbindung mit Docker einzulesen.

Falls jemand noch einen Lösungsansatz für mich hat, bitte, immer her damit.
 
donnerwolke schrieb:
einen Lösungsansatz für mich hat, bitte, immer her damit.
Ich weiß das klingt jetzt vielleicht absurd und manche möchten mich vermutlich für diesen Vorschlag steinigen aber wie wäre es denn mal mit dem lesen der Doku? Gerüchten zufolge stehen da ja manchmal hilfreiche Dinge drin und wenn man das gelesen und verstanden hat ist es anschließend deutlich einfacher, das Problem zu lösen.
https://docs.docker.com/network/iptables/

BTW: Die Hetzner "Firewall" war auch nur eine simple ACL am Switchport. Bei Standardports & -anwendungen wird zwar viel Müll gefiltert aber mehr auch nicht, da diese ACLs z.B. komplett stateless arbeiten. Anyway, netcup bietet das nicht und netcup hat noch ein paar andere Baustellen...

Der Kernproblem ist hier aber ufw. Docker und ufw versuchen beide eine einfache Möglichkeit anzubieten zur Konfiguration von iptables/nftables aber beide wissen nichts voneinander.

donnerwolke schrieb:
Auch die Ports, die ich eigentlich nicht freigeben will, da der Traffic durch den NGINX gehen soll. (zb. Portainer mit dem Port 9443)
Wenn Portainer nicht auf <public ip>:9443 lauschen soll, warum publisht du den Port dann? Binde ihn an localhost:9443 und im nginx kannst dann z.B. als reverse proxy von portainer.your-domain.de auf localhost:9443 umsetzen.

Gibt es eigentlich einen speziellen Grund warum du deinen Containern feste IPs gibst? Ist eigentlich nicht notwendig bzw. mir fällt bisher kein use-case dafür ein und ich bin neugierig und vielleicht lerne ich ja noch etwas dazu.
 
  • Gefällt mir
Reaktionen: Unnu, matthias3000 und andy_m4
Guten Abend,

zuerst vorweg: Ich habe mich natürlich mit der Doku beschäftigt. Ich saß gestern Abend noch bis um halb 12 vorm Rechner und hab gefühlt das halbe Internet durchsucht (~40 Goole Suchen :D)
Für jemand, der sich davor noch nie mit der Thematik auseinander gesetzt hat, ist das halt schwierig. Muss ich ganz klar sagen.

Ich kann aber sagen, dass ich mich nun weitgehend mit dem Thema UFW und IPTABLES auseinander gesetzt habe und heute Nachmittag dann endlich mein Ziel erreicht habe.
Ich habe nun Ports an die Docker weitergeleitet und von aussen geöffnet.
Ein paar Konfigurationen waren auch so notwendig. Zwischenzeitig hatten alle meine Docker keinen Internetzugriff, aber mit IPTABLES habe ich das hinbekommen. Alle Konfigurationen sind nun stichpunktartig dokumentiert und automatisiert (weil nach einem Neustart fehlten mir anfänglich alle IPTABLES Einträge.

Die Hetzner Firewall hat scheinbar trotzdem irgendwas anders gemacht als ich es jetzt machen musste. Bei Hetzner habe ich einfach nur den Port angegeben, den ich von Docker auf den Host gemappt hatte. Hier musste ich die Portweiterleitungen direkt auf den Docker machen (den Port, den Docker intern verwendet)

Womit wir nun bei den IP Adressen wären:
Ich habe meinen Dockern feste IP Adressen vergeben, weil ich so weiss welcher Docker welche IP hat und die sich dann auch nicht ändert.
Im NGINX habe ich nämlich auch die IP Adressen angeben "müssen".

1669655923699.png


Ich weiss nicht, bestimmt wäre es sicher auch irgendwie anders gegangen aber so habe ich es auch für die Zukunft eingestellt und muss mir keine Gedanken mehr darüber machen.

Der Portainer war nur ein Beispiel. Den wollte ich nicht durch den NGINX schicken, weil ich sonst noch mal eine Subdomain hätte anlegen müssen. Da ich den Zugriff eh auf eine IP von extern beschränkt habe, verbinde ich mich dann einfach per hostname : port und gut ist.

Nun noch ein wenig Doku:
Ich habe das verwalten der IPTABLES durch Docker deaktiviert.
In der Datei
Code:
/etc/docker/daemon.json
habe ich folgendes reingeschrieben:
Code:
{
  "iptables": false,
  "dns": ["1.1.1.1", "1.0.0.1"]
}
Das erhöht den Aufwand für mich etwas, aber es ist OK. Ich möchte am Ende einfach ein paar Anwendungen laufen haben und fertig. Zum basteln habe ich meine Landschaft Zuhause.

So habe ich eine Portfreigabe gemacht bzw einen Port an Docker weitergeleitet:
Code:
ufw route allow proto tcp from any port 21115:21119 to 172.18.0.2 port 21115:21119 comment "Rustdesk TCP Host->Docker"
ufw allow from any to any port 21115:21119 proto tcp comment "Rustdesk TCP Ext"
Erstes gibt einen Portbereich vom Host an Docker weiter. (Forwarding)
Wichtig: Es muss immer der Port Dockerseitig sein.
Der zweite Befehl öffnet den Port dann für das Öffentliche Netz. (das Internet)

Zum vergleich noch eben den Docker, den ich da in meiner
Code:
docker-compose.yaml
konfiguriert habe. Für die, die es nicht wissen: Die Ports auf der rechten Seite sind die Ports, die Containerseitig verwendet werden.
Code:
  hbbs:
    container_name: hbbs
    ports:
      - 21115:21115
      - 21116:21116
      - 21116:21116/udp
      - 21118:21118
    image: rustdesk/rustdesk-server:latest
    environment:
      - "RELAY=$(DOMAIN):21117"
      - "ENCRYPTED_ONLY=1"
    command: "hbbs -r $(DOMAIN):21117 -k $(SECRET)"
    volumes:
      - ./hbbs:/root
    networks:
      - rustdesk-net
    depends_on:
      - hbbr
    restart: unless-stopped

In der UFW FW sieht das ganze dann so aus:
1669657859000.png


Nun habe ich für meine beiden Dockernetze die IPTABLES gepflegt, damit die auch ins Internet kommen:
Code:
iptables -t nat -A POSTROUTING ! -o docker0 -s 172.20.0.0/16 -j MASQUERADE
iptables -t nat -A POSTROUTING ! -o docker0 -s 172.18.0.0/16 -j MASQUERADE

Das ganze habe ich dann mit
Code:
iptables-save > /etc/iptables/rules.v4
in die Datei gespeichert. Diese wird nach jedem reboot neu geladen.

Damit das ganze natürlich wirken kann müssen noch die Anwendungen neu gestartet werden:
Code:
systemctl restart docker.service ufw.service

Damit IPTABLES automatisch beim Reboot geladen werden können, sollte man sich
Code:
iptables-persistent
angucken.

Ich glaube, dass es auch wichtig ist, dass wenn ich neue UFW Freigaben gemacht habe, dass ich diese dann in die
Code:
/etc/iptables/rules.v4
reinschreibe. Bin mir aber wirklich nicht sicher. Vll kann dies noch als abschließende Frage gesehen werden.

Wenn auch nicht sehr professionell, hoffe ich trotzdem, dass es dem einen oder anderen evtl ein wenig hilft.
Um noch mal auf den Kommentar mit der Doku zurück zu kommen:
Ich wollte einfach nur einen Schubs in die richtige Richtung. Ein wenig Hilfe. Dafür sind Foren doch da.

Der Thread ist damit für mich erledigt.
Ich wünsche euch allen einen schönen Abend!
 
Zuletzt bearbeitet:
Von IPv6 ist dann schlicht und einfach kein Zugriff drauf möglich und das ist aktuell noch OK für mich. Standardmäßig wird ja alles gedroppt.
Wenn ich irgendwann mehr über IPv6 weiss, werde ich das Thema noch mal angreifen. Aktuell aber habe ich keinen Bedarf. Am liebsten würd ich die IPv6 eh wegschmeissen. Mir reicht die IPv4 aktuell voll aus.
 
donnerwolke schrieb:
Standardmäßig wird ja alles gedroppt.
Solange das wirklich sichergestellt ist, ist es ok. Nur leider bin ich nicht deiner Meinung, dass es grundsätzlich so ist. Dafür muss man schon selber sorgen.
 
  • Gefällt mir
Reaktionen: donnerwolke
donnerwolke schrieb:
Ich saß gestern Abend noch bis um halb 12 vorm Rechner und hab gefühlt das halbe Internet durchsucht (~40 Goole Suchen :D)
Für jemand, der sich davor noch nie mit der Thematik auseinander gesetzt hat, ist das halt schwierig. Muss ich ganz klar sagen.
Hoffentlich nur die direkten Dokus verwendet und nicht irgendwelche Tutorials von Dritten die oft genug veraltet oder unsicher sind.
Also Tutorials sind nix verwerfliches wenn man sich eine Idee abholen will wie man etwas umsetzen kann aber man sollte diese nie als Rezept verstehen, das man 1:1 blind nachkochen sollte sondern immer als Idee ansehen zum verstehen wie und warum man etwas macht und um es an die eigenen Gegebenheiten anzupassen.
Ja ich stimme dir voll und ganz zu: Docker hat eine enorm steile Lernkurve und auch (virtuelle) Netzwerke unter Linux können sehr schnell sehr anspruchsvoll werden.

donnerwolke schrieb:
Ich habe meinen Dockern feste IP Adressen vergeben, weil ich so weiss welcher Docker welche IP hat und die sich dann auch nicht ändert.
Im NGINX habe ich nämlich auch die IP Adressen angeben "müssen".
Warum wäre es schlimm, wenn sich die IPs ändern würden? Passiert ja sowieso ggf. nur, wenn ein Container neu gestartet oder erzeugt wird. Docker kümmert sich von Haus aus um DNS zwischen Containern (in deinem Setup zwar nicht mehr, dazu siehe unten aber im Regelfall eben schon). Sofern der nginx auch als Container läuft dann bräuchte man nicht mit IPs hantieren sondern könnte mit menschenverständlichen Namen arbeiten.

donnerwolke schrieb:
Ich weiss nicht, bestimmt wäre es sicher auch irgendwie anders gegangen aber so habe ich es auch für die Zukunft eingestellt und muss mir keine Gedanken mehr darüber machen.
Jain. Du bist jetzt sehr aufwendig und sehr manuell einen Weg zu Fuß gegangen mit ähnlichem oder identischem Ausgang wenn du die nativen Automatismen von Docker verwendet hättest.
Mehrwert sehe ich hier nur im Verständnis des Setups aber Mehraufwand beim Betrieb.

Ich vermute einfach, du bist vor einiger Zeit falsch abgebogen und gehst diesen steinigen Weg weiter anstatt umzudrehen und zu gucken ob es nicht deutlich einfachere Wege gibt, die zu einem gleichen Ziel führen.

donnerwolke schrieb:
habe ich folgendes reingeschrieben:
Code:
{
"iptables": false,
"dns": ["1.1.1.1", "1.0.0.1"]
}
Hier ist so ein Beispiel für falsch abgebogen: Durch die explizite Angabe von DNS-Servern verwenden deine Container Cloudflare für alle DNS Abfragen. Dadurch bist du gezwungen für die Kommunikation zwischen Containern bzw. Host und Container auf IPs zurück zu greifen.
Ohne diese Angabe würden Container zuerst den integrierten DNS vom Docker Daemon fragen und erst wenn dieser keine Antwort (lokal) geben kann, geht die Anfrage an die in der /etc/resolv.conf eingetragenen DNS Server des Hosts, also deines VPS. Bisschen Lektüre dazu gibt es unter: https://docs.docker.com/config/containers/container-networking/#dns-services

Ein vergleichbares Szenario wäre: 0815 Haushalt mit 1+ Endgeräten, nem pseudo-smarten TV, einem NAS und was man noch so hat und einer handelsüblichen und weit verbreiteten Fritzbox und dann stellt man an allen Geräten ein, dass anstatt der Fritzbox doch direkt 1.1.1.1 oder 8.8.8.8 bei DNS Anfragen benutzt werden soll und sich dann anschließend wundert warum man am PC oder TV die Urlaubsbilder vom NAS nicht mehr angucken kann und das NAS nicht mehr unter \\name-des-nas\Urlaubsfotos erreichen kann sondern jetzt immer die IP eingeben muss. (Haushalt = VPS, Endgeräte/TV/NAS/etc = Container)
Durch diese Fehlkonfiguration hat man erst einmal keinen Mehrwert geschaffen aber dafür ein paar Probleme erzeugt.
 
Zurück
Oben