it-swarm.com.de

Was ist die beste Praxis von docker + ufw unter Ubuntu?

Ich habe gerade Docker ausprobiert. Es ist großartig, aber es scheint nicht gut mit ufw zu funktionieren. Standardmäßig bearbeitet das Andockfenster die iptables ein wenig. Das Ergebnis ist kein Fehler, aber nicht das, was ich erwartet hatte. Für weitere Details lesen Sie Die Gefahren von UFW + Docker

Mein Ziel ist es, ein solches System aufzubauen

    Host (running ufw) -> docker container 1 - nginx (as a reverse proxy)
                       -> docker container 2 - node web 1
                       -> docker container 3 - node web 2
                       -> .......

Ich möchte den eingehenden Datenverkehr über ufw verwalten (z. B. den Zugriff einschränken). Daher möchte ich nicht, dass das Docker meine iptables berührt. Hier ist mein Test

Umgebung:

  • ein neu installiertes Ubuntu 14.04 (Kernel: 3.13.0-53)
  • Docker 1.6.2 
  • ufw forwarding ist aktiviert. ( UFW forwarding aktivieren )
  • --iptables=false wurde dem Docker-Daemon hinzugefügt.

Erster Versuch

docker run --name ghost -v /home/xxxx/ghost_content:/var/lib/ghost -d ghost
docker run --name nginx -p 80:80 -v /home/xxxx/nginx_site_enable:/etc/nginx/conf.d:ro --link ghost:ghost -d nginx

Kein Glück. Der erste Befehl ist in Ordnung, aber der zweite Befehl gibt einen Fehler aus

Error response from daemon: Cannot start container

Zweiter Versuch

Dann fand ich folgendes: nicht in der Lage, Container mit --iptables = false zu verknüpfen # 12701

Nachdem Sie den folgenden Befehl ausgeführt haben, sieht alles in Ordnung aus.

Sudo iptables -N DOCKER

Mir ist jedoch aufgefallen, dass ich keine ausgehenden Verbindungen in Containern herstellen kann. Zum Beispiel:

[email protected]:~$ Sudo docker exec -t -i nginx /bin/bash
[email protected]:/# ping 74.125.21.147
PING 74.125.21.147 (74.125.21.147): 56 data bytes
^C--- 74.125.21.147 ping statistics ---
35 packets transmitted, 0 packets received, 100% packet loss
[email protected]:/# 

Wenn ich --iptables=false aus dem Docker-Dämon entferne, ist die Internetverbindung der Container wieder normal, aber der Ufw funktioniert nicht "richtig" (na ja ... nach meiner Definition).

Also, was ist die beste Praxis von docker + ufw? Kann jemand Hilfe geben?

Vielen Dank.

Bart.

21
Yi-Chien Chang

Ich hatte ein solches Problem wie schon vor Monaten und beschloss kürzlich, das Problem zusammen mit der Lösung in meinem Blog zu beschreiben. Hier ist die Abkürzung.

Die Verwendung von --iptables=false wird Ihnen bei dem beschriebenen Fall nicht viel helfen. Hier reicht es einfach nicht. Standardmäßig kann keiner Ihrer Container eine ausgehende Verbindung herstellen. 

Es gibt einen kleinen Schritt, den Sie auf Ihrem Weg weglassen, um Container hier hinter UFW zu haben. Sie können --iptables=false verwenden oder eine /etc/docker/daemon.json-Datei mit Inhalt wie folgt erstellen

{
  "iptables": false
}

das Ergebnis ist dasselbe, aber für die letztere Option müssen Sie den gesamten Andockdienst mit service docker restart neu starten oder sogar einen Neustart durchführen, wenn Andock die Möglichkeit hatte, iptables-Regeln hinzuzufügen, bevor Sie diese Funktion deaktiviert haben.

Wenn es fertig ist, tun Sie einfach zwei weitere Dinge:

$ sed -i -e 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/g' /etc/default/ufw
$ ufw reload

sie richten also in UFW eine Standard-Forward-Policy für Accept ein und verwenden Folgendes:

$ iptables -t nat -A POSTROUTING ! -o docker0 -s 172.17.0.0/16 -j MASQUERADE

Auf diese Weise wird das unordentliche Verhalten des Dockers in den iptables-Regeln deaktiviert, und gleichzeitig wird das Docker mit dem erforderlichen Routing versehen, so dass die Container ausgehende Verbindungen problemlos ausführen können. Die UFW-Regeln werden von diesem Zeitpunkt an jedoch weiterhin eingeschränkt.

Ich hoffe, das löst das Problem für Sie und alle, die hier auf der Suche nach einer Antwort kommen. 

Ich habe das Problem und die Lösung ausführlicher beschrieben unter https://www.mkubaczyk.com/2017/09/05/force-docker-not-bypass-ufw-rules-ubuntu-16-04/

12
mkubaczyk

Problem

Dieses Problem gibt es schon lange.

Das Deaktivieren von iptables in Docker kann andere Probleme verursachen.

Rollback ändert sich zuerst

Wenn Sie Ihren Server an die aktuelle Lösung angepasst haben, die wir im Internet finden, machen Sie zunächst diese Änderungen rückgängig, einschließlich:

  • Aktivieren Sie die iptables-Funktion von Docker. Entfernen Sie alle Änderungen wie --iptables=false, einschließlich der Konfigurationsdatei /etc/docker/daemon.json.
  • Die standardmäßige FORWARD-Regel von UFW wird wieder in die Standardvariable DROP anstelle von ACCEPT zurückgesetzt.
  • Entfernen Sie die Regeln für das Docker-Netzwerk in der UFW-Konfigurationsdatei /etc/ufw/after.rules.
  • Wenn Sie Docker-Konfigurationsdateien geändert haben, starten Sie Docker zuerst neu. Wir werden die UFW-Konfiguration später ändern und können sie dann neu starten.

Lösen von UFW- und Docker-Problemen

Bei dieser Lösung muss nur eine UFW-Konfigurationsdatei geändert werden. Alle Docker-Konfigurationen und Optionen bleiben der Standard. Die Docker-Funktion iptables muss nicht deaktiviert werden.

Ändern Sie die UFW-Konfigurationsdatei /etc/ufw/after.rules und fügen Sie am Ende der Datei die folgenden Regeln hinzu:

# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16

-A DOCKER-USER -j ufw-user-forward

-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12

-A DOCKER-USER -j RETURN
COMMIT
# END UFW AND DOCKER

Verwenden Sie den Befehl Sudo systemctl restart ufw, um UFW nach dem Ändern der Datei neu zu starten. Nun kann das öffentliche Netzwerk nicht auf veröffentlichte Docker-Ports zugreifen, der Container und das private Netzwerk können sich regelmäßig besuchen, und die Container können auch von innen auf das externe Netzwerk zugreifen.

Wenn Sie öffentlichen Netzwerken den Zugriff auf die vom Docker-Container bereitgestellten Dienste ermöglichen möchten, lautet der Dienstport eines Containers beispielsweise 80. Führen Sie den folgenden Befehl aus, damit die öffentlichen Netzwerke auf diesen Dienst zugreifen können:

ufw route allow proto tcp from any to any port 80

Mit diesem Befehl kann das öffentliche Netzwerk auf alle veröffentlichten Ports zugreifen, deren Containerport 80 ist.

Hinweis: Wenn wir einen Port mit der Option -p 8080:80 veröffentlichen, sollten wir den Containerport 80 verwenden, nicht den Hostport 8080.

Wenn mehrere Container mit einem Service-Port 80 vorhanden sind, möchten wir jedoch nur, dass das externe Netzwerk auf einen bestimmten Container zugreift. Wenn die private Adresse des Containers beispielsweise 172.17.0.2 lautet, verwenden Sie den folgenden Befehl:

ufw route allow proto tcp from any to 172.17.0.2 port 80

Wenn das Netzwerkprotokoll des Dienstes UDP ist, beispielsweise ein DNS-Dienst, können Sie den folgenden Befehl verwenden, um dem externen Netzwerk den Zugriff auf alle veröffentlichten DNS-Dienste zu ermöglichen:

ufw route allow proto udp from any to any port 53

Wenn nur für einen bestimmten Container, z. B. die IP-Adresse 172.17.0.2, gilt:

ufw route allow proto udp from any to 172.17.0.2 port 53

Wie es funktioniert?

Die folgenden Regeln ermöglichen es den privaten Netzwerken, sich gegenseitig zu besuchen. Normalerweise sind private Netzwerke vertrauenswürdiger als öffentliche Netzwerke.

-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16

Mit den folgenden Regeln kann UFW verwalten, ob öffentliche Netzwerke die vom Docker-Container bereitgestellten Dienste besuchen dürfen. Damit können wir alle Firewall-Regeln an einem Ort verwalten.

-A DOCKER-USER -j ufw-user-forward

Die folgenden Regeln blockieren Verbindungsanforderungen, die von allen öffentlichen Netzwerken initiiert werden. Interne Netzwerke können jedoch auf externe Netzwerke zugreifen. Beim TCP Protokoll wird verhindert, dass aktiv eine TCP - Verbindung von öffentlichen Netzwerken hergestellt wird. Beim UDP-Protokoll werden alle Zugriffe auf Ports, die weniger als 32767 beträgt, blockiert. Warum ist dieser Hafen? Da das UDP-Protokoll zustandslos ist, ist es nicht möglich, das Handshake-Signal, das die Verbindungsanfrage initiiert, zu blockieren, wie dies bei TCP der Fall ist. Für GNU/Linux finden wir den lokalen Portbereich in der Datei /proc/sys/net/ipv4/ip_local_port_range. Der Standardbereich ist 32768 60999. Beim Zugriff auf einen UDP-Protokolldienst aus einem laufenden Container wird der lokale Port zufällig aus dem Portbereich ausgewählt, und der Server gibt die Daten an diesen zufälligen Port zurück. Daher können wir davon ausgehen, dass der Überwachungsport des UDP-Protokolls in allen Containern weniger als 32768 ist. Aus diesem Grund möchten wir nicht, dass öffentliche Netzwerke auf die UDP-Ports zugreifen, die weniger als 32768 sind.

-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12

-A DOCKER-USER -j RETURN

Mehr

https://github.com/chaifeng/ufw-docker

Sudo wget -O /usr/local/bin/ufw-docker https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker
chmod +x /usr/local/bin/ufw-docker

Verwendungszweck

ufw-docker help
ufw-docker install
ufw-docker status
ufw-docker allow webapp
ufw-docker allow webapp 80
ufw-docker allow webapp 53/udp
ufw-docker list webapp
ufw-docker delete allow webapp 80/tcp
ufw-docker delete allow webapp

Update: 2018-09-10

Der Grund für die Wahl von ufw-user-forward, nicht ufw-user-input

mit ufw-user-input

Profi:

Einfach zu bedienen und zu verstehen, unterstützt ältere Versionen von Ubuntu.

Um der Öffentlichkeit den Zugriff auf einen veröffentlichten Port zu ermöglichen, dessen Containerport 8080 lautet, verwenden Sie den Befehl:

ufw allow 8080

Con:

Es macht nicht nur Ports von Containern verfügbar, sondern auch Ports des Hosts.

Wenn beispielsweise ein Dienst auf dem Host ausgeführt wird und der Port 8080 ist. Mit dem Befehl ufw allow 8080 kann das öffentliche Netzwerk den Dienst und alle veröffentlichten Ports aufrufen, deren Containerport 8080 lautet. Wir möchten jedoch nur den Dienst, der auf dem Host ausgeführt wird, oder nur den Dienst, der in Containern ausgeführt wird, und nicht die beiden bereitstellen.

Um dieses Problem zu vermeiden, müssen wir möglicherweise für alle Container einen Befehl verwenden, der dem folgenden ähnelt:

ufw allow proto tcp from any to 172.16.0.3 port 8080

mit ufw-user-forward

Profi:

Dienste, die auf Hosts und Containern gleichzeitig ausgeführt werden, können nicht mit demselben Befehl bereitgestellt werden.

Wenn Sie beispielsweise den Port 8080 von Containern veröffentlichen möchten, verwenden Sie den folgenden Befehl:

ufw route allow 8080

Das öffentliche Netzwerk kann auf alle veröffentlichten Ports zugreifen, deren Containerports 8080 sind.

Auf den Port 8080 des Hosts kann jedoch noch nicht vom öffentlichen Netzwerk zugegriffen werden. Wenn wir dies tun möchten, führen Sie den folgenden Befehl aus, um den öffentlichen Zugriff auf den Port des Hosts separat zuzulassen:

ufw allow 8080

Con:

Unterstützt keine älteren Ubuntu-Versionen und der Befehl ist etwas komplizierter. Sie können jedoch mein Skript https://github.com/chaifeng/ufw-docker verwenden.

Fazit

Wenn wir eine ältere Version von Ubuntu verwenden, können wir ufw-user-input chain verwenden. Achten Sie jedoch darauf, dass Sie keine Dienste offenlegen, die nicht offengelegt werden sollten.

Wenn wir eine neuere Version von Ubuntu verwenden, die den ufw route-Unterbefehl unterstützt, sollten Sie ufw-user-forward chain verwenden und den ufw route-Befehl zum Verwalten der Firewall-Regeln für Container verwenden.


Update: 6. Oktober 2018

Das Skript ufw-docker unterstützt jetzt Docker Swarm. Weitere Informationen finden Sie im neuesten Code https://github.com/chaifeng/ufw-docker

Dieses Skript kann nur auf Managerknoten verwendet werden, um Firewall-Regeln im Swarm-Modus zu verwalten.

  • Ändern aller after.rules-Dateien auf allen Knoten, einschließlich Managern und Arbeitern
  • Bereitstellen dieses Skripts auf Managerknoten

Dieses Skript wird im Docker Swarm-Modus ausgeführt und fügt einen globalen Dienst ufw-docker-agent hinzu. Das Bild chaifeng/ufw-docker-agent wird ebenfalls automatisch aus diesem Projekt erstellt.

26
Feng

Was es wert ist, ist hier ein Nachtrag zur Antwort von @ mkubaczyk für den Fall, dass mehr Bridge-Netzwerke in das gesamte Setup eingebunden sind. Diese können von Docker-Compose-Projekten bereitgestellt werden. Im Folgenden erfahren Sie, wie die richtigen Regeln erstellt werden können, da diese Projekte von systemd gesteuert werden.

/etc/systemd/system/[email protected]

[Unit]
Description=Docker-Compose project: %I
After=docker.service
BindsTo=docker.service
AssertPathIsDirectory=/<projects_path>/%I
AssertFileNotEmpty=/<projects_path>/%I/docker-compose.yml

[Service]
Type=simple
Restart=always
WorkingDirectory=/<projects_path>/%I
ExecStartPre=/usr/bin/docker-compose up --no-start --remove-orphans
ExecStartPre=+/usr/local/bin/update-iptables-for-docker-bridges
ExecStart=/usr/bin/docker-compose up
ExecStop=/usr/bin/docker-compose stop --timeout 30
TimeoutStopSec=30
User=<…>
StandardOutput=null

[Install]
WantedBy=multi-user.target

/usr/local/bin/update-iptables-for-docker-bridges

#!/bin/sh

for network in $(docker network ls --filter 'driver=bridge' --quiet); do
  iface=$(docker network inspect --format '{{index .Options "com.docker.network.bridge.name"}}' ${network})
  [ -z $iface ] && iface="br-${network}"
  subnet=$(docker network inspect --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' ${network})
  rule="! --out-interface ${iface} --source ${subnet} --jump MASQUERADE"
  iptables --table nat --check POSTROUTING ${rule} || iptables --table nat --append POSTROUTING ${rule}
done

Offensichtlich wird dies nicht so gut skalieren.

Es ist auch bemerkenswert, dass das gesamte Grundkonzept die Quelle der Verbindungen für die in einem Container ausgeführten Anwendungen verschleiert.

0
funky-future

Sie sind sich nicht ganz sicher, was Sie fragen, aber Sie möchten besser kontrollieren, wer auf Ihre Apps zugreifen kann, die in Docker laufen? Ich habe hier eine ähnliche Frage beantwortet, um den Verkehr über einen Front-End-Proxy zu steuern, anstatt mit IP-Tabellen Externen Zugriff auf Docker-Container blockieren

Hoffe das hilft

Dylan

Bearbeiten

Mit dem oben beschriebenen Ansatz können Sie dann UFW verwenden, um nur eingehende Verbindungen zu Port 80 (dh dem Proxy) zuzulassen. Dadurch wird das Risiko einer Portierung auf ein Minimum reduziert. Außerdem können Sie den Datenverkehr über eine Proxy-Konfiguration und ein DNS steuern 

0
Dylan Scott