Skip to content

GitLab CI Deployment mit Vault SSH Secret Engine und Rsync

Diese Anleitung beschreibt, wie man in GitLab CI/CD Pipelines das Hashicorp Vault SSH Secret Engine nutzt, um sichere SSH-Verbindungen für Deployments via Rsync herzustellen.

Voraussetzungen

  • Hashicorp Vault mit aktiviertem SSH Secret Engine
  • GitLab mit JWT-Authentifizierung für Vault konfiguriert (siehe gitlab-jwt.md)
  • Ziel-Server mit SSH-Zugang
  • GitLab Runner mit vault und rsync Binary

Vault Konfiguration

SSH Secret Engine aktivieren

bash
vault secrets enable -path=ssh ssh

SSH CA konfigurieren

Generiere ein CA-Schlüsselpaar für das Signieren von SSH-Zertifikaten:

bash
vault write ssh/config/ca generate_signing_key=true

SSH-Rolle erstellen

Erstelle eine Rolle für GitLab CI Deployments:

bash
vault write ssh/roles/gitlab-deployer \
  key_type=ca \
  ttl=10m \
  max_ttl=30m \
  allow_user_certificates=true \
  allowed_users="deploy,www-data" \
  default_extensions="permit-pty="

Parameter:

  • key_type=ca - Verwendet CA-basierte Signierung
  • ttl=10m - Zertifikate sind 10 Minuten gültig
  • allowed_users - Erlaubte SSH-Benutzer auf dem Ziel-Server
  • default_extensions - SSH-Zertifikat-Extensions

Policy für SSH-Zugriff

Erstelle gitlab-ssh-deploy.hcl:

hcl
# SSH-Zertifikat signieren
path "ssh/sign/gitlab-deployer" {
  capabilities = ["create", "update"]
}

Policy anwenden:

bash
vault policy write gitlab-ssh-deploy gitlab-ssh-deploy.hcl

JWT-Rolle aktualisieren:

bash
vault write auth/jwt_gitlab/role/gitlab-deployer - <<EOF
{
  "role_type": "jwt",
  "policies": ["gitlab-ssh-deploy"],
  "token_explicit_max_ttl": 600,
  "user_claim": "user_email",
  "bound_audiences": "https://vault.iteas.cloud",
  "bound_claims_type": "glob",
  "bound_claims": {
    "project_path": "iteas/*",
    "ref_protected": "true"
  },
  "token_no_default_policy": true
}
EOF

Ziel-Server Konfiguration

CA Public Key auf Server installieren

Hole den öffentlichen CA-Schlüssel von Vault:

bash
vault read -field=public_key ssh/config/ca > /etc/ssh/vault_ca.pub

Konfiguriere SSH Server (/etc/ssh/sshd_config):

TrustedUserCAKeys /etc/ssh/vault_ca.pub

SSH-Dienst neu laden:

bash
systemctl reload sshd

GitLab CI/CD Konfiguration

Beispiel .gitlab-ci.yml

yaml
stages:
  - deploy

variables:
  VAULT_ADDR: "https://vault.iteas.cloud"
  DEPLOY_USER: "deploy"
  DEPLOY_HOST: "prod.example.com"
  DEPLOY_PATH: "/var/www/app"

deploy-prod:
  stage: deploy
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.iteas.cloud
  before_script:
    # Vault-Authentifizierung via JWT
    - export VAULT_TOKEN=$(vault write -field=token auth/jwt_gitlab/login jwt=$VAULT_ID_TOKEN)

    # Temporären SSH-Schlüssel generieren
    - ssh-keygen -t ed25519 -f /tmp/deploy_key -N ""

    # Öffentlichen Schlüssel von Vault signieren lassen
    - vault write -field=signed_key ssh/sign/gitlab-deployer public_key=@/tmp/deploy_key.pub > /tmp/deploy_key-cert.pub

    # SSH-Konfiguration
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - cp /tmp/deploy_key ~/.ssh/deploy_key
    - cp /tmp/deploy_key-cert.pub ~/.ssh/deploy_key-cert.pub
    - chmod 600 ~/.ssh/deploy_key
    - |
      cat > ~/.ssh/config <<EOF
      Host ${DEPLOY_HOST}
        User ${DEPLOY_USER}
        IdentityFile ~/.ssh/deploy_key
        CertificateFile ~/.ssh/deploy_key-cert.pub
        StrictHostKeyChecking accept-new
      EOF
  script:
    # Deployment via Rsync
    - |
      rsync -avz --delete \
        --exclude='config/secrets.php' \
        --exclude='runtime/*' \
        --exclude='web/uploads/*' \
        --exclude='.git' \
        ./ ${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/

    # Post-Deployment-Befehle via SSH
    - ssh ${DEPLOY_USER}@${DEPLOY_HOST} "cd ${DEPLOY_PATH} && composer install --no-dev"
    - ssh ${DEPLOY_USER}@${DEPLOY_HOST} "cd ${DEPLOY_PATH} && php yii migrate --interactive=0"
    - ssh ${DEPLOY_USER}@${DEPLOY_HOST} "cd ${DEPLOY_PATH} && php yii cache/flush-all"
  only:
    - tags
  tags:
    - docker-runner
  image: alpine:latest

.install_dependencies: &install_dependencies
  - apk add --no-cache openssh-client rsync curl
  - curl -o /usr/local/bin/vault https://releases.hashicorp.com/vault/1.21.0/vault_1.21.0_linux_amd64.zip
  - unzip /usr/local/bin/vault -d /usr/local/bin/
  - chmod +x /usr/local/bin/vault

Erweiterte Rsync-Optionen

yaml
script:
  - |
    rsync -avz \
      --delete \
      --delete-excluded \
      --exclude='config/secrets.php' \
      --exclude='runtime/*' \
      --exclude='web/uploads/*' \
      --exclude='.git' \
      --exclude='tests/' \
      --exclude='codeception.yml' \
      --checksum \
      --compress-level=9 \
      ./ ${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/

Wichtige Rsync-Flags:

  • -a - Archive-Modus (erhält Rechte, Timestamps, etc.)
  • -v - Verbose-Ausgabe
  • -z - Komprimierung während Übertragung
  • --delete - Löscht Dateien auf dem Ziel, die in der Quelle nicht existieren
  • --checksum - Verwendet Checksummen statt Timestamps
  • --exclude - Schließt Dateien/Verzeichnisse aus

Sicherheitshinweise

  1. Protected Branches: Deployments sollten nur auf geschützten Branches laufen (ref_protected: true in der Vault-Rolle)
  2. Kurze TTL: SSH-Zertifikate sollten eine kurze Lebensdauer haben (5-30 Minuten)
  3. Minimale Berechtigungen: Der Deploy-User auf dem Server sollte nur Schreibrechte auf das Deployment-Verzeichnis haben
  4. Audit Logging: Aktiviere Vault Audit Logs für SSH-Zertifikat-Requests
  5. Known Hosts: Verwende StrictHostKeyChecking accept-new statt no für bessere Sicherheit

Fehlerbehebung

SSH-Verbindung schlägt fehl

Prüfe auf dem Ziel-Server:

bash
# SSH-Logs prüfen
journalctl -u sshd -f

# CA-Zertifikat testen
ssh-keygen -L -f /etc/ssh/vault_ca.pub

Vault-Authentifizierung schlägt fehl

bash
# Token-Payload prüfen
echo $VAULT_ID_TOKEN | cut -d. -f2 | base64 -d | jq

# Vault-Login testen
vault write auth/jwt_gitlab/login jwt=$VAULT_ID_TOKEN

Rsync-Probleme

bash
# Trockenlauf
rsync -avzn --dry-run ./ user@host:/path/

# Verbose-Output erhöhen
rsync -avvvz ./ user@host:/path/

Vorteile dieses Ansatzes

  • Keine statischen SSH-Keys: Zertifikate werden on-demand generiert
  • Automatische Rotation: Kurze TTL erzwingt regelmäßige Erneuerung
  • Audit Trail: Alle SSH-Zugriffe werden in Vault protokolliert
  • Zentrale Verwaltung: SSH-Berechtigungen werden zentral in Vault verwaltet
  • Erhöhte Sicherheit: Kompromittierte Zertifikate sind nur kurz gültig

Iteas Tools Integration Platform Version v1.0.21

Version: v1.0.21 Version: v1.0.21
Commit: 7a0e1c11
Deployed at: 2026-09-24T13:56:52Z