Skip to content

CI/CD Integration

GitLab CI/CD pipelines authenticate with Vault using short-lived JWT tokens. No static Vault tokens or credentials are stored as GitLab CI variables.


How It Works

GitLab generates a signed JWT (CI_JOB_JWT_V2) for each CI job. The pipeline exchanges this JWT for a short-lived Vault token scoped to the policies defined for the GitLab JWT role.

GitLab pipeline starts
    |
    | CI_JOB_JWT_V2 generated by GitLab
    v
POST /v1/auth/jwt/login  →  Vault validates JWT signature
                              checks bound_claims
                              returns short-lived token (15m)
    |
    | vault kv get secret/<path>
    v
Pipeline reads the secrets it needs, uses them in the job
    |
Token expires automatically at pipeline end

Standard Pipeline Pattern

# .gitlab-ci.yml
stages:
  - build
  - deploy

.vault-auth: &vault-auth
  before_script:
    - |
      VAULT_TOKEN=$(curl -sf \
        --request POST \
        --data "{\"jwt\": \"$CI_JOB_JWT_V2\", \"role\": \"gitlab-orchestrator\"}" \
        "$VAULT_ADDR/v1/auth/jwt/login" | jq -r '.auth.client_token')
      export VAULT_TOKEN

deploy-dev:
  stage: deploy
  <<: *vault-auth
  script:
    - |
      # Read deployment credentials from Vault
      NEXUS_PASSWORD=$(vault kv get -field=password secret/nexus/svc-orchestrator)
      SONAR_TOKEN=$(vault kv get -field=token secret/sonarqube/svc-orchestrator)

      # Use credentials
      docker login nexus.shared.cwiq.io:8443 -u svc-orchestrator -p "$NEXUS_PASSWORD"

VAULT_ADDR is a GitLab group-level CI variable

VAULT_ADDR is set at the GitLab group level (group 9) to https://vault.shared.cwiq.io. It does not need to be redefined in per-project YAML.


JWT Role Configuration

The gitlab-orchestrator role grants access to the secrets needed by the CWIQ platform pipelines:

vault write auth/jwt/role/gitlab-orchestrator \
  role_type="jwt" \
  bound_claims='{"namespace_path": ["orchestrator"]}' \
  user_claim="sub" \
  token_policies="nexus-read-policy,sonarqube-ci,defectdojo-ci" \
  token_ttl=15m \
  token_max_ttl=30m

The namespace_path bound claim restricts authentication to pipelines from GitLab projects under the orchestrator namespace only.

Listing JWT Roles

vault list auth/jwt/role
vault read auth/jwt/role/gitlab-orchestrator

Adding a New Secret to CI/CD

When a pipeline needs a new secret:

  1. Store the secret in Vault:

    vault kv put secret/<app>/svc-orchestrator \
      token=<value> \
      url=https://<app>.shared.cwiq.io
    

  2. Update the policy to grant read access:

    # View current policy
    vault policy read nexus-read-policy
    
    # Update policy to add new path
    vault policy write nexus-read-policy - <<'EOF'
    path "secret/data/nexus/*" {
      capabilities = ["read"]
    }
    path "secret/data/<app>/svc-orchestrator" {
      capabilities = ["read"]
    }
    EOF
    

  3. Update vault-server/docs/02-cli-operations.md to document the new secret path.


Debugging JWT Auth Failures

Common causes

Symptom Likely Cause
invalid role name The role name in the pipeline doesn't match what's in Vault
bound claims mismatch Pipeline runs from a namespace not in bound_claims
permission denied reading secret Policy doesn't include the path
token is expired or invalid JWT expired before vault login ran (slow job startup)

Check the JWT role

vault read auth/jwt/role/gitlab-orchestrator

Verify bound_claims includes the project's namespace.

Check the policy

vault policy read <policy-name>
# Verify the secret path is listed with "read" capability

Test manually

# Decode the JWT to inspect claims (from within a pipeline job)
echo "$CI_JOB_JWT_V2" | cut -d'.' -f2 | base64 -d 2>/dev/null | python3 -m json.tool

Security Considerations

  • JWT tokens are valid for the duration of token_ttl (15 minutes). Pipelines that take longer need token_max_ttl set appropriately.
  • Use bound_claims to restrict which GitLab projects and branches can authenticate. Never use a role without bound claims.
  • Pipelines receive only the secrets their policy allows. Use separate policies per concern (Nexus, SonarQube, DefectDojo).