Loading...
SSL/TLS für PostgreSQL mit Docker einrichten

SSL/TLS für PostgreSQL mit Docker einrichten

Lukas Mauser - Co-Founder von sliplane.ioLukas Mauser
9 min

Ziel

Wenn du PostgreSQL produktiv einsetzen möchtest, ist die Einrichtung von Transport Layer Security (TLS) Pflicht, um Man-in-the-Middle-Angriffe zu verhindern. In dieser Schritt-für-Schritt-Anleitung zeige ich dir, wie du dich mit deiner Datenbank sicher verbindest – und zwar mit einer eigenen lokalen Certificate Authority. Wenn du eine tiefere Erklärung zu allen Einstellungen brauchst, schau dir die offizielle Doku zum TLS-Setup in PostgreSQL an.

In diesem Beispiel erlauben wir NUR verschlüsselte Verbindungen und setzen sslmode auf verify-full, was die strengste Verbindungseinstellung in PostgreSQL ist.

Wenn du mitmachen willst, findest du das Beispiel-Repository hier: PostgreSQL mit TLS Repository

Voraussetzungen

Du brauchst Docker auf deinem Rechner. Wenn du einen Mac oder Windows nutzt, installiere Docker Desktop und starte es im Hintergrund. Prüfe, ob Docker läuft, indem du docker ps im Terminal eingibst. Du solltest eine Liste laufender Container sehen – oder zumindest keine Fehlermeldung, falls Docker läuft.

Video Tutorial (Textversion unten)

Schritt 1: Zertifikate generieren

Certificate Authority (CA) erstellen

Hinweis: Alle im Folgenden generierten Zertifikatsdateien (CA, Server und Client) sind streng vertraulich zu behandeln. Füge sie nicht ins Git-Repository ein, logge sie nicht und packe sie nicht ins Docker-Image (nur als Runtime-Env!). Falls nötig, ergänze deine .gitignore.

Zuerst erstellen wir einen Root-CA-Key und ein Zertifikat, das unsere Server- und Client-Zertifikate signiert. Du kannst den Namen anpassen, wenn du möchtest. Unsere CA ist 365 Tage gültig und muss danach erneuert werden.

openssl genrsa -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 365 -out rootCA.crt -subj "/CN=MyLocalCA"

Server-Zertifikat erstellen

Erzeuge einen Key und ein CSR (Certificate Signing Request) und signiere es mit der CA:

Wichtig: Hier musst du den Domainnamen angeben, auf dem deine PostgreSQL-Instanz läuft. Im Beispiel nutze ich localhost, aber passe den Hostnamen an, falls du einen anderen verwendest.

openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/CN=localhost"
openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 365 -sha256

Client-Zertifikat erstellen

Dieses Zertifikat nutzt der Client zur Authentifizierung. Nur Zertifikate, die von derselben CA wie das Server-Zertifikat signiert wurden, funktionieren.

Das Client-Zertifikat erzeugen wir genauso wie das Server-Zertifikat. Als Common Name nehme ich den postgres-User:

openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr -subj "/CN=postgres"
openssl x509 -req -in client.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out client.crt -days 365 -sha256

Schritt 2: Konfigurations-Skripte für TLS in PostgreSQL

Lege im Projektroot eine Datei namens ssl-config.sh an. Das Skript enthält die Anweisungen, um SSL in PostgreSQL zu aktivieren und TLS für alle Verbindungen mit sslmode=verify-full zu erzwingen.

Wenn du unsichere Verbindungen erlauben willst, kannst du den Abschnitt "Force SSL" im Skript weglassen oder den sslmode lockern.

#!/bin/bash
set -e

# PostgreSQL für SSL konfigurieren
echo "ssl = on" >> /var/lib/postgresql/data/postgresql.conf
echo "ssl_cert_file = '/var/lib/postgresql/server.crt'" >> /var/lib/postgresql/data/postgresql.conf
echo "ssl_key_file = '/var/lib/postgresql/server.key'" >> /var/lib/postgresql/data/postgresql.conf
echo "ssl_ca_file = '/var/lib/postgresql/rootCA.crt'" >> /var/lib/postgresql/data/postgresql.conf

# SSL für alle Verbindungen erzwingen
echo "hostssl all all all cert clientcert=verify-full" > /var/lib/postgresql/data/pg_hba.conf

Erstelle eine weitere Datei im Projektroot namens entrypoint.sh. Dieses Skript kopiert die Zertifikate beim Start in den Container, bevor der Default-Entrypoint ausgeführt wird.

#!/bin/bash
set -e

# Zertifikatsdateien hinzufügen
echo "$SERVER_CRT" > /var/lib/postgresql/server.crt
echo "$SERVER_KEY" > /var/lib/postgresql/server.key
echo "$ROOT_CA_CRT" > /var/lib/postgresql/rootCA.crt

# Dateiberechtigungen der Zertifikate aktualisieren
chmod 600 /var/lib/postgresql/server.* /var/lib/postgresql/rootCA.crt
chown postgres:postgres /var/lib/postgresql/server.* /var/lib/postgresql/rootCA.crt

# Basis-Entrypoint ausführen
docker-entrypoint.sh postgres

Das Skript benötigt 3 Umgebungsvariablen:

  • SERVER_CRT
  • SERVER_KEY
  • ROOT_CA_CRT

Der Inhalt dieser Variablen ist der Klartext der drei Zertifikatsdateien: server.crt, server.key und rootCA.crt.

Schritt 3: Docker-Image mit TLS bauen

Lege eine Datei namens Dockerfile an. Wir nutzen das Base-Image postgres:17.5, kopieren unsere Skripte hinein, machen sie ausführbar und ersetzen den Default-Entrypoint:

# Starte mit postgres 17.5 als Basis-Image
FROM postgres:17.5

# Kopiere das ssl-config Skript, das beim ersten Start ausgeführt wird
COPY ssl-config.sh /docker-entrypoint-initdb.d/ssl-config.sh

# Kopiere das entrypoint Skript ins Image
COPY entrypoint.sh /usr/local/bin/entrypoint.sh

# Mache das entrypoint Skript ausführbar
RUN chmod +x /usr/local/bin/entrypoint.sh

# Setze den Entrypoint
ENTRYPOINT [ "/usr/local/bin/entrypoint.sh"]

Schritt 4: Verbindung testen

Um zu prüfen, ob alles funktioniert, bauen und starten wir das Image lokal und versuchen, uns zu verbinden. Mit dem psql-Client testen wir die Verbindung. Falls du ihn noch nicht installiert hast, installiere ihn z.B. mit brew install postgresql auf macOS.

Hinweis: Dieser lokale Test funktioniert nur, wenn du beim Server-Zertifikat als Common Name localhost gesetzt hast. Sonst musst du ein neues Zertifikat mit localhost als CN erstellen.

Image bauen:

docker build -t postgres-tls .

Image starten:

docker run \
 -e POSTGRES_PASSWORD=secret \
 -e SERVER_CRT="$(cat server.crt)" \
 -e SERVER_KEY="$(cat server.key)" \
 -e ROOT_CA_CRT="$(cat rootCA.crt)" \
 -p 5432:5432 --name postgres-tls postgres-tls

Achte darauf, die richtigen Pfade zu den Zertifikatsdateien zu verwenden, falls du sie verschoben hast.

Verbindung testen:

# Damit solltest du in die PostgreSQL-Shell kommen:
psql "host=localhost dbname=postgres user=postgres sslmode=verify-full sslrootcert=rootCA.crt sslcert=client.crt sslkey=client.key"

# Das hier sollte fehlschlagen, weil wir TLS erzwingen:
psql "host=localhost dbname=postgres user=postgres sslmode=disable"

Wichtig: Führe das aus demselben Verzeichnis aus, in dem deine Zertifikate liegen, oder passe die Pfade an. Außerdem: Die Berechtigungen auf client.key müssen auf 600 stehen (chmod 600 client.key).

Schritt 5: Deployment

Damit die Datenbank über das Internet erreichbar ist, deployen wir sie auf Sliplane.

Sliplane ist ein günstiger Cloud-Provider, der das Deployment und Management von containerisierten Anwendungen super einfach macht.

1.) Lege ein GitHub-Repository mit den beiden oben beschriebenen Dateien an: entrypoint.sh aus Schritt 3 und Dockerfile aus Schritt 4. Ein Beispiel-Repo zum Forken findest du hier: PostgreSQL mit TLS Repository 2.) Logge dich bei Sliplane mit deinem GitHub-Account ein 3.) Erstelle ein neues Projekt und klicke auf "Deploy Service" 4.) Lege einen neuen Server an, auf dem deine PostgreSQL-Instanz laufen soll – du kannst mit dem Basiserver starten und später skalieren 5.) Wähle "Repository" als Deploy-Quelle 6.) Wähle dein PostgreSQL-Repository aus dem Dropdown

Falls das Repository nicht angezeigt wird, klicke auf "Configure Repository Access", um Sliplane Zugriff zu geben

7.) Im Bereich "Expose Service" stelle das Protokoll auf TCP um

Image description

8.) Im Bereich "Environment Variables" füge hinzu:

POSTGRES_PASSWORD – beliebiges Passwort SERVER_CRT – Inhalt deiner server.crt SERVER_KEY – Inhalt deiner server.key ROOT_CA_CRT – Inhalt deiner rootCA.crt

Image description

9.) Im Bereich "Volumes" füge ein neues Volume hinzu und wähle /var/lib/postgresql/data als Mount-Pfad

Image description

10.) Klicke auf "Deploy".

Hier siehst du, wie die Einstellungen aussehen sollten:

Image description

Nach dem Deploy bekommst du eine sliplane.app-Domain, die du für dein Server-Zertifikat nutzen kannst. Alternativ kannst du auch eine eigene Domain im Zertifikat verwenden und diese später an den Service hängen.

11.) Erstelle im Terminal ein neues Server-Zertifikat wie oben beschrieben und nutze deine sliplane.app-Domain als Common Name. Die Domain findest du in den Service-Einstellungen unter "Public Domain".

Image description

openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/CN=...sliplane.app"
openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 365 -sha256

Hinweis: Dein Service muss öffentlich sein, sonst bekommst du keine Public Domain (siehe Screenshot oben). Ersetze das \CN=... mit deiner eigenen sliplane.app-Domain.

12.) Ersetze die SERVER_CRT-Umgebungsvariable deines Sliplane-Services mit dem neuen Zertifikat. Nach dem Speichern wird ein neues Deployment ausgelöst.

Das war's! Jetzt hast du Zugriff auf eine PostgreSQL-Instanz über eine sichere Verbindung. Teste es mit:

psql "host=YOUR_APP.sliplane.app dbname=postgres user=postgres sslmode=verify-full sslrootcert=rootCA.crt sslcert=client.crt sslkey=client.key"

Hinweis: Ersetze die sliplane.app-Domain durch deine eigene, die auch im Zertifikat steht.

Gib das Tutorial gerne weiter!

Danke!

Lukas

Willkommen in der Container-Cloud

Sliplane macht es einfach, Container in der Cloud zu deployen und bei Bedarf zu skalieren. Probier es jetzt aus!