in GitLab sicher und reproduzierbar verwalten
Wer Composer-Credentials im Repository ablegt, riskiert Kompromittierung bei jedem Offboarding. Wer sie gar nicht verwaltet, kämpft mit fehlerhaften Builds. Dieser Artikel zeigt, wie auth.json, private Repository-Tokens und Packagist-Mirror-Konfigurationen sicher als GitLab Variables hinterlegt und in Pipelines korrekt konsumiert werden.
Inhaltsverzeichnis
- 1. Das Problem mit Composer-Credentials in CI
- 2. auth.json – Format, Inhalte und Grenzen
- 3. COMPOSER_AUTH als GitLab Variable anlegen
- 4. Magento Marketplace: Public Key und Access Key
- 5. Private Repositories: GitLab, GitHub und Satis
- 6. Composer Mirror und Private Packagist konfigurieren
- 7. Composer Auth in der Pipeline korrekt konsumieren
- 8. Vergleich: unsicherer vs. sicherer Composer-Setup
- 9. Typische Fehlerbilder bei Composer Auth in CI
- 10. Zusammenfassung
- 11. FAQ
1. Das Problem mit Composer-Credentials in CI
Composer-Credentials – also Zugangsdaten für den Magento Marketplace, kommerzielle Erweiterungen und private Paketquellen – werden in vielen Projekten entweder im Repository als auth.json eingecheckt oder auf dem Build-Server manuell hinterlegt. Beides ist problematisch: Eingecheckte Credentials erscheinen in der Git-History und sind bei jedem Clone des Repositories zugänglich – auch für Entwickler, die längst das Team verlassen haben. Manuell hinterlegte Credentials auf Build-Servern sind unsichtbar, werden selten rotiert und hängen an der Lebensdauer des Servers.
GitLab CI/CD Variables lösen dieses Problem strukturell: Die Credentials liegen ausschließlich in GitLab, nicht im Repository und nicht auf dem Server. Sie können pro Umgebung gescopedt, per Protected-Flag auf authorized Branches beschränkt und jederzeit rotiert werden, ohne den Code oder die Server-Konfiguration zu berühren. Wer diesen Weg konsequent geht, hat einen reproduzierbaren Build-Prozess, bei dem jeder Entwickler dieselbe Pipeline ausführen kann, ohne lokale Credentials kennen zu müssen – und bei dem Offboarding keine manuelle Credentials-Rotation erfordert.
2. auth.json – Format, Inhalte und Grenzen
Die auth.json-Datei ist Composers primäre Methode, Authentifizierungs-Credentials zu speichern. Sie unterstützt drei Typen: http-basic für Username/Password-Authentifizierung (wie beim Magento Marketplace), bearer für Token-basierte Authentifizierung (wie bei GitLab Personal Access Tokens oder GitHub Tokens) und gitlab-token als spezieller Alias für GitLab-Token-Auth. Die Datei hat eine klare JSON-Struktur mit Host-Namen als Keys – Composer findet automatisch die passenden Credentials für eine gegebene Paketquelle.
Die Grenzen der auth.json: Sie ist eine lokale Datei, die pro Entwickler-Rechner oder CI-Runner hinterlegt werden muss. In CI-Umgebungen bedeutet das, dass die Datei entweder vorab auf dem Runner hinterlegt ist (schlechte Praxis, weil nicht versionierbar) oder in jedem Job-Script aus einer Variable erzeugt wird. Composer kennt dafür aber einen eleganteren Weg: Die Umgebungsvariable COMPOSER_AUTH enthält denselben JSON-Inhalt wie die Datei – Composer liest beide mit identischer Priorität. Das ist der empfohlene Weg für GitLab-Pipelines, weil die Variable direkt aus GitLab übergeben wird, ohne Datei-Schreibschritte im Script.
# auth.json format — used as COMPOSER_AUTH variable value in GitLab
# Set this entire JSON as the value of the COMPOSER_AUTH CI/CD Variable
{
"http-basic": {
"repo.magento.com": {
"username": "public-key-from-marketplace",
"password": "private-key-from-marketplace"
},
"hyva-themes.com": {
"username": "token",
"password": "your-hyva-license-key"
}
},
"bearer": {
"gitlab.example.com": "glpat-your-personal-access-token"
}
}
3. COMPOSER_AUTH als GitLab Variable anlegen
Die Variable wird in GitLab unter Settings → CI/CD → Variables angelegt. Als Wert kommt der vollständige JSON-Inhalt der auth.json rein – ohne Dateiname, nur der JSON-String. Wichtige Konfiguration: Das Masked-Flag setzen, damit der JSON-Inhalt in Job-Logs nicht im Klartext erscheint. Das Protected-Flag setzen, wenn der Value nur in Production-Pipelines benötigt wird. Der Environment Scope sollte so eng wie möglich sein: Wenn Staging andere Composer-Credentials braucht als Production, sind zwei separate Variablen mit demselben Namen aber unterschiedlichem Scope die saubere Lösung.
Ein häufiger Fehler beim Anlegen: Das JSON enthält einen Zeilenumbruch-Fehler oder eine falsche Quotierung, was dazu führt, dass Composer die Variable ignoriert oder mit einem JSON-Parse-Fehler abbricht. Vor dem Anlegen der Variable empfiehlt es sich, den JSON-Inhalt lokal mit jq . auth.json zu validieren. Ein weiterer Fallstrick: Bei sehr langen Composer-Auth-Strings (viele Pakete, viele Credentials) kann die Masked-Funktion scheitern, weil GitLab eine Längenbeschränkung für Masked Variables hat. In diesem Fall muss auf das Protected-Flag allein vertraut werden, oder die Credentials werden auf mehrere Variables aufgeteilt.
4. Magento Marketplace: Public Key und Access Key
Der Magento Marketplace nutzt http-basic-Authentifizierung mit zwei unterschiedlichen Schlüsseln: Der Public Key entspricht dem Username, der Access Key dem Passwort. Beide werden im Marketplace-Account unter "Access Keys" generiert. Der Public Key kann vergleichsweise frei geteilt werden – er identifiziert den Account, enthält aber kein Geheimnis. Der Access Key ist das eigentliche Secret und muss als Masked und Protected Variable hinterlegt werden.
Für Produktionspipelines empfiehlt sich, dedizierte Marketplace-Keys für CI anzulegen, getrennt von den persönlichen Entwickler-Keys. So kann der CI-Key jederzeit rotiert oder gesperrt werden, ohne den Entwicklungs-Workflow zu unterbrechen. Ein weiterer Vorteil: Die Marketplace-API begrenzt Requests pro Key. Bei parallelen Builds auf mehreren Branches kann es zu Rate-Limit-Fehlern kommen, wenn alle Builds denselben persönlichen Key verwenden. Dedizierte CI-Keys haben höhere Limits und sind klarer dem automatisierten Build-Prozess zugeordnet.
5. Private Repositories: GitLab, GitHub und Satis
Private Composer-Pakete kommen in Magento-Projekten häufig von drei Quellen: einem eigenen GitLab-Repository (Custom-Modul), einem kommerziellen Anbieter mit privatem GitHub-Repository oder einem selbst gehosteten Satis-Server. Für alle drei Quellen wird die Authentifizierung über die COMPOSER_AUTH-Variable konfiguriert. GitLab-Repositories verwenden entweder einen Personal Access Token mit read_api-Scope oder einen Deploy Token, der ausschließlich Lesezugriff auf das Repository hat. Deploy Tokens sind die sicherere Wahl, weil sie nicht an einen Benutzer-Account gebunden sind und beim Offboarding nicht ungültig werden.
Für Satis-Server, die intern Composer-Pakete hosten, gelten dieselben Prinzipien: Basic-Auth mit dediziertem CI-Benutzer, Token in der COMPOSER_AUTH-Variable, kein Hardcoding im composer.json. Wichtig: Die Repository-URLs in composer.json dürfen keine eingebetteten Credentials enthalten (also kein https://user:password@satis.intern/). Die Credentials kommen ausschließlich aus der Variable. Das ermöglicht es, composer.json problemlos einzuchecken, ohne Credentials zu exponieren.
# Build job — consuming COMPOSER_AUTH from GitLab CI/CD Variable
build:composer:
stage: build
image: php:8.4-cli
variables:
# COMPOSER_CACHE_DIR enables GitLab cache for vendor packages
COMPOSER_CACHE_DIR: ".cache/composer"
# COMPOSER_AUTH is injected automatically from GitLab Variable
# No need to write auth.json manually
cache:
key: "composer-$CI_COMMIT_REF_SLUG"
paths:
- .cache/composer/
policy: pull-push
before_script:
- apt-get update -qq && apt-get install -y -qq git unzip
- php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
- php composer-setup.php --quiet
script:
# Composer automatically reads COMPOSER_AUTH environment variable
- php composer.phar install --no-dev --prefer-dist --no-interaction --no-ansi
- php composer.phar dump-autoload --optimize --no-dev
# Verify no auth.json was accidentally written
- test ! -f auth.json || { echo "ERROR: auth.json must not exist"; exit 1; }
artifacts:
paths:
- vendor/
expire_in: 2 hours
6. Composer Mirror und Private Packagist konfigurieren
Composer-Mirror und Private Packagist werden in Projekten eingesetzt, um Abhängigkeiten von der öffentlichen Packagist-Infrastruktur zu entkoppeln, Download-Zeiten zu reduzieren und Pakete intern zu cachen. Die Konfiguration eines Mirrors erfolgt über composer.json im repositories-Block oder über composer config-Befehle im Build-Script. Private Packagist spezifisch erfordert einen Token, der über die COMPOSER_AUTH-Variable übergeben wird – im selben JSON-Format wie bei anderen Bearer-Token-Quellen.
Wichtig bei Mirror-Konfigurationen: Der Mirror muss im Build-Job erreichbar sein. Wenn der GitLab-Runner in einem internen Netz läuft und der Mirror intern gehostet wird, ist das kein Problem. Wenn der Runner in der Cloud läuft und der Mirror intern ist, braucht es VPN oder einen externen Mirror. Die Konfiguration des Mirrors im composer.json selbst ist unproblematisch – sie enthält keine Credentials. Die Authentifizierung am Mirror kommt aus der Variable. Dieser Trennung zwischen Konfiguration (im Repository) und Credentials (in GitLab) konsequent zu folgen ist das zentrale Prinzip einer sicheren Composer-Setup.
7. Composer Auth in der Pipeline korrekt konsumieren
In der Pipeline gilt: Composer liest COMPOSER_AUTH automatisch – kein explizites Übergeben als Argument nötig. Das funktioniert, weil Composer standardmäßig die Umgebungsvariable COMPOSER_AUTH prüft und als Auth-Konfiguration behandelt, bevor es die lokale auth.json liest. Im .gitlab-ci.yml muss die Variable nur im richtigen Scope sichtbar sein; der Build-Job liest sie ohne weiteres Zutun. Ein häufiger Debugging-Schritt: Im Job composer config --list ausführen (nur in Debug-Jobs, nie in Production) – die Ausgabe zeigt, ob die http-basic-Credentials für repo.magento.com oder andere Quellen korrekt gesetzt sind.
Caching der vendor/-Verzeichnisse in Kombination mit korrekter Composer-Auth ergibt die beste Build-Performance. Der Cache-Key sollte composer.lock als Input einschließen, damit bei einer Änderung der Lock-Datei automatisch ein neuer Cache-Eintrag erzeugt wird. Wer den Vendor-Ordner cacht, muss aber sicherstellen, dass die gecachten Packages zum aktuellen Lock-File passen – ein Mismatch führt zu schwer debugbaren Fehler, weil Composer den Cache blind verwendet. Das Flag --no-cache in einen Test-Job einzubauen, der periodisch einen sauberen Build ohne Cache durchführt, ist eine gute Praxis.
8. Vergleich: unsicherer vs. sicherer Composer-Setup
Der Unterschied zwischen einem improvisierten und einem sicheren Composer-Setup in GitLab ist messbar – in Audit-Risiko, Rotationsaufwand und Build-Reproduzierbarkeit.
| Aspekt | Unsicher | Korrekt | Auswirkung |
|---|---|---|---|
| Credential-Ort | auth.json im Repository | COMPOSER_AUTH Variable | Kein Credential-Abfluss über Git-History |
| Key-Typ | Persönlicher Entwickler-Key | Dedizierter CI-Key | Rotierbar ohne Entwickler-Workflow-Unterbrechung |
| Scope | Variable Scope * (alle Branches) | Scope production / staging | Feature-Branches sehen keine Production-Keys |
| Mirror URL | https://user:pw@mirror/ in composer.json | URL in composer.json, Auth in Variable | composer.json sicher eincheckbar |
| Rotation | Datei-Update auf jedem Server | Variable in GitLab aktualisieren | Rotation ohne Server-Zugriff möglich |
Die konsequente Trennung von Konfiguration (im Repository) und Credentials (in GitLab Variables) ist das Grundprinzip, das alle Zeilen der Tabelle verbindet. Wer es einmal sauber aufgebaut hat, zahlt beim Onboarding neuer Entwickler, beim Offboarding alter Teammitglieder und beim Rotieren von Credentials jeweils deutlich weniger operativen Aufwand als Teams, die Credentials verteilt auf Servern und Repositories verwalten.
9. Typische Fehlerbilder bei Composer Auth in CI
Das häufigste Fehlerbild ist Could not authenticate against repo.magento.com. Ursachen: Die COMPOSER_AUTH-Variable ist nicht gesetzt (Scope-Problem), das JSON-Format ist ungültig (fehlende Anführungszeichen, Komma-Fehler), oder der Access Key ist abgelaufen. Diagnose: Im Build-Job echo $COMPOSER_AUTH | php -r "echo json_last_error() ? 'invalid JSON' : 'valid JSON';" ausführen, um das Format zu prüfen. Dann im Marketplace-Account prüfen, ob der Access Key noch aktiv ist.
Das zweite Fehlerbild ist Package not found bei privaten Repositories. Ursache: Der Token hat nicht die nötigen Scopes, oder die Repository-URL in composer.json stimmt nicht mit dem Credential-Host in der Variable überein. Diagnose: Den Token in der GitLab-Variable mit einem minimalen Read-Scope für das spezifische Repository anlegen und im Build-Job mit composer why-not prüfen, ob die Paketquelle überhaupt gefunden wird. Das dritte Fehlerbild ist ein Rate-Limit-Fehler vom Magento Marketplace – erkennbar an HTTP 429-Responses im Job-Log. Lösung: Composer-Cache in GitLab korrekt konfigurieren, damit dieselben Pakete nicht bei jedem Build neu heruntergeladen werden.
# Validate COMPOSER_AUTH and test connectivity in a debug job
debug:composer-auth:
stage: build
image: php:8.4-cli
environment: staging
script:
# Validate JSON format of COMPOSER_AUTH without printing the value
- |
php -r "
\$auth = getenv('COMPOSER_AUTH');
if (!\$auth) { echo 'ERROR: COMPOSER_AUTH not set'; exit(1); }
json_decode(\$auth);
if (json_last_error() !== JSON_ERROR_NONE) {
echo 'ERROR: Invalid JSON in COMPOSER_AUTH: ' . json_last_error_msg();
exit(1);
}
echo 'COMPOSER_AUTH: valid JSON, length=' . strlen(\$auth) . PHP_EOL;
"
# Test connectivity to Magento Marketplace (no download, just HEAD request)
- curl -sf -o /dev/null -w "HTTP %{http_code}" https://repo.magento.com/ || true
when: manual
allow_failure: true
10. Zusammenfassung
Composer Auth, private Repositories und Mirror-Konfigurationen in GitLab sicher zu verwalten ist das Fundament eines reproduzierbaren Magento-Builds in CI. COMPOSER_AUTH als Masked Variable mit dem JSON-Inhalt der auth.json – kein auth.json im Repository, kein manuelles Credential-Management auf Servern. Dedizierte CI-Keys statt persönlicher Entwickler-Keys für den Magento Marketplace. Environment Scope für die Variable, damit Feature-Branches keine Production-Credentials sehen. Composer-Cache in GitLab, damit Pakete nicht bei jedem Build neu heruntergeladen werden.
Das Ziel ist ein Build-Prozess, bei dem jeder Entwickler dieselbe Pipeline ausführen kann, ohne lokale Credentials zu kennen – und bei dem das Rotieren eines Marketplace-Keys oder eines Repository-Tokens eine Änderung in GitLab ist, nicht ein Koordinationsprojekt über drei Server und sechs Entwickler-Rechner. Diese Entkopplung ist es, die Composer Auth in GitLab vom pragmatischen Workaround zum dauerhaften Standard macht.
Composer Auth in GitLab — Das Wichtigste auf einen Blick
Credential-Ablage
COMPOSER_AUTH als Masked Variable – nie auth.json im Repository. JSON-Format vorab mit jq validieren.
Key-Management
Dedizierte CI-Keys für Magento Marketplace, getrennt von persönlichen Entwickler-Keys. Deploy Tokens für GitLab-interne private Repos.
Scope & Rotation
Environment Scope eng definieren. Rotation erfolgt nur in GitLab – keine Server-Updates, kein Code-Change.
Mirror & Cache
Mirror-URLs in composer.json, Credentials in Variable. Composer-Cache in GitLab konfigurieren, Rate-Limits vermeiden.