GitLab CI/CD Variables wirklich verstehen
Die drei Häkchen beim Anlegen einer GitLab-Variable sehen harmlos aus – aber welche Kombination für welche Variable gilt, bestimmt über die Sicherheit des gesamten Deployment-Prozesses. Protected, Masked und Environment Scope bedeuten drei verschiedene Dinge, die oft verwechselt werden.
Inhaltsverzeichnis
- 1. Protected, Masked und Scope: drei verschiedene Konzepte
- 2. Protected Variables: nur auf Protected Branches
- 3. Masked Variables: kein Klartext im Job-Log
- 4. Environment Scopes: variable pro Umgebung
- 5. File-Type Variables: SSH-Keys und .env-Dateien
- 6. Group- vs. Project-Variables: Sichtbarkeit steuern
- 7. Magento-Deployment-Variables in der Praxis
- 8. Typische Konfigurationsfehler
- 9. Variable-Typen im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Protected, Masked und Scope: drei verschiedene Konzepte
Der häufigste Irrtum beim Umgang mit GitLab CI/CD Variables: dass Protected und Masked dasselbe bedeuten. Das tun sie nicht. Protected steuert, auf welchen Branches und Tags eine Variable verfügbar ist. Masked steuert, ob der Wert der Variable im Job-Log angezeigt oder unkenntlich gemacht wird. Environment Scope steuert, in welchen Deployment-Umgebungen eine Variable injiziert wird. Diese drei Dimensionen sind unabhängig voneinander konfigurierbar und ergeben zusammen das Sicherheitsmodell.
Ein konkretes Beispiel: Ein SSH-Private-Key für Production sollte Protected (nur auf main und Tags verfügbar), Masked (kein Klartext im Log) und scoped auf die production-Umgebung sein. Fehlt eine dieser drei Eigenschaften, entsteht eine Lücke: ohne Protected landet der Key in Feature-Branch-Pipelines, ohne Masked ist er im Job-Log lesbar, ohne Scope könnte er auf Staging-Jobs wirken, wo er nicht benötigt wird.
Das Verständnis dieser drei Konzepte ist die Grundlage für einen sicheren GitLab-Deployment-Prozess. Wer sie verwechselt, konfiguriert Variables entweder zu offen (Sicherheitsrisiko) oder zu restriktiv (Pipeline-Jobs schlagen fehl, weil Variables nicht verfügbar sind). Beide Fehler sind in der Praxis häufig und leicht vermeidbar.
2. Protected Variables: nur auf Protected Branches
Eine Protected Variable ist ausschließlich in Jobs verfügbar, die auf Protected Branches oder Protected Tags laufen. In GitLab werden Branches als Protected markiert unter Settings → Repository → Protected Branches. Die typische Konfiguration für Magento: main und release/* als Protected Branches, v* als Protected Tags. Eine Variable mit Protected-Flag ist auf Feature-Branches nicht verfügbar – das verhindert, dass ein Entwickler mit einem Feature-Branch-Pipeline-Lauf auf Production-Credentials zugreift.
Der wichtigste Anwendungsfall: Production-SSH-Keys, Production-Datenbankpasswörter und Production-API-Keys werden als Protected Variables angelegt. Sie sind damit auf Feature-Branches unsichtbar, auch wenn jemand versucht, sie über echo $VARIABLE in einem Job auszulesen. Nur Jobs auf main oder Tags sehen diese Variables. Diese Einschränkung ist keine Bequemlichkeit, sondern ein aktiver Schutz gegen Credential-Leakage durch böswilligen oder versehentlichen Code in Merge Requests.
# Variables configuration — protection levels for Magento deployment
variables:
# Non-sensitive: available on all branches, no masking needed
DEPLOY_PATH: /var/www/magento
RELEASE_RETENTION: "5"
MAGENTO_LOCALE: de_DE
# Protected + Masked + Production-scoped variables (set in GitLab UI):
# SSH_PRIVATE_KEY — Protected: yes, Masked: yes, Scope: production
# COMPOSER_AUTH — Protected: yes, Masked: yes, Scope: *
# DB_PASSWORD_PROD — Protected: yes, Masked: yes, Scope: production
# DB_PASSWORD_STAGING — Protected: no, Masked: yes, Scope: staging
# Usage in deploy job — SSH_PRIVATE_KEY only injected on protected branches
deploy:production:
stage: deploy
environment:
name: production
rules:
# Only runs on protected tags — Protected Variable will be injected
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
before_script:
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | ssh-add -
script:
- echo "Deploying to production..."
3. Masked Variables: kein Klartext im Job-Log
Masked Variables werden im Job-Log mit [MASKED] ersetzt, wenn ihr Wert in der Ausgabe erscheinen würde. Das schützt vor versehentlicher Anzeige von Secrets in Pipeline-Logs, die von anderen Teammitgliedern eingesehen werden können. Wichtig zu verstehen: Masking ist kein vollständiger Schutz – wenn der Secret-Wert in einem anderen Format (z.B. Base64-kodiert oder aufgeteilt) im Log erscheint, wird er nicht maskiert. Masking ist eine Sicherheitsebene, keine absolute Garantie.
Es gibt technische Einschränkungen für Masked Variables: der Wert muss mindestens 8 Zeichen lang sein und darf keine Newlines enthalten. SSH-Private-Keys können daher nicht direkt als Masked Variables angelegt werden – sie enthalten Newlines. Der Workaround: den Key Base64-kodieren, als Masked Variable speichern und im Job dekodieren. Das ist in der Praxis das üblichste Muster für SSH-Keys in GitLab-Pipelines.
4. Environment Scopes: Variable pro Umgebung
Environment Scopes sind das mächtigste und am häufigsten falsch verstandene Feature von GitLab CI/CD Variables. Ein Scope bestimmt, in welchen Environments eine Variable injiziert wird. Der Wildcard-Scope * bedeutet: Variable ist in allen Environments verfügbar. Ein spezifischer Scope wie production bedeutet: Variable wird nur injiziert, wenn der Job in einem Environment mit dem Namen production läuft.
Für Magento-Deployments bedeutet das: DEPLOY_HOST kann zweimal angelegt werden – einmal mit Wert staging.example.com und Scope staging, einmal mit Wert production.example.com und Scope production. Der Deploy-Job bekommt automatisch den richtigen Wert, je nachdem in welchem Environment er läuft. Das eliminiert bedingte Logik in der Pipeline-Konfiguration und stellt sicher, dass Staging-Jobs niemals Production-Hosts verwenden können.
# Environment-scoped variables allow the same job definition for both environments
# Variables configured in GitLab UI with scopes:
# DEPLOY_HOST → staging.example.com (scope: staging)
# DEPLOY_HOST → production.example.com (scope: production)
# DEPLOY_USER → deploy (scope: *)
# REDIS_HOST → 10.0.1.10 (scope: staging)
# REDIS_HOST → 10.0.2.10 (scope: production)
# Single job definition — correct DEPLOY_HOST injected automatically per environment
.deploy-template: &deploy-template
stage: deploy
before_script:
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | ssh-add -
script:
- echo "Deploying to ${DEPLOY_HOST} as ${DEPLOY_USER}"
- rsync -az ./ "${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/releases/${RELEASE_ID}/"
deploy:staging:
<<: *deploy-template
environment:
name: staging
url: https://staging.example.com
rules:
- if: '$CI_COMMIT_BRANCH == "develop"'
deploy:production:
<<: *deploy-template
environment:
name: production
url: https://shop.example.com
rules:
- if: '$CI_COMMIT_TAG =~ /^v\d+/'
when: manual
5. File-Type Variables: SSH-Keys und .env-Dateien
File-Type Variables in GitLab CI/CD speichern ihren Inhalt nicht als Umgebungsvariable, sondern als temporäre Datei, deren Pfad als Variable injiziert wird. Das ist die korrekte Methode für SSH-Private-Keys, die Newlines enthalten und daher nicht als normale Variables verwendet werden können. Ein SSH-Key als File-Type Variable angelegt: GitLab schreibt den Key in eine temporäre Datei und setzt die Variable auf den Dateipfad, z.B. /tmp/gitlab-ci-key-XXXX.
Der Vorteil gegenüber dem Base64-Workaround: kein Encoding und Decoding im Job-Script, kein Risiko, dass der dekodierte Inhalt im Log erscheint. Die Verwendung im Job ist direkter: ssh-add "$SSH_PRIVATE_KEY" – wobei $SSH_PRIVATE_KEY den Pfad zur temporären Datei enthält. Diese temporäre Datei wird nach dem Job automatisch gelöscht. File-Type Variables sind auch für composer auth.json und andere Konfigurationsdateien mit strukturierten Inhalten geeignet.
6. Group- vs. Project-Variables: Sichtbarkeit steuern
GitLab ermöglicht die Verwaltung von Variables auf drei Ebenen: Instance, Group und Project. Group-Variables werden an alle Projekte innerhalb der Gruppe vererbt, Project-Variables überschreiben Group-Variables mit demselben Namen. Für Magento-Agenturen mit mehreren Projekten ist das Group-Level der richtige Ort für gemeinsame Secrets wie Composer-Auth für private Pakete oder allgemeine Build-Tools-Credentials.
Project-spezifische Secrets wie Production-SSH-Keys, Datenbankpasswörter und API-Keys gehören ausschließlich auf Project-Level. Das verhindert, dass ein Projekt versehentlich auf Credentials eines anderen Projekts zugreift – auch wenn beide in derselben Gruppe sind. Die Hierarchie: Project-Variable überschreibt Group-Variable, Group-Variable überschreibt Instance-Variable. Bei Namenskonflikten gewinnt immer der spezifischere Level.
7. Magento-Deployment-Variables in der Praxis
Für ein typisches Magento-Deployment mit Staging und Production ergibt sich folgendes Variables-Schema: Nicht-sensible Werte wie Pfade und Locale-Settings werden direkt in der .gitlab-ci.yml als variables: definiert – sie sind transparent und versioniert. Semi-sensible Werte wie Hostnamen und Deploy-User werden in GitLab als nicht-Protected, nicht-Masked, mit Environment-Scope angelegt. Hochsensible Werte wie SSH-Keys, Datenbankpasswörter und API-Keys werden als Protected, Masked und environment-scoped angelegt.
Diese Dreiteilung hat einen praktischen Vorteil: die Pipeline-Konfiguration in der .gitlab-ci.yml bleibt lesbar und nachvollziehbar, weil alle nicht-sensiblen Werte dort stehen. Die GitLab-UI enthält nur die Secrets. Neue Entwickler können die Pipeline-Logik verstehen, ohne Zugriff auf GitLab-Settings zu benötigen. Und Änderungen an nicht-sensiblen Werten können über Merge Requests nachverfolgt werden.
8. Typische Konfigurationsfehler
Der häufigste Fehler: Production-Credentials werden ohne Protected-Flag angelegt. Sie sind damit in Feature-Branch-Pipelines verfügbar und könnten theoretisch über einen Merge Request mit einer echo $VARIABLE-Zeile im Script ausgelesen werden. Der zweithäufigste Fehler: Environment Scopes werden nicht gesetzt, weil es auf den ersten Blick einfacher erscheint, eine Variable global anzulegen. Das führt dazu, dass Staging-Jobs mit Production-Hosts oder -Credentials arbeiten, wenn die Variable denselben Namen hat und nur der Wert unterschiedlich sein sollte.
Ein dritter, subtilerer Fehler: SSH-Keys werden als normale (nicht File-Type) Variable angelegt und dann in Deployment-Scripts mit echo "$SSH_KEY" | ssh-add - verwendet. Das funktioniert in vielen Fällen, hat aber zwei Probleme: Der Key-Inhalt erscheint möglicherweise im Job-Log (wenn Masking fehlschlägt, weil der Wert zu lang oder aufgeteilt ist), und der Key wird direkt in die Prozessumgebung expandiert, was bei bestimmten Shell-Konfigurationen zu Problemen mit Whitespace-Handling führen kann. File-Type Variables vermeiden beide Probleme.
9. Variable-Typen im Vergleich
Die Auswahl der richtigen Variable-Konfiguration hängt von der Sensitivität und dem Verwendungskontext ab. Die folgende Tabelle zeigt die empfohlene Konfiguration für typische Magento-Deployment-Variables.
| Variable | Protected | Masked | Scope | Typ |
|---|---|---|---|---|
| SSH_PRIVATE_KEY (prod) | Ja | n/a | production | File |
| COMPOSER_AUTH | Ja | Ja | * | Variable |
| DEPLOY_HOST | Nein | Nein | staging / production | Variable |
| DB_PASSWORD | Ja | Ja | staging / production | Variable |
| DEPLOY_PATH | Nein | Nein | * | In .gitlab-ci.yml |
Die Tabelle zeigt das Prinzip: Je sensibler eine Variable, desto restriktiver die Konfiguration. Nicht-sensible Werte gehören direkt in die .gitlab-ci.yml – sie sind transparent, versioniert und müssen nicht über die GitLab-UI verwaltet werden. Hochsensible Secrets werden Protected, Masked und environment-scoped – sie sind in keinem Log sichtbar und auf Feature-Branches nicht zugänglich.
10. Zusammenfassung
Protected, Masked und Environment Scopes sind drei unabhängige Sicherheitsdimensionen, die zusammen das Variablen-Sicherheitsmodell einer GitLab-Pipeline bestimmen. Protected schränkt auf Protected Branches ein, Masked verhindert Log-Ausgabe und Scope trennt Umgebungen. Wer alle drei korrekt konfiguriert, stellt sicher, dass Staging-Credentials nicht in Production-Jobs landen und Production-Secrets nicht in Feature-Branch-Logs erscheinen.
Die praktische Empfehlung: Nicht-sensible Werte direkt in der .gitlab-ci.yml als variables: definieren. Umgebungsspezifische Hostnamen und User ohne Protected/Masked, aber mit Scope. Alle Secrets als Protected + Masked + environment-scoped. SSH-Keys als File-Type Variables. Diese Systematik macht die Pipeline-Konfiguration nachvollziehbar, die Secrets sicher und die Umgebungstrennung verlässlich.
GitLab Variables richtig konfigurieren — Das Wichtigste auf einen Blick
Protected
Variable nur auf Protected Branches und Tags verfügbar. Verhindert Zugriff auf Production-Credentials in Feature-Branch-Pipelines.
Masked
Wert wird im Job-Log mit [MASKED] ersetzt. Kein absoluter Schutz, aber verhindert versehentliche Klartext-Anzeige in Pipeline-Logs.
Environment Scope
Variable wird nur in Jobs injiziert, die im definierten Environment laufen. Ermöglicht gleiche Variablennamen mit verschiedenen Werten pro Umgebung.
File-Type
Inhalt wird als temporäre Datei gespeichert. Pflicht für SSH-Keys mit Newlines. Verhindert Whitespace-Probleme und unbeabsichtigte Log-Ausgabe.