let’s dev GmbH & Co. KG - Die Marke für wegweisende Individualsoftware

Blog

Top!

let’s dev | Scroll to top
let’s dev | Scroll to next content item

Hello

let’s dev | Scroll to previous content item
let’s dev Blog |
by
01. Januar 1970

Wer hat noch nicht davon gehört? Docker und seine beliebten Container sollen die Bereitstellung von Anwendungen beschleunigen und vereinfachen. Wer hier allerdings nicht genau aufpasst kann unbewusst Hackern eine Angriffsmöglichkeit eröffnen. Wie das passieren kann und wie man sich dagegen schützt, erklären wir anhand des nachfolgenden Beispiels.

Wir benötigen eine Anwendung, die in einem Docker Container gestartet wurde. Die Anwendung läuft innerhalb des Containers auf dem Port 8080 und soll auf unserem Docker Host unter 9090 erreichbar sein. Warum der Port 9090 für den Docker Host? Wir könnten beispielsweise noch weitere Container am Laufen haben, die sich nicht alle den Port 8080 des Hosts teilen können und getrennt erreichbar sein müssen.

Das ist so ziemlich das Erste mit dem man in Kontakt kommt, wenn man das erste Mal mit Docker arbeitet:

docker run -p 9090:8080

Und die Anwendung läuft…

docker ps
eb77b9f37140 … 0.0.0.0:9090->8080/tcp

Huch? Jetzt steht hier etwas von 0.0.0.0:9090, das hatten wir so aber nicht angegeben. Macht doch nichts - ist schließlich Standard, oder?

Naja, das kommt darauf an wo wir uns befinden, denn 0.0.0.0 bedeutet der Port ist offen für alle Adressen. Um besser zu verstehen was das Problem ist, müssen wir die Netzwerkschicht betrachten.

Network Interfaces

Veranschaulichen wir uns den Aufbau doch zunächst einmal auf einem nicht Docker-fähigen Host System. Das heißt Docker wird in einer Virtual Machine (VM) auf dem Host betrieben.

Zunächst haben wir unseren Container - das alles klingt für sich alleine betrachtet recht simpel.

Docker Container Schritt 1

Das Netzwerk Interface eth0 sowie lo (loopback) sind offen für den Port 8080. Dabei bietet eth0 diese Schnittstelle nach außen an.

Nach außen heißt zunächst einmal für die docker-engine.

Die docker-engine managed für uns ihre Container und stellt das Interface für die Kommunikation bereit - docker0. Unser Container ist über ein weiteres virtuelles Interface vethXY über docker0 verbunden.

Somit wandern Anfragen über docker0, über vethXY schließlich an das eth0 Interface unseres Containers.

Docker Container Schritt 2

Damit unsere Anfrage an docker0 geleitet wird gibt es den docker-proxy. Dieser lauscht auf die gewünschten Ports und leitet entsprechend weiter.

Docker Container Schritt 3
ps -ef | grep docker-proxy
… /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 9090 -container-ip 172.17.0.2 -container-port 8080

Hier sehen wir nun auch wo unsere magische 0.0.0.0 herkommt.
Um nun das Problem nochmal zu verdeutlichen, komplettieren wir unser Schaubild um unsere Host-Maschine.

Docker Container Schritt 4

Die 0.0.0.0 hängt am Interface der VM und ist nicht weiter nach außen freigegeben. Somit können wir von unserem Browser zwar auf die Anwendung unter dem Port zugreifen, ein Dritter von außen jedoch nicht. Der Port 9090 ist nicht auf unserer Host-Maschine direkt sondern in der VM freigegeben.

Jetzt verschieben wir unsere Test Anwendung auf einen Produktions-Server aus der Cloud, ein auf Linux basierender, Docker-fähiger Host.

Docker Container Schritt 5

Die VM ist weggefallen, da unser Host Docker direkt betreiben kann. Nun lauschen die docker-proxies direkt auf unserem Host Interface und offenbaren Ports die wir vielleicht garnicht nach außen sichtbar haben wollen.

Überprüfen können wir das mittels nmap

nmap my.server -p 9090
Starting Nmap 7.80 ( https://nmap.org ) at 2020-05-28 14:40 CEST
Nmap scan report for my.server
Host is up (0.056s latency).
PORT     STATE …
9090/tcp open  …

Unser Port 9090 ist also für die ganze Welt offenbart, bewusst oder unbewusst.
Das wollen wir gegebenenfalls unterbinden und so eine Sicherheitslücke schließen. Hier kommen wir wieder auf das erwähnte lo (loopback) Interface zurück.

Port sichern

Das ganze ist auch nicht sonderlich schwer. Wir können unserem Container einfach mitteilen dass er statt auf 0.0.0.0 nur auf das lo Interface lauschen soll.

docker run -p 127.0.0.1:9090:8080

Mit dieser Einschränkung ist unser Container innerhalb unseres Systems weiterhin unter dem Port 9090 erreichbar, nach außen hin jedoch gesperrt.

Überprüfen wir das nochmal.

ps -ef | grep docker-proxy
… /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 9090 -container-ip 172.17.0.2 -container-port 8080
nmap my.server -p 9090
Starting Nmap 7.80 ( https://nmap.org ) at 2020-05-28 15:04 CEST
Nmap scan report for my.server
Host is up (0.061s latency).
PORT     STATE …
9090/tcp closed  …

Das sieht doch schon viel sicherer aus.

Takeaway

Der Einstieg in die Docker Welt ist einfach und erlaubt viele Spielereien um schnell mal Dienste zur Verfügung zu stellen. Damit das auch so einfach ist, geschieht viel unter der Haube, was jedoch zu bösen Überraschungen führen kann.

Verlässt man eine gesicherte Test-Umgebung gilt es sowieso sich mit dem Thema Sicherheit außeinanderzusetzen. Das Öffnen der Ports nach außen ist sicherlich nicht immer gewollt und somit ein böser Fallstrick wenn man Docker ohne zu Überprüfen genauso einsetzt wie es die meißten Tutorials nun mal so machen.


Fatal error: Uncaught Error: Call to a member function getAll() on null in /homepages/12/d366218748/htdocs/websites/letsdev3.0/modules/module_blog_article_slider_all.php:2 Stack trace: #0 /homepages/12/d366218748/htdocs/websites/letsdev3.0/blog/moegliche-sicherheitsluecke-bei-port-freigaben-im-docker-container.php(27): include() #1 {main} thrown in /homepages/12/d366218748/htdocs/websites/letsdev3.0/modules/module_blog_article_slider_all.php on line 2