Skip to content
CloudOps
All prompts
AI for GitLab CI/CD Difficulty: Intermediate ClaudeChatGPT

GitLab CI/CD Pipeline & Access Tokens Security Prompt

Manage and secure GitLab tokens — trigger tokens, project access tokens, group access tokens, $CI_JOB_TOKEN scope, leak detection and rotation.

Target user
DevOps engineers managing GitLab access tokens
Difficulty
Intermediate
Tools
Claude, ChatGPT

The prompt

You are a senior DevOps engineer / security engineer who has audited and rotated GitLab tokens at scale. You know the difference between trigger tokens, deploy tokens, project access tokens, $CI_JOB_TOKEN, and personal access tokens — and which ones leak in practice.

I will provide:
- The use case (CI to trigger downstream, CI to call API, external system to call GitLab, deploy authentication)
- Current token type in use (if any)
- Token storage approach (variables, external secret manager)
- Rotation policy / current state
- The symptom (if debugging: leak, 401, scope-too-narrow, scope-too-wide)

Your job:

1. **Map token type to use case**:
   - **`$CI_JOB_TOKEN`** — auto-generated per job; scope to specific projects' APIs (Settings → CI/CD → Job token permissions); preferred for in-instance API calls
   - **Pipeline trigger token** — project-scoped; can ONLY create pipelines; for external CI systems firing pipelines
   - **Project access token (PrAT)** — project-scoped; configurable scopes (api, read_repository, etc.); has an expiry; for project-bound automations
   - **Group access token (GrAT)** — group-scoped variant of PrAT
   - **Personal access token (PAT)** — user-scoped; broadest power; should be avoided for shared automation
   - **Deploy token** — project-scoped; read-only to repo/registry by default; for deploy reads
   - **Deploy key** — SSH-based; pull or pull+push to one repo
2. **For storage**:
   - **Don't commit tokens to git**, ever
   - **Don't paste in chat / email / tickets**
   - **GitLab variables (masked)** — adequate for general use; rotation requires manual update
   - **External secret manager (Vault, AWS SM)** — preferred; CI pulls at runtime
   - **`.env` files with sealed-secrets pattern** — for GitOps-like flows
3. **For rotation**:
   - Project / group access tokens have built-in expiry (1d to 1y)
   - Trigger tokens are revocable but not auto-expiring
   - PATs have expiry
   - Plan rotation: create new token → update consumers → revoke old → verify
4. **For audit / leak detection**:
   - GitLab Premium has audit events for token usage
   - Self-managed: review `production_json` logs for token use patterns
   - Secret scanning (GitLab built-in) catches tokens in commits — alerts in Security Dashboard
   - GitHub-style: if a token leaks to GitHub, GitLab's GH integration may revoke
5. **For least-privilege scoping**:
   - `$CI_JOB_TOKEN` permissions (newer GitLab): allowlist specific projects allowed to use this project's job token
   - PrAT scopes: pick minimum needed (`read_api` vs `api`)
   - Avoid `api` scope where `read_api` works
6. **For multi-project / multi-tenancy**:
   - Group access tokens for org-wide automation
   - Project tokens for project-specific
   - Avoid PAT in shared automation; the user can leave the org
7. **For incident response (suspected leak)**:
   - Revoke token immediately via API or UI
   - Check audit log / pipeline log for unexpected use
   - Rotate downstream dependents
   - Consider: was the token used in places the legitimate user wouldn't have triggered?
8. **For automated rotation**:
   - PrAT/GrAT can be rotated via API
   - Update GitLab variable AND consumer simultaneously
   - Use a wrapper script or rotation tool

Mark DESTRUCTIVE: revoking a token in active use without notifying consumers, generating a PAT with `api` scope for a long-lived integration (broad blast radius), using a service-account-style PAT (single user) for team-shared automation.

---

Use case: [DESCRIBE]
Current token type: [DESCRIBE]
Storage: [DESCRIBE]
Rotation policy: [DESCRIBE]
Symptom (if debugging): [DESCRIBE]

Why this prompt works

GitLab tokens are easy to provision but hard to govern. Many teams use PATs everywhere, which creates user-dependency, broad scopes, and rotation headaches. This prompt enforces token-type-by-use-case.

How to use it

  1. Match token to use case — see the cheat sheet.
  2. Start with least scoperead_api before api.
  3. Plan rotation — set expiry, automate updates.
  4. Audit usage — Premium audit events or log review.

Useful commands

# List project access tokens
curl --header "PRIVATE-TOKEN: <t>" \
    "https://gitlab.example.com/api/v4/projects/<id>/access_tokens" | jq

# Create a project access token (limited scope)
curl --request POST --header "PRIVATE-TOKEN: <t>" --header "Content-Type: application/json" \
    --data '{
        "name": "deploy-token-2026",
        "scopes": ["read_api","read_repository"],
        "access_level": 30,
        "expires_at": "2027-05-30"
    }' \
    "https://gitlab.example.com/api/v4/projects/<id>/access_tokens" | jq

# Rotate a project access token (1 API call; old becomes invalid, new returned)
curl --request POST --header "PRIVATE-TOKEN: <t>" \
    "https://gitlab.example.com/api/v4/projects/<id>/access_tokens/<token-id>/rotate" | jq

# Revoke
curl --request DELETE --header "PRIVATE-TOKEN: <t>" \
    "https://gitlab.example.com/api/v4/projects/<id>/access_tokens/<token-id>"

# List trigger tokens
curl --header "PRIVATE-TOKEN: <t>" \
    "https://gitlab.example.com/api/v4/projects/<id>/triggers" | jq

# Job token allowlist (newer GitLab)
# Settings → CI/CD → Job token permissions
# API: PATCH project's ci_cd_settings
curl --request PATCH --header "PRIVATE-TOKEN: <t>" --header "Content-Type: application/json" \
    --data '{"ci_inbound_job_token_scope_enabled":true}' \
    "https://gitlab.example.com/api/v4/projects/<id>"

Token cheat sheet

Use caseToken typeWhy
CI calls own project’s API$CI_JOB_TOKENAuto-generated, scope-aware
CI triggers downstream pipeline (same instance)$CI_JOB_TOKEN or trigger tokenJOB_TOKEN if target’s allowlist set
External system triggers pipelineTrigger tokenProject-scoped, limited
CI pushes to another project’s repoPrAT or deploy keyScope: write_repository
External tool reads project metadataPrAT (read_api)Least privilege
Org-wide automationGroup access tokenSpans projects in group
User-bound personal scriptsPATTied to user; usually not for shared automation
Image registry pull (CI)Deploy tokenread_registry
Git pull-only mirrorDeploy key (SSH)No web token leaks

Patterns

Hot-swap rotation

# 1. Create new token (note expiry on old)
NEW_TOKEN=$(curl -X POST -H "PRIVATE-TOKEN: $ADMIN_PAT" \
    -d '{"name":"rotation-2026-05","scopes":["read_api"],"expires_at":"2026-08-30"}' \
    "$GITLAB/api/v4/projects/$PROJECT_ID/access_tokens" | jq -r .token)

# 2. Update consumer's variable / secret
curl -X PUT -H "PRIVATE-TOKEN: $ADMIN_PAT" \
    -d "value=$NEW_TOKEN" \
    "$GITLAB/api/v4/projects/$PROJECT_ID/variables/MY_TOKEN"

# 3. Verify consumer can authenticate (test job or healthcheck)
# 4. Revoke old token
curl -X DELETE -H "PRIVATE-TOKEN: $ADMIN_PAT" \
    "$GITLAB/api/v4/projects/$PROJECT_ID/access_tokens/$OLD_TOKEN_ID"

Using $CI_JOB_TOKEN for cross-project API calls

In the target project’s Settings → CI/CD → Job token permissions → Allowlist source projects.

# In source project's .gitlab-ci.yml
trigger-other:
  script:
    - curl --request POST \
        --header "JOB-TOKEN: $CI_JOB_TOKEN" \
        "https://gitlab.example.com/api/v4/projects/<target-id>/jobs/<job-id>/play"

External secret manager pull

deploy:
  script:
    - |
      # Retrieve token from Vault using OIDC auth
      VAULT_TOKEN=$(curl -s -X POST -d "{\"jwt\":\"$CI_JOB_JWT\",\"role\":\"my-role\"}" \
          $VAULT_ADDR/v1/auth/jwt/login | jq -r .auth.client_token)
      DEPLOY_TOKEN=$(curl -s -H "X-Vault-Token: $VAULT_TOKEN" \
          $VAULT_ADDR/v1/secret/data/deploy-token | jq -r .data.data.token)
      ./deploy.sh
  variables:
    VAULT_ADDR: https://vault.example.com

Common findings this catches

  • PAT used in shared CI when user leaves → pipelines break.
  • api scope used where read_api would suffice — over-privileged.
  • Trigger token in .gitlab-ci.yml committed to git — public credential.
  • No expiry on PrAT — long-lived; against best practice.
  • Token rotation breaks pipelines because consumers not updated atomically.
  • $CI_JOB_TOKEN rejected by target project — needs allowlist permission.
  • Deploy token has api scope — should be only read_repository / read_registry.

When to escalate

  • Suspected token leak → revoke immediately, audit usage, coordinate with security.
  • Org-wide rotation campaign — coordinate with platform team; staged rollout.
  • Compliance requires audit trail of token usage → enable Premium audit events; integrate with SIEM.

Related prompts

Newsletter

Get weekly AI workflows for DevOps engineers

Practical prompts, automation ideas, and tool reviews for infrastructure engineers. One email per week. No spam.