für sichere Deployments einrichten
Deployment-Keys, die zu breit berechtigt sind oder ohne Rotation jahrelang im Einsatz bleiben, sind ein unterschätztes Risiko. Ed25519-Schlüsselpaare, GitLab Deploy Keys, korrekte Known-Hosts-Verwaltung und sichere Rotation sind die Bausteine für auditierbare, widerrufbare SSH-Verbindungen in automatisierten Magento-Deployments.
Inhaltsverzeichnis
- 1. SSH-Schlüsseltypen: RSA, ECDSA und Ed25519 im Vergleich
- 2. GitLab Deploy Keys vs. Personal Access Tokens vs. CI-Variablen
- 3. Ed25519-Schlüsselpaar für Deployments generieren
- 4. Private Key als GitLab File-Variable hinterlegen
- 5. authorized_keys auf dem Zielserver korrekt konfigurieren
- 6. Known Hosts: Fingerprints sammeln und verwenden
- 7. Sichere vs. unsichere Key-Verwaltung im Vergleich
- 8. Key-Rotation ohne Deployment-Downtime
- 9. SSH-Key-Setup in der GitLab-Pipeline vollständig integrieren
- 10. Zusammenfassung
- 11. FAQ
1. SSH-Schlüsseltypen: RSA, ECDSA und Ed25519 im Vergleich
Für neue Deployment-Keys ist Ed25519 die richtige Wahl. Der Algorithmus basiert auf Elliptic-Curve-Kryptographie und erzeugt kurze, schnell zu verarbeitende Schlüssel, die bei gleichem Sicherheitsniveau erheblich kompakter sind als RSA-4096. Ed25519 ist seit OpenSSH 6.5 verfügbar und auf allen aktuellen Linux-Systemen unterstützt – inklusive der GitLab-Runner-Umgebungen. Im Gegensatz zu ECDSA, bei dem fehlerhafte RNG-Implementierungen historisch zu Key-Kompromittierungen geführt haben, ist Ed25519 konstruktionsbedingt robust gegen solche Angriffsvektoren.
RSA-Keys mit 2048 Bit sollten für neue Deployments nicht mehr verwendet werden. RSA-4096 ist zwar noch sicher, aber langsamer und produziert deutlich längere Schlüsseldateien, was in automatisierten Systemen keine Vorteile bringt. Für bestehende RSA-Keys gilt: Sie müssen nicht sofort rotiert werden, aber bei der nächsten planmäßigen Rotation auf Ed25519 wechseln. Der Unterschied in der Sicherheitsbewertung liegt nicht primär im Algorithmus, sondern in der Schlüssellänge und der korrekten Verwaltung.
Ein Aspekt, der oft übersehen wird: Der Schlüsseltyp beeinflusst auch die Zeilenlänge in der authorized_keys. Ed25519-Keys passen in eine kompakte Zeile und lassen sich einfacher überprüfen und in GitLab-Variablen speichern. RSA-4096-Keys sind mehrzeilig, was in einigen CI-Variablen-Konfigurationen zu Problemen führen kann, wenn Zeilenumbrüche unbeabsichtigt escaped werden.
2. GitLab Deploy Keys vs. Personal Access Tokens vs. CI-Variablen
GitLab bietet drei Wege, SSH-Zugriff für automatisierte Prozesse zu konfigurieren. Deploy Keys sind SSH-Public-Keys, die direkt an ein Repository gebunden sind und nur Lesezugriff (oder optional Schreibzugriff) auf dieses eine Repository gewähren. Sie sind ideal für Fälle, in denen der Runner Code aus einem privaten Repository klonen muss. Ein Deploy Key ist kein Personenkonto – er gehört dem Repository, nicht einem User.
CI/CD-Variablen vom Typ File sind der richtige Weg, den Private Key für Server-Deployments im Runner verfügbar zu machen. Der Unterschied zu Deploy Keys: Der Private Key in einer CI-Variable authentifiziert den Runner gegenüber einem Server, nicht gegenüber GitLab selbst. Diese Trennung ist wichtig: Für Repository-Zugriff Deploy Keys nutzen, für Server-Deployments CI-Variablen mit dem Deployment-Key-Paar. Beide kombinieren manche Teams – ein Deploy Key für Composer-Pakete aus privaten GitLab-Repositories, ein separates Schlüsselpaar für den Zielsystem-SSH-Zugriff.
stages:
- build
- deploy
- verify
variables:
GIT_STRATEGY: fetch
COMPOSER_CACHE_DIR: .cache/composer
# Reusable SSH setup anchor — loaded before each deploy/verify job
.ssh_init: &ssh_init
before_script:
# Start SSH agent and load deployment private key (File variable)
- eval $(ssh-agent -s)
- chmod 600 "$SSH_PRIVATE_KEY"
- ssh-add "$SSH_PRIVATE_KEY"
# Write known_hosts from CI variable — prevents MitM attacks
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
deploy:production:
stage: deploy
<<: *ssh_init
script:
- ssh -o StrictHostKeyChecking=yes "$DEPLOY_USER@$DEPLOY_HOST" \
"bash -s -- $CI_COMMIT_TAG" < scripts/deploy.sh
environment:
name: production
url: https://mironsoft.de
only:
- tags
when: manual
3. Ed25519-Schlüsselpaar für Deployments generieren
Ein Deployment-Schlüsselpaar wird einmalig auf einer sicheren lokalen Maschine generiert und nie auf dem Produktionsserver erzeugt. Der Befehl lautet: ssh-keygen -t ed25519 -C "gitlab-deploy@mironsoft.de" -f ~/.ssh/magento_deploy_ed25519 -N "". Das -N "" setzt keine Passphrase – das ist bei automatisierten Deployments notwendig, weil kein interaktiver Passphrase-Input möglich ist. Der Private Key wird in GitLab als CI-Variable hinterlegt, der Public Key auf dem Zielserver in die authorized_keys des Deployment-Users eingetragen.
Der Kommentar (-C) sollte den Verwendungszweck klar beschreiben: Wer diesen Key wofür verwendet. Das erleichtert die Verwaltung, wenn auf einem Server mehrere Keys in der authorized_keys stehen. Ein typisches Namensschema für Deployment-Keys: gitlab-deploy-[projekt]-[umgebung]@[organisation]. Mit diesem Schema sieht man sofort, ob ein Key für Production oder Staging gedacht ist und zu welchem Projekt er gehört.
4. Private Key als GitLab File-Variable hinterlegen
In den Projekteinstellungen unter Settings > CI/CD > Variables legt man eine neue Variable an: Name SSH_PRIVATE_KEY, Typ File, Wert: den vollständigen Inhalt der privaten Schlüsseldatei inklusive der Headerzeilen -----BEGIN OPENSSH PRIVATE KEY----- und -----END OPENSSH PRIVATE KEY-----. Die Variable muss als Protected markiert sein, wenn sie nur für Protected Branches und Tags verfügbar sein soll. Masked für File-Variablen ist in GitLab nicht möglich, weil der Inhalt mehrere Zeilen hat.
Ein häufiger Fehler: Die Variable als normalen String statt als File anlegen. Als String erhält der Runner den Key-Inhalt als Umgebungsvariable, nicht als Dateipfad. Der ssh-add-Befehl erwartet aber einen Pfad. Die Fehlermeldung ist dann No such file or directory, obwohl die Variable gesetzt ist. Als File-Variable schreibt GitLab den Inhalt in eine temporäre Datei und übergibt deren Pfad als Umgebungsvariable $SSH_PRIVATE_KEY – genau das, was chmod 600 "$SSH_PRIVATE_KEY" && ssh-add "$SSH_PRIVATE_KEY" erwartet.
5. authorized_keys auf dem Zielserver korrekt konfigurieren
Der Public Key des Deployment-Schlüsselpaares wird in die Datei ~/.ssh/authorized_keys des Deployment-Users auf dem Zielserver eingetragen. Die Datei und das .ssh-Verzeichnis müssen die korrekten Berechtigungen haben: chmod 700 ~/.ssh und chmod 600 ~/.ssh/authorized_keys. Falsche Berechtigungen führen dazu, dass sshd den Key ignoriert – ohne Fehlermeldung, die den wahren Grund nennt.
Für zusätzliche Sicherheit kann jeder authorized_keys-Eintrag mit Optionen eingeschränkt werden. Die Option no-pty,no-agent-forwarding,no-X11-forwarding verhindert, dass der Deployment-Key für interaktive Sessions oder Agent-Forwarding missbraucht wird. Mit from="[IP-des-Runners]" kann der Key auf Verbindungen von bestimmten IP-Adressen beschränkt werden – praktisch, wenn der GitLab-Runner eine feste IP hat. Mit command="/opt/deploy/entrypoint.sh" wird der Key auf ein einziges Skript beschränkt, was aber die Deployment-Flexibilität stark einschränkt.
6. Known Hosts: Fingerprints sammeln und verwenden
Die known_hosts-Datei ist der Mechanismus, mit dem SSH sicherstellt, dass der Zielserver derselbe ist wie beim letzten Verbindungsaufbau. In automatisierten Umgebungen muss diese Datei vorab befüllt werden, weil kein interaktiver Fingerprint-Bestätigungs-Dialog möglich ist. Der Befehl ssh-keyscan -H $DEPLOY_HOST liefert die Hostschlüssel des Servers im known_hosts-Format. Das -H-Flag hasht den Hostnamen, was empfohlen wird, damit ein kompromittierter known_hosts-Eintrag keine Hostnamen preisgibt.
Den Output von ssh-keyscan kopiert man als GitLab CI/CD-Variable SSH_KNOWN_HOSTS. Im before_script schreibt man ihn mit echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts in die known_hosts-Datei des Runners. Die Kombination aus gespeichertem Fingerprint und StrictHostKeyChecking=yes stellt sicher, dass die Pipeline sofort fehlschlägt, wenn der Server-Fingerprint nicht übereinstimmt – ein wichtiger Sicherheitsindikator für kompromittierte oder gewechselte Server.
# Collecting SSH fingerprints for known_hosts (run locally, not in pipeline):
# ssh-keyscan -H production.mironsoft.de > known_hosts_production
# ssh-keyscan -H staging.mironsoft.de > known_hosts_staging
# Then paste content into GitLab CI variables SSH_KNOWN_HOSTS_PROD / SSH_KNOWN_HOSTS_STAG
verify:connection:
stage: verify
before_script:
- eval $(ssh-agent -s)
- chmod 600 "$SSH_PRIVATE_KEY"
- ssh-add "$SSH_PRIVATE_KEY"
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
# Use environment-specific known_hosts variable
- echo "$SSH_KNOWN_HOSTS_PROD" > ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
# Verify connection and basic Magento availability
- ssh -o StrictHostKeyChecking=yes "$DEPLOY_USER@$DEPLOY_HOST" \
"cd $DEPLOY_PATH/current && bin/magento --version"
# Verify HTTP health endpoint responds with 200
- curl --fail --silent --output /dev/null \
--write-out "HTTP %{http_code}" \
"https://mironsoft.de/health"
environment:
name: production
only:
- tags
7. Sichere vs. unsichere Key-Verwaltung im Vergleich
Die Unterschiede zwischen sicherer und unsicherer SSH-Key-Verwaltung in GitLab-Deployments lassen sich in wenigen Punkten zusammenfassen, die jeweils konkrete Konsequenzen haben.
| Aspekt | Unsichere Praxis | Sichere Praxis | Risiko bei unsicher |
|---|---|---|---|
| Schlüsseltyp | RSA-2048 oder älter | Ed25519 | Schwächere Sicherheitsmargin |
| Variable-Typ | String-Variable | File-Variable | ssh-add schlägt fehl |
| Host-Prüfung | StrictHostKeyChecking=no | StrictHostKeyChecking=yes | MitM-Angriff möglich |
| Key-Berechtigung | Root-User oder breite sudo-Rechte | Dedizierter Deploy-User ohne sudo | Kompromittierung = Root-Zugriff |
| Key-Rotation | Kein Rotationsplan | Jährlich oder bei Personalwechsel | Veraltete Keys im Einsatz |
Besonders der Punkt StrictHostKeyChecking=no ist in vielen Projekten als Quick-Fix eingebaut worden, um Verbindungsprobleme zu umgehen. Diese Einstellung sollte in keiner Produktionspipeline existieren. Der korrekte Fix ist immer, die known_hosts-Variable zu aktualisieren – nicht, die Sicherheitsprüfung zu deaktivieren.
8. Key-Rotation ohne Deployment-Downtime
Die Rotation eines Deployment-Keys ohne Unterbrechung laufender Deployments ist mit einem einfachen zweistufigen Verfahren möglich. Zuerst generiert man ein neues Ed25519-Schlüsselpaar. Den neuen Public Key fügt man als zweiten Eintrag in die authorized_keys des Deployment-Users ein – der alte Key bleibt dabei erhalten. Dann aktualisiert man die GitLab CI/CD-Variable SSH_PRIVATE_KEY auf den neuen Private Key und löst einen Test-Pipeline-Lauf aus.
Sobald der Test erfolgreich ist und bestätigt wird, dass die Pipeline den neuen Key korrekt nutzt, entfernt man den alten Public Key aus der authorized_keys. Laufende Jobs, die noch mit dem alten Key verbunden sind, werden durch diesen zweiten Schritt nicht unterbrochen, weil die SSH-Verbindung bereits aufgebaut ist. Zukünftige Jobs verwenden ausschließlich den neuen Key. Die Rotation sollte im Team kommuniziert werden und in einem Wartungsfenster stattfinden, in dem keine kritischen Deployments geplant sind.
9. SSH-Key-Setup in der GitLab-Pipeline vollständig integrieren
Ein vollständig integriertes SSH-Key-Setup in einer GitLab-Pipeline nutzt YAML-Anchors (&anchor und *anchor), um den SSH-Initialisierungs-Block nicht in jedem Job zu wiederholen. Der Anchor enthält den kompletten before_script-Block mit SSH-Agent-Start, Key-Load und known_hosts-Schreibung. Jobs, die SSH benötigen, mergen diesen Anchor mit <<: *ssh_init. Das reduziert Wiederholung, stellt aber sicher, dass der SSH-Setup in jedem Job frisch initialisiert wird – weil Runner-Umgebungen nach jedem Job zurückgesetzt werden.
Für Multi-Environment-Setups mit Staging und Production empfiehlt sich eine Variable SSH_KNOWN_HOSTS pro Environment-Scope. GitLab erlaubt es, unterschiedliche Variablenwerte für unterschiedliche Environments (staging, production) zu definieren. So holt sich der Deploy-Job automatisch die richtigen Known-Hosts für seine Umgebung, ohne dass die Pipeline-Konfiguration für jede Umgebung manuell angepasst werden muss. Dasselbe gilt für DEPLOY_HOST und DEPLOY_USER.
stages:
- build
- package
- deploy
- verify
# SSH initialization anchor — merged into any job needing SSH access
.ssh_init: &ssh_init
before_script:
- eval $(ssh-agent -s)
- chmod 600 "$SSH_PRIVATE_KEY"
- ssh-add "$SSH_PRIVATE_KEY"
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
# SSH_KNOWN_HOSTS is environment-scoped in GitLab variable settings
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
# Verify key is loaded before proceeding
- ssh-add -l
deploy:production:
stage: deploy
<<: *ssh_init
script:
- |
ssh -o StrictHostKeyChecking=yes \
-o ConnectTimeout=10 \
"$DEPLOY_USER@$DEPLOY_HOST" bash -s <<'REMOTE'
set -euo pipefail
readonly RELEASE_ID="$(date +%Y%m%d-%H%M%S)"
readonly RELEASE_PATH="$DEPLOY_PATH/releases/$RELEASE_ID"
mkdir -p "$RELEASE_PATH"
echo "[INFO] Prepared release directory: $RELEASE_PATH"
REMOTE
environment:
name: production
rules:
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
when: manual
verify:production:
stage: verify
<<: *ssh_init
script:
- curl --fail --max-time 10 "https://mironsoft.de/health"
- ssh -o StrictHostKeyChecking=yes "$DEPLOY_USER@$DEPLOY_HOST" \
"cd $DEPLOY_PATH/current && bin/magento cache:status"
environment:
name: production
rules:
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
10. Zusammenfassung
Sichere SSH-Key-Verwaltung in GitLab-Deployments beginnt mit der richtigen Wahl des Algorithmus – Ed25519 statt RSA-2048 – und endet bei einem definierten Rotationsplan. Der Private Key gehört als File-Variable in GitLab CI/CD, der Public Key in die authorized_keys eines dedizierten Deployment-Users ohne sudo-Rechte. Known Hosts werden vorab mit ssh-keyscan gesammelt und als Variable hinterlegt. StrictHostKeyChecking=yes ist keine optionale Sicherheitsmaßnahme, sondern Pflicht in jeder produktionsnahen Pipeline.
Die Rotation von Deployment-Keys ist mit dem beschriebenen zweistufigen Verfahren ohne Downtime möglich und sollte mindestens einmal jährlich oder bei Personalwechseln durchgeführt werden. Teams, die diese Grundlagen sauber umgesetzt haben, können auf jedem weiteren Sicherheitsaudit mit klaren Antworten punkten: welcher Key wird für welchen Zweck verwendet, wann wurde er zuletzt rotiert und wie kann er im Notfall sofort widerrufen werden.
SSH-Keys und Deploy Keys in GitLab — Das Wichtigste auf einen Blick
Schlüsseltyp
Ed25519 für alle neuen Deployment-Keys. Kürzer, schneller und konstruktionsbedingt robuster als RSA-2048.
GitLab-Variable
SSH_PRIVATE_KEY als File-Variable anlegen, nicht als String. ssh-add erwartet einen Dateipfad, keine Stringvariable.
Known Hosts
ssh-keyscan -H $DEPLOY_HOST ausführen, Output als CI-Variable speichern, vor jedem Job in ~/.ssh/known_hosts schreiben.
Key-Rotation
Neuen Key hinzufügen, Variable aktualisieren, testen, alten Key entfernen. Beide Keys kurzzeitig aktiv – kein Deployment-Ausfall.