Docker Networking mit und ohne Isolation
Docker Networking kann isolieren, muss aber nicht. Beispiele für verschiedene Anwendungsfälle.
In Docker können verschiedene Netzwerk-Konfigurationen vorgenommen werden. Je nach Anwendungsfall bieten diese Vor- und Nachteile, die hier genauer beleuchtet werden sollen. Folgende Szenarien werden hier behandelt (Teilmenge der Möglichkeiten!):
- Host-Network (unisoliert)
- default Bridge "docker0" (isoliert)
- User-defined bridge networks "brX" (isoliert)
Die hier getätigten Aussagen gelten für Standard-Einstellungen und können bei Änderungen an Docker / Firewall (oder Betriebssystem) durchaus abweichen!
Host Network (unisoliert)
Bei Verwendung des Host Networks wird der Container nicht isoliert und erhält den Zugriff zur Netzwerk-Umgebung des Host-Systems. Anwendungen innerhalb des Containers können somit direkt an den verfügbaren Ports des Hosts lauschen.
Ein Docker-Proxy entfällt hierbei, kann und braucht nicht konfiguriert werden!
Run-Command
docker run --network host --name mariadb --restart unless-stopped -v /srv/mariadb/:/var/lib/mysql mariadb:latest
Docker-Compose
File: docker-copose.yml
version: "3"
services:
mariadb:
image: mariadb:latest
container_name: mariadb
network_mode: host
volumes:
- /srv/docker/containers/mariadb/data:/var/lib/mysql
restart: unless-stopped
Docker default Bridge docker0 (isoliert)
Erstellt man einen Docker-Container ohne genauere Netzwerk-Angaben, wird dieser in der Regel in die default Bridge (docker0) eingehangen und ist dort als isolierter Container in der Lage Netzwerkverbindungen ausgehend aufzubauen, selbst aber nicht von außen erreichbar. Möchte man Netzwerk-Zugriffe auf die Anwendung im Container ermöglichen, kann der Docker-Proxy den Traffic in den Container leiten. Hier Beispiele für die diverse Konfigurationen des Docker Proxies. Das Port-Mapping (-p) kann mehrfach konfiguriert werden!
Bindung Docker-Proxy an :3306
Der Proxy lauscht auf allen Interfaces und kann auch von externen Clients kontaktiert werden!
docker run -p 3306:3306 --name mariadb --restart unless-stopped -v /srv/mariadb/:/var/lib/mysql mariadb:latest
Bindung Docker-Proxy an 127.0.0.1:3306
Der Proxy lauscht nur lokal und lokale System Anwendungen können diesen erreichen.
docker run -p 127.0.0.1:3306:3306 --name mariadb --restart unless-stopped -v /srv/mariadb/:/var/lib/mysql mariadb:latest
Bindung Docker-Proxy an 192.168.111.5:3306 und 192.168.111.5:13306
Clients mit Zugriff auf die IP-Adresse des Hosts (192.168.111.5) und lokale System-Anwendungen können den Proxy erreichen.
docker run -p 192.168.111.5:13306:3306 -p 192.168.111.5:3306:3306 --name mariadb --restart unless-stopped -v /srv/mariadb/:/var/lib/mysql mariadb:latest
Den Zugang zum Internet erhält der Container mittels iptables Firewall Regeln die das Forwarding von docker0 ausgehend erlauben und mittels SNAT (masquerading) die Absender-IP in die des Hosts umschreibt (siehe Unten).
Forwarding (Routing)
Fowarding beschreibt Traffic der nicht für lokale Interfaces (IP-Adressen) bestimmt ist und geroutet werden muss. Docker setzt diese Regeln und das Standardverhalten auf DROP! Dies kann in manchen Fällen funktionierendes Routing unterbrechen!
> iptables -nvL
Chain FORWARD (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 DOCKER-USER all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 DOCKER-ISOLATION-STAGE-1 all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
0 0 DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0 0.0.0.0/0
Chain DOCKER (1 references)
pkts bytes target prot opt in out source destination
Chain DOCKER-ISOLATION-STAGE-1 (1 references)
pkts bytes target prot opt in out source destination
0 0 DOCKER-ISOLATION-STAGE-2 all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
Chain DOCKER-ISOLATION-STAGE-2 (1 references)
pkts bytes target prot opt in out source destination
0 0 DROP all -- * docker0 0.0.0.0/0 0.0.0.0/0
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
Chain DOCKER-USER (1 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
Masquerading (Source NAT)
Hiermit werden alle Pakete dessen Absender-Adresse aus dem Netz 172.17.0.0/16 kommen und nicht an Container (docker0) geschickt werden, maskiert. Die hierbei verwendete IP-Adresse entspricht der ersten auf dem ausgehenden Interface. Dies kann sowohl eine öffentliche zum Internet, als auch eine private im internen Netz sein! Letztlich wird der Empfänger nicht die IP-Adresse des Containers, sondern des Hosts sehen.
> iptables -t nat -nvL
Chain POSTROUTING (policy ACCEPT 9 packets, 977 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
User-defined bridge networks (isoliert)
Möchte man Container auf Ethernet-Ebene (Layer2) voneinander isolieren, kann es ratsam sein, ein neues Docker Netzwerk anzulegen. Dies resultiert in eine neue Bridge und neuen IP-Adressbereich, aus dem Container eine IP-Adresse zugewiesen bekommen.
Im Gegensatz zur default Bridge kann jeder Container eine vorab festgelegte IP-Adresse erhalten. Dies bietet die möglichkeit die Firewall genauer zu definieren und Kommunikation zwischen Containern individuell einzustellen.
Anlegen des Docker Networks
Der Host erhält die IP-Adresse 192.168.111.1 und bindet diese auf der neuen Bridge br_docker. Der Name der Bridge und des Docker Networks können unterschiedlich lauten!
docker network create --gateway 192.168.111.1 --subnet 192.168.111.0/24 --opt com.docker.network.bridge.name=br_docker my_cust_net
Docker Container mit spezifischer IP-Adresse
Die gewählte IP-Adresse muss sich hierbei im Subnetz des Docker Networks befinden!
Run-Command
docker run --name test --network my_cust_net --ip 192.168.111.99 -ti --rm --entrypoint /bin/bash ubuntu:latest
Docker-Compose
Der Netzwerkname (default) ist für die Referenzierung innerhalb eines Services und kann beliebig gewählt werden. Aufgrund des external Keys ist docker-compose klar, dass es kein eigenes Netzwerk anlegen und das vorhandene verwenden soll. Deshalb muss dieses vorab angelegt werden!
File: docker-copose.yml
version: "3"
services:
mariadb:
image: mariadb:latest
container_name: mariadb
volumes:
- /srv/docker/containers/mariadb/data:/var/lib/mysql
restart: unless-stopped
networks:
default:
ipv4_address: 192.168.111.99
mariadb2:
image: mariadb:latest
container_name: mariadb2
volumes:
- /srv/docker/containers/mariadb/data:/var/lib/mysql
restart: unless-stopped
networks:
default:
ipv4_address: 192.168.111.250
networks:
default:
external:
name: my_cust_net
Quellen / Dokumentation