Zum Inhalt springen

Projekt Phoenyx – Teil 4: Ordnung im Chaos (Die Architektur)

Kategorie: Core-Stack / Architektur | Lesezeit: ca. 20 Minuten

Einführung: Das Monolith-Problem

Wer schon einmal mit Docker gearbeitet hat, kennt das Problem: Man fängt mit einer kleinen docker-compose.yml an. Ein Webserver, vielleicht eine Datenbank. Dann kommt Traefik dazu. Dann Monitoring. Dann Crowdsec. Und plötzlich hat man eine Datei mit 500 Zeilen Code, bei der niemand mehr durchblickt.

Das führt zu Problemen:

  • Unübersichtlichkeit: Wo endet die Konfiguration von Traefik und wo beginnt die Datenbank?
  • Fehleranfälligkeit: Ein falsches Einrücken in Zeile 340 kann den Start des gesamten Stacks verhindern.
  • Wartbarkeit: Wenn du nur an der Datenbank arbeiten willst, musst du dich durch hunderte Zeilen scrollen.

Für Projekt Phoenyx nutzen wir ein modernes Feature von Docker Compose, das erst kürzlich stabil wurde und die Art und Weise, wie wir Stacks bauen, revolutioniert: Includes.

Das Konzept: Anstatt alles in eine Datei zu quetschen, erstellen wir eine Hauptdatei (Master), die wie ein Inhaltsverzeichnis funktioniert und auf spezialisierte Unterdateien verweist.

1. Die Master-Datei: Ein Inhaltsverzeichnis

Unsere zentrale docker-compose.yml liegt im Root-Verzeichnis unseres Projekts (/opt/containers/docker-compose.yml). Sie definiert keine Services direkt. Sie delegiert.

# Die Zentrale: /opt/containers/docker-compose.yml
include:
  - ./networks/network.yml
  - ./socket-proxy/socket-proxy-docker-compose.yml
  - ./traefik/traefik-docker-compose.yml
  - ./crowdsec/crowdsec-docker-compose.yml
  - ./authentik/authentik-docker-compose.yml
  - ./mariadb/mariadb-docker-compose.yml

Wenn wir jetzt im Hauptverzeichnis den Befehl docker compose up -d ausführen, liest Docker all diese Dateien ein, fügt sie im Speicher zu einer riesigen Konfiguration zusammen und führt sie aus. Für Docker ist es ein Stack. Für uns sind es sauber getrennte Module.

Der Vorteil ist immens: Wenn wir an der Datenbank arbeiten wollen, öffnen wir nur die mariadb-docker-compose.yml. Wir können nichts versehentlich bei Traefik kaputt machen, weil diese Datei gar nicht geöffnet ist.


2. Deep Dive: Die Netzwerk-Architektur

Kommen wir zum Herzstück unserer Infrastruktur. Viele Tutorials machen es sich einfach: Sie erstellen ein einziges Netzwerk (z.B. app-network) und werfen alle Container dort hinein. Das funktioniert, ist aber aus Sicherheitssicht ein Albtraum. Warum sollte die Datenbank direkt mit dem Webserver kommunizieren können, wenn sie nur von der App gebraucht wird?

Wir gehen einen professionelleren Weg. Wir nutzen Netzwerk-Segmentierung und manuelles IPAM (IP Address Management).

Warum manuelles IPAM?

Normalerweise würfelt Docker beim Starten von Containern IPs zufällig aus (z.B. 172.18.0.x). Das hat Nachteile:

  1. Überschneidungen: Wenn du viele Stacks hast, können sich Netzwerke überlappen.
  2. Firewall-Regeln: Es ist schwer, feste Regeln zu schreiben („Erlaube Zugriff von IP X“), wenn sich IP X bei jedem Neustart ändert.
  3. Ordnung: Wir wollen genau wissen, welcher Dienst wo wohnt.

In unserer network.yml definieren wir deshalb feste Subnetze. Wir haben uns für den Block 172.28.0.0/14 entschieden. Das gibt uns genug Platz für alles, was wir jemals bauen wollen.

Unsere Sicherheits-Zonen (Analyse der network.yml)

Schauen wir uns die Definitionen in der network.yml genau an. Wir teilen unsere Infrastruktur in logische Blöcke auf. Jeder Block ist ein /25 Subnetz, was uns 128 IP-Adressen pro Netzwerk gibt – mehr als genug für jeden Zweck.

A. Die Hochsicherheits-Zonen (Internal Networks)

Diese Netzwerke haben das Flag internal: true. Das bedeutet: Container in diesem Netzwerk haben keinen Zugriff auf das Internet und sind von außen nicht erreichbar. Es ist ein digitaler Tresorraum.

  • authentik-internal (172.28.0.0/25):
    Hier lebt unser Identity Provider. Der Authentik-Server muss mit seiner Datenbank (PostgreSQL) und seinem Redis-Cache sprechen. Niemand sonst hat hier Zutritt.
  • database_internal (172.28.0.128/25):
    Das Zuhause unserer MariaDB. Deine WordPress-Instanz wird Mitglied in diesem Netzwerk, um Daten zu speichern. Aber die Datenbank selbst kann nicht „nach Hause telefonieren“ und ist vor direkten Angriffen aus dem Web geschützt.
  • socket_proxy (172.28.2.0/25):
    Das sensibelste Netzwerk von allen. Hier läuft die Kommunikation mit dem Docker-Daemon. Nur Traefik (um Container zu erkennen) und der Socket-Proxy selbst sind hier erlaubt.
  # Beispiel aus der network.yml
  database_internal:
    name: database_internal
    driver: bridge
    internal: true  # <--- Der Sicherheits-Schalter!
    ipam:
      driver: default
      config:
      - subnet: 172.28.0.128/25

B. Die Kommunikations-Zonen (Attachable Networks)

Diese Netzwerke verbinden verschiedene Stacks miteinander. Hier nutzen wir attachable: true, damit wir später auch Container aus anderen Compose-Dateien (z.B. eine separate WordPress-Installation) in dieses Netzwerk einhängen können.

  • traefik_public (172.28.1.128/25):
    Das ist unsere "öffentliche Straße". Jeder Container, der eine Webseite bereitstellt (z.B. Authentik Dashboard, WordPress, Nextcloud), muss Mitglied in diesem Netzwerk sein. Warum? Weil Traefik (unser Reverse Proxy) auch hier drin ist. Traefik holt die Anfrage aus dem Internet ab und reicht sie über dieses Netzwerk an den Container weiter.
  • crowdsec (172.28.1.0/25):
    Ein Spezialnetzwerk für die Sicherheit. Traefik und Crowdsec tauschen hier Informationen aus ("Ist diese IP böse?").

IPv6 - Wir sind bereit für die Zukunft

Ein Detail, das oft übersehen wird: Unsere network.yml aktiviert konsequent IPv6 (enable_ipv6: true). Wir weisen jedem Netzwerk auch ein IPv6-Subnetz zu (z.B. fd00:1:be:a:7001:0:1::/123). Damit ist unsere Infrastruktur zukunftssicher, auch wenn viele Docker-Setups heute noch rein auf IPv4 setzen.

Zusammenfassung der Strategie:
Wir nutzen das Prinzip der geringsten Privilegien ("Least Privilege"). Ein Container ist nur in den Netzwerken Mitglied, die er zwingend braucht. Eine Datenbank braucht kein Internet -> ab ins interne Netz. Ein Webserver muss erreichbar sein -> ab ins Traefik-Netz.

Wie geht es weiter?

Wir haben jetzt das Fundament und den Bauplan. Die "Straßen" (Netzwerke) sind gebaut, die Bauleitung (Docker Compose Master-File) steht bereit. Im nächsten Teil fangen wir an, die ersten Bewohner einziehen zu lassen. Wir beginnen mit dem wichtigsten Sicherheits-Feature: Dem Schutz unseres Docker-Sockets.

Published inHowToNerdStuffTutorial

Sei der Erste der einen Kommentar abgibt

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert