Shared, Private oder Self-Hosted?
Die Wahl des Runner-Typs ist keine Komfortfrage — sie ist eine Sicherheitsentscheidung. Shared Runner sind praktisch, aber ihr Sicherheitsmodell ist für Magento-Produktions-Deployments mit SSH-Keys und Datenbank-Credentials oft nicht ausreichend. Dieser Artikel zeigt, wo die Grenzen liegen und was Self-Hosted Runner leisten können.
Inhaltsverzeichnis
- 1. Die drei Runner-Typen und ihr Sicherheitsmodell
- 2. Shared Runner: Risiken und Einsatzgrenzen
- 3. Private Runner: Isolation auf Projekt- und Gruppenebene
- 4. Self-Hosted Runner: vollständige Kontrolle und ihre Kosten
- 5. Secrets-Schutz: wie Variablen in jedem Runner-Typ behandelt werden
- 6. Sichere Self-Hosted Runner-Konfiguration für Magento
- 7. Netzwerkisolation und Privileged Mode
- 8. Direkter Sicherheitsvergleich
- 9. Typische Sicherheitsfehler bei der Runner-Konfiguration
- 10. Zusammenfassung
- 11. FAQ
1. Die drei Runner-Typen und ihr Sicherheitsmodell
GitLab unterscheidet bei Runnern nach Sichtbarkeit und Registrierungsebene: Shared Runner sind für alle Projekte einer GitLab-Instanz verfügbar, werden von GitLab Inc. betrieben (bei SaaS) oder vom Admin einer Self-Managed-Instanz bereitgestellt. Group Runner (oft als Private Runner bezeichnet) sind auf eine GitLab-Gruppe und ihre Unterprojekte beschränkt. Project Runner sind exklusiv einem einzelnen Projekt zugeordnet. Self-Hosted Runner beschreibt in der Praxis alle Runner, bei denen das eigene Team die Hardware, das Betriebssystem und die gitlab-runner-Software selbst betreibt — unabhängig davon, ob sie auf Shared-, Group- oder Project-Ebene registriert sind.
Das Sicherheitsmodell unterscheidet sich fundamental: Bei Shared Runnern auf GitLab.com führt derselbe Runner-Prozess Jobs von tausenden verschiedener Projekte und Organisationen aus. Beim Self-Hosted Runner führt der Runner ausschließlich Jobs der eigenen Organisation aus. Das bedeutet nicht, dass Shared Runner unsicher sind — GitLab.com setzt Docker-Isolation mit kurzlebigen Containern ein — aber es bedeutet, dass der eigene Einfluss auf Isolation, Netzwerkzugriff und Secrets-Schutz auf einem fremden Runner minimal ist.
Für Magento-Deployments, bei denen SSH-Private-Keys, Datenbank-Passwörter, Composer-Auth-Tokens und Production-Server-Adressen in GitLab-CI-Variablen gespeichert sind, ist die Frage der Runner-Sicherheit keine akademische Diskussion. Ein kompromittierter Runner mit Zugriff auf diese Variablen kann SSH-Verbindungen zu Produktionsservern aufbauen. Deshalb lohnt es sich, die Sicherheitsgarantien der verschiedenen Runner-Typen konkret zu verstehen.
2. Shared Runner: Risiken und Einsatzgrenzen
Shared Runner auf GitLab.com verwenden den Docker-Executor mit kurzlebigen Containern. Jeder Job startet einen frischen Container und löscht ihn nach Abschluss. Das verhindert, dass ein Job Daten aus einem vorherigen Job desselben Containers liest. Die Isolation zwischen Jobs verschiedener Projekte erfolgt auf Container-Ebene — vorausgesetzt, der Container ist nicht im Privileged Mode gestartet, was auf GitLab.com standardmäßig nicht der Fall ist.
Das verbleibende Risiko bei Shared Runnern liegt weniger in der technischen Isolation als im Vertrauen in den Betreiber und in der Sichtbarkeit von Variablen. Auf GitLab.com sind Protected Variablen nur in Jobs sichtbar, die auf Protected Branches oder Protected Tags laufen. Ungeschützte Variablen sind in jedem Job sichtbar, auch in Merge-Request-Pipelines von Fork-Repositories. Wenn ein Repository öffentlich ist und Pipelines aus Forks erlaubt, können externe Nutzer Jobs auslösen, die Zugriff auf ungeschützte CI/CD-Variablen erhalten. Für private Repositories ohne Forks ist dieses Risiko geringer, aber es bleibt die Grundfrage: Warum sollte ein Magento-Team, das Production-SSH-Keys in GitLab speichert, diese Keys auf einer fremden Infrastruktur exponieren?
3. Private Runner: Isolation auf Projekt- und Gruppenebene
Ein Group Runner, der für eine Gruppe mit mehreren Magento-Projekten registriert ist, bringt eine wesentliche Verbesserung gegenüber Shared Runnern: Er führt nur Jobs dieser Gruppe aus. Andere GitLab-Gruppen oder öffentliche Projekte haben keinen Zugriff auf diesen Runner. Das reduziert die Angriffsfläche erheblich: Nur Mitglieder der eigenen Gruppe können Pipelines auslösen, die auf diesem Runner laufen.
Ein Project Runner — exklusiv einem einzigen Projekt zugeordnet — bietet die stärkste Isolation auf GitLab-Ebene. Jobs anderer Projekte können diesen Runner nicht nutzen, selbst wenn sie zur gleichen Gruppe gehören. Dieses Modell ist sinnvoll für Production-Deploy-Runner, auf denen SSH-Keys für Produktionsserver gespeichert sind: Nur Pipelines des einen Projekts können diesen Runner und damit die gespeicherten Secrets nutzen.
4. Self-Hosted Runner: vollständige Kontrolle und ihre Kosten
Ein Self-Hosted Runner — eine eigene Maschine, auf der das eigene Team gitlab-runner betreibt — gibt vollständige Kontrolle über Betriebssystem, Netzwerkzugriff, installierte Tools, gespeicherte Credentials und Ausführungsumgebung. Kein Shared-Runner-Betreiber, kein Cloud-Provider und keine andere Organisation hat Zugriff auf diese Maschine. Das ist das stärkste Isolation-Modell — aber es bringt entsprechenden Betriebsaufwand: Patch-Management, Monitoring, Backups, Incident Response bei Ausfällen und Zugang für das Team.
Für Magento-Deployments mit sensiblen Production-SSH-Keys und Datenbank-Credentials ist der Self-Hosted Runner in den meisten Fällen die richtige Wahl. Der Deploy-Runner, der SSH-Verbindungen zu Produktionsservern aufbaut, sollte immer selbst betrieben werden. Der Build-Runner für composer install und setup:di:compile ohne Production-Secrets kann dagegen auf GitLab.com Shared Runnern laufen — hier reichen die Sicherheitsgarantien von GitLab.com in der Regel aus, solange keine Production-Credentials im Build-Job exponiert werden.
5. Secrets-Schutz: wie Variablen in jedem Runner-Typ behandelt werden
GitLab CI/CD-Variablen werden vom GitLab-Server verschlüsselt gespeichert und zur Laufzeit des Jobs als Umgebungsvariablen in die Runner-Umgebung injiziert. Der Unterschied zwischen Runner-Typen liegt darin, wer die Umgebung kontrolliert, in der diese Variablen existieren. Auf einem Self-Hosted Runner kontrolliert das eigene Team, welche Prozesse auf der Maschine laufen, welche Ports offen sind und wer SSH-Zugang hat. Auf einem Shared Runner des Cloud-Providers existiert diese Kontrolle nicht.
Masked Variablen in GitLab verhindern, dass der Wert in Job-Logs angezeigt wird. Protected Variablen sind nur in Jobs auf Protected Branches und Tags sichtbar. Diese Schutzmechanismen gelten unabhängig vom Runner-Typ — aber sie schützen nicht vor einem kompromittierten Runner-Host selbst. Ein Angreifer mit Zugriff auf den Runner-Prozess kann Umgebungsvariablen aus dem Prozess-Kontext auslesen, bevor sie maskiert werden. Deshalb gilt: Je sensibler die Variable (Production-SSH-Key, Datenbank-Root-Passwort), desto stärker muss die Isolation des Runners sein.
6. Sichere Self-Hosted Runner-Konfiguration für Magento
Eine sichere Grundkonfiguration für einen Self-Hosted Runner umfasst mehrere Ebenen: den Runner-Prozess selbst mit minimalen Rechten, den Docker-Executor mit Ressourcenlimits und ohne Privileged Mode, und die Netzwerkkonfiguration, die ausgehende Verbindungen auf bekannte Ziele beschränkt. Die folgende Konfiguration zeigt einen produktionsnahen Setup für einen Magento-Build-Runner ohne Production-Secrets.
# /etc/gitlab-runner/config.toml — Secure self-hosted build runner
concurrent = 4
check_interval = 5
shutdown_timeout = 60
log_level = "warning"
log_format = "json"
[[runners]]
name = "magento-build-secure"
url = "https://gitlab.example.com"
token = "${GITLAB_RUNNER_TOKEN}"
executor = "docker"
limit = 2
maximum_timeout = 1500
# Only accept tagged jobs — no untagged job pickup
run_untagged = false
tag_list = ["build", "php84", "docker"]
[runners.docker]
tls_verify = false
image = "php:8.4-cli"
# Never run in privileged mode for build jobs
privileged = false
# Disable Docker socket access — no container escape via /var/run/docker.sock
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
# Allowed volumes: only cache, no host system paths
volumes = ["/runner-cache:/cache:rw"]
# No extra capabilities
cap_add = []
cap_drop = ["ALL"]
security_opt = ["no-new-privileges:true"]
memory = "3g"
memory_swap = "3g"
cpus = "2.0"
# Isolated network — no host network access
network_mode = "runner-net"
# Pull always to prevent stale image attacks
pull_policy = ["always"]
[runners.cache]
Type = "local"
Shared = false
Drei Sicherheitsaspekte sind in dieser Konfiguration besonders wichtig: privileged = false verhindert, dass Jobs Container-Escape-Techniken nutzen können. cap_drop = ["ALL"] mit leerem cap_add entfernt alle Linux-Capabilities vom Container. network_mode = "runner-net" platziert den Container in ein isoliertes Docker-Netzwerk ohne Zugang zum Host-Netzwerk oder zu internen Diensten. Für den Deploy-Runner, der SSH-Keys benötigt, gelten andere Regeln — aber dieser Runner sollte auf einem eigenen Host mit deutlich schärferen Zugriffskontrollen laufen.
7. Netzwerkisolation und Privileged Mode
Der Privileged Mode ist der gefährlichste Schalter in der Runner-Konfiguration. Ein Container im Privileged Mode hat nahezu vollständigen Zugriff auf den Host-Kernel und kann Linux-Namespaces verlassen. GitLab.com schaltet Privileged Mode standardmäßig ab. Auf Self-Hosted Runnern aktivieren manche Teams ihn für Docker-in-Docker-Builds (dind), also für Jobs, die selbst Docker-Images bauen. Für Magento-Build-Jobs ist Docker-in-Docker nicht notwendig und sollte deaktiviert bleiben.
Netzwerkisolation schützt davor, dass ein kompromittierter Job auf interne Dienste im Netzwerk des Runner-Hosts zugreift. Ohne Isolation könnte ein Job, der auf dem Build-Runner läuft, Verbindungen zu internen Diensten wie Redis, RabbitMQ oder Datenbank-Servern aufbauen, die im selben Netzwerksegment liegen. Mit einem isolierten Docker-Netzwerk (network_mode = "runner-net") hat der Container nur Zugang zu explizit erlaubten Zielen — zum Beispiel GitLab selbst für Artifact-Uploads und Packagist für Composer.
8. Direkter Sicherheitsvergleich
Der folgende Vergleich fasst die Sicherheitseigenschaften der drei Runner-Typen für den typischen Magento-Deployment-Kontext zusammen. Die Bewertungen beziehen sich auf den Einsatz mit Production-SSH-Keys und Datenbank-Credentials.
| Eigenschaft | Shared Runner | Group/Project Runner | Self-Hosted Runner |
|---|---|---|---|
| Job-Isolation | Container pro Job | Container pro Job | Vollständig kontrolliert |
| Wer hat Zugriff? | Alle Projekte der Instanz | Nur eigene Gruppe / Projekt | Nur eigene Pipelines |
| Netzwerkkontrolle | Keine eigene Kontrolle | Keine eigene Kontrolle | Vollständig konfigurierbar |
| Production-SSH-Keys | Nicht empfehlenswert | Bedingt geeignet | Empfohlen |
| Betriebsaufwand | Kein eigener Aufwand | Eigene Runner-Instanz | Host, OS, Monitoring, Updates |
Die Empfehlung für die meisten Magento-Teams lautet: Build-Jobs (ohne Production-Secrets) können auf GitLab.com Shared Runnern oder auf einem Group Runner laufen. Deploy-Jobs (mit Production-SSH-Key) müssen auf einem Self-Hosted Runner laufen, der auf einem eigenen Host mit strikten Zugriffskontrollen betrieben wird. Diese Aufteilung kombiniert die Bequemlichkeit von Shared Runnern für unkritische Builds mit der Sicherheit von Self-Hosted Runnern für kritische Deployments.
9. Typische Sicherheitsfehler bei der Runner-Konfiguration
Der häufigste Fehler ist das Speichern von Production-SSH-Keys als ungeschützte Variablen (ohne Protected-Flag). Ungeschützte Variablen sind in jedem Job sichtbar, auch in Jobs auf Feature-Branches und in Merge-Request-Pipelines. Auf einem Shared Runner bedeutet das, dass der Key — wenn auch nur vorübergehend — in einer Container-Umgebung existiert, die nicht vollständig unter eigener Kontrolle ist. Lösung: SSH-Keys immer als Protected und Masked Variablen anlegen, und nur auf Protected Branches deployen.
Ein zweiter schwerwiegender Fehler ist das Aktivieren von privileged = true auf Build-Runnern, die keine Docker-in-Docker-Builds benötigen. Teams aktivieren Privileged Mode oft, weil ein Job es verlangt, ohne die Konsequenzen zu verstehen. Im Privileged Mode kann ein Job auf den Docker-Socket des Hosts zugreifen (/var/run/docker.sock), alle Container des Hosts inspecten und beliebige Container mit Host-Netzwerkzugang starten — das ist ein vollständiger Container-Escape. Ein dritter Fehler: Runner ohne Netzwerklimit in einem Netzwerksegment mit Datenbank-Servern registrieren. Selbst ohne Privileged Mode kann ein Job über das Netzwerk interne Dienste erreichen, wenn kein Netzwerk-Isolation-Konzept umgesetzt wurde.
10. Zusammenfassung
Die Wahl des richtigen Runner-Typs ist für Magento-Teams eine sicherheitskritische Entscheidung, nicht nur eine infrastrukturelle. Shared Runner bieten null Aufwand, aber minimale Kontrolle über Isolation und Netzwerkzugang. Self-Hosted Runner bieten maximale Kontrolle, aber erfordern eigenen Betriebsaufwand. Die pragmatische Lösung ist eine Aufteilung: unkritische Build-Jobs auf Shared oder Group Runnern, Production-Deployments ausschließlich auf Self-Hosted Runnern mit strenger Netzwerk- und Privileged-Konfiguration.
Drei Regeln, die keine Ausnahmen dulden: Production-SSH-Keys niemals auf Shared Runnern exponieren. Privileged Mode nur aktivieren, wenn Docker-in-Docker wirklich benötigt wird. Alle Variablen mit Production-Zugang als Protected und Masked anlegen, damit sie nur auf Protected Branches sichtbar sind. Mit diesen Grundregeln lässt sich ein GitLab-CI/CD-Setup aufbauen, das auch einer Sicherheitsüberprüfung standhält.
Runner-Security — Das Wichtigste auf einen Blick
Shared Runner
Kein eigener Aufwand, aber keine Kontrolle über Isolation und Netzwerk. Nicht geeignet für Production-SSH-Keys und Datenbank-Credentials.
Self-Hosted Deploy-Runner
Eigene Hardware, eigene Netzwerkkontrolle, privileged = false, cap_drop ALL. Pflicht für Jobs mit Production-SSH-Zugang.
Protected Variablen
Production-Secrets immer Protected und Masked. Sichtbarkeit auf Protected Branches beschränken — unabhängig vom Runner-Typ.
Netzwerk-Isolation
Docker-Netzwerk für Runner-Container isolieren. Kein Zugang zu internen Diensten ohne explizite Konfiguration.
11. FAQ: Runner-Security in GitLab
1Sind Shared Runner auf GitLab.com sicher?
2Protected vs. Masked Variablen?
3Wann ist privileged = true gerechtfertigt?
4Group Runner sicher für Production-Deploys?
5run_untagged = false — Zweck?
6Docker-Socket auf Runner-Hosts schützen?
7Warum ist Netzwerkisolation wichtig?
8Forks und Secrets?
9Linux-Capabilities im Container begrenzen?
10SSH-Keys in GitLab CI/CD?
Entscheidungshilfe: Welcher Runner-Typ für welchen Job?
Für Production-Deployments: eigener, gesperrter Runner mit privileged = false. Für Code-Qualitätschecks (PHPStan, PHPCS): Shared Runner ausreichend. Für Artifacts mit Vendor-Cache: Group Runner mit persistentem Cache-Volume. Für Integrationstests mit echter DB: Self-Hosted Runner mit Docker-Isolation.
Runner-Sicherheit ist kein Einmal-Setup — sie muss mit der Infrastruktur mitwachsen. Neue Projekte, neue Teammitglieder und neue Deployment-Umgebungen erfordern regelmäßige Überprüfung der Runner-Konfiguration und Zugriffsrechte.