Join our Newsletter — 33% off our NHI Course
Home› Guides› GitLab Security Guide: Tokens, Runners and Pipeline Security
Guide Software & AI Supply Chain

GitLab Security Guide: Tokens, Runners and Pipeline Security

PDF for newsletter subscribers · free to read online

← All guides
By Lalit Choda, NHI Mgmt Group Updated 24 October 2026 16 min read
On this page

A GitLab instance holds source code, CI/CD secrets, deployment credentials and the runners that execute code with them, so one leaked token or one misconfigured runner can reach production. GitLab offers strong controls (expiring tokens, short-lived job tokens, OIDC ID tokens, protected branches and variables, secret push protection) but many are optional, tier-dependent or off by default on self-managed instances. This guide explains how identity and tokens work in GitLab, how to control administrators and service accounts, how to harden runners and pipelines, and what to log. It is written for platform engineers, DevSecOps teams and security architects running GitLab.com, GitLab Dedicated or GitLab Self-Managed. Feature names follow the GitLab documentation as of the 19.x releases in October 2026.

Key takeaways

  • Tokens are the real identities. GitLab has more than a dozen token types, each with a recognisable prefix (glpat-, glrt-, gldt-, glcbt- and others), and most compromises of GitLab estates involve a token rather than a password.
  • Prefer short-lived credentials in CI/CD. CI_JOB_TOKEN is valid only while the job runs and OIDC ID tokens expire with the job timeout (five minutes if none is set); personal access tokens in pipelines should be the exception.
  • Runners are where secrets meet untrusted code. Any Developer can run code on a runner, privileged Docker mode can give jobs root on the host, and shared non-ephemeral runners let one project's job affect another's.
  • Protected branches and protected variables work together. A protected variable is only exposed to pipelines on protected branches or tags, so who can push or merge to those branches decides who can read production secrets.
  • Patch self-managed instances fast. CISA's KEV catalogue lists exploited GitLab flaws including CVE-2023-7028 (account takeover by password reset) and CVE-2026-85706 (unauthenticated arbitrary file read, CVSS 10.0).

How identity and access work in GitLab

Human users sign in with a password, passkey or an external provider (SAML, OIDC, LDAP), optionally with two-factor authentication. Access to groups and projects comes from membership with a role: Guest, Planner, Reporter, Developer, Maintainer or Owner, with custom roles on Ultimate. Roles inherit down the group hierarchy, which is why membership at a top-level group is far more powerful than it looks in a single project.

Everything non-interactive authenticates with a token. The GitLab token overview lists these types:

TokenPrefixActs asLifetimeMain risk
Personal access tokenglpat- (configurable)The user, with chosen scopesExpiry required by default; default and maximum 365 daysCarries all of a person's access, survives job changes
Project or group access tokenglpat-A bot user with a set roleAs aboveOver-scoped bots (Maintainer, api scope) used across many pipelines
Deploy tokengldt-Repository and registry access, no APIOptional expiryLong-lived registry pull or push credentials in clusters
Deploy keySSH keyRead-only or read-write repository accessUntil removedWrite-enabled keys shared across projects
Runner authentication tokenglrt-A runner picking up jobsUntil resetA cloned runner with the same token can steal jobs
CI/CD job tokenglcbt-The user who triggered the pipeline, limited endpointsOnly while the job runsCross-project access if allowlists are wide
Pipeline trigger tokenglptt-Starts pipelinesUntil revokedLets anyone holding it run pipelines with the owner's access
Feed and incoming email tokensglft-, glimt-Read feeds, create issues by emailDo not expireLow privilege, but never rotate on their own
OAuth access tokensApplication secrets gloas-A third-party app on a user's behalfAccess token 2 hours by defaultForgotten integrations with api scope

The fixed prefixes are what make secret scanning reliable. Administrators can customise the personal access token prefix, but the account settings documentation warns that custom prefixes can reduce secret detection coverage.

Token expiry

  • Required by default: new personal, project, group and impersonation tokens must have an expiry date. If none is entered it defaults to 365 days, and the default maximum lifetime is 365 days (400 days behind the buffered_token_expiration_limit feature flag, from 17.6). Upgrades may apply an expiry to existing tokens that had none.
  • Do not switch it off: administrators can disable "Require expiration date"; tokens then created without a date never expire. Service account tokens follow a separate setting (service_access_tokens_expiration_enforced).
  • Shorter caps on Ultimate: a maximum allowable lifetime can be set in days; tokens without expiry or with a longer lifetime are revoked three hours after the limit is set, so warn token owners first.
  • Notifications: GitLab emails owners seven days before expiry, and also at 30 and 60 days from 17.6. Inactive group and project access tokens are deleted after 30 days by default.

Admin roles and privileged access

  • Admin Mode: on self-managed instances, enable Admin Mode so administrator accounts do not carry admin rights by default. Admins re-authenticate to enter it, it ends automatically after six hours, and API calls need a token with the admin_mode scope. Git access over SSH or HTTPS is not covered by Admin Mode.
  • Two-factor enforcement: enforce 2FA for all users, or at minimum for administrators (which includes users with custom admin roles), with a short grace period. A grace period of 0 enforces registration at the next sign-in.
  • Disable password sign-in where SSO exists: sign-in restrictions can turn off web password authentication, block password sign-in for users with an SSO identity, and disable password authentication for Git over HTTP(S) so Git clients must use tokens.
  • Few administrators and Owners: instance administrators can read every project and create impersonation tokens. Keep the number small, use separate admin accounts from daily accounts, and review top-level group Owners as privileged users.
  • Account takeover paths: CVE-2023-7028 let attackers send password reset emails to an unverified address, which CISA added to KEV in May 2024 (fixed in the 16.7.2 security release). It is a strong argument for enforcing 2FA on every account, not only administrators.
  • Unknown sign-in emails: turn on notifications for sign-ins from unknown IP addresses or devices.

See the CI/CD Pipeline Identity Security Guide for how these human roles translate into pipeline permissions.

Integrations, API tokens and service accounts

Choosing the right credential

GitLab's own guidance for CI/CD is to prefer job tokens, then project tokens, then group tokens, and to avoid personal access tokens. For automation outside pipelines, use service accounts.

NeedUseAvoid
Job calls the GitLab API or registry in its own projectCI_JOB_TOKENA stored PAT variable
Job needs another project's packages or repositoryCI_JOB_TOKEN plus the other project's job token allowlist, with fine-grained permissions"Allow access from all projects" on self-managed
Job deploys to AWS, Azure, Google Cloud or reads VaultOIDC ID token (id_tokens) with a narrow trust policyStatic cloud keys in CI/CD variables
Long-running external automation (bots, reporting, MCP or AI agent tools)Service account with a scoped, expiring tokenA named employee's PAT
Cluster pulls imagesDeploy token with read_registry onlyGroup token with api scope

Job tokens and allowlists

  • Allowlist: a project's CI/CD job token allowlist (renamed from "Limit access to this project" in 17.3) by default contains only the project itself. Add specific groups or projects, up to 200 of each. Adding a group includes its future projects, so prefer projects.
  • Fine-grained permissions: restrict which REST endpoints a job token may call from allowlisted projects instead of granting everything the triggering user could do.
  • Audit: Settings > CI/CD > Job token permissions shows which projects authenticated with a job token, with a CSV download; use it to build the allowlist before enforcing.
  • Limits: job tokens cannot call GraphQL, cannot outlive their job and cannot exceed the triggering user's permissions. From 19.0 the JWT format is the default.

OIDC ID tokens to cloud and Vault

  • Declare per job: the id_tokens keyword issues an RS256-signed JWT with an aud you choose. Use a different audience per target so a token for one service cannot be replayed to another.
  • Bind trust to stable claims: the default sub is project_path:{group}/{project}:ref_type:{type}:ref:{branch}. Cloud trust policies should require the exact project and a protected ref, and preferably project_id rather than a path that could be reused after a rename. ref_protected, environment_protected and deployment_tier claims let policies require protected deployments.
  • Platform difference: GitLab's documentation notes that on GitLab.com the AWS OIDC provider can condition on additional keys such as project_id and ref_protected, while Self-Managed and Dedicated support only sub. Encode protection into the sub claim components on those platforms.
  • Retire legacy tokens: CI_JOB_JWT and CI_JOB_JWT_V2 are deprecated; move to id_tokens. See the Workload Identity Federation Guide.

Service accounts

  • Types: instance service accounts (created by administrators), group service accounts and project service accounts. They use no licensed seat and cannot sign in through the UI.
  • Tokens: they authenticate with personal access tokens that default to 365 days. If the instance does not require expiry dates, a service account token can be created with no expiry, which is the most common source of immortal GitLab credentials.
  • Ownership: record an owning team, purpose and review date for each service account and project bot. See the NHI Ownership Guide.

Hardening baseline

Instance and group settings

  • Patch cadence: GitLab publishes patch releases for supported versions. The 19.3.2 patch release fixed CVE-2026-85706, an unauthenticated path traversal in the repository commits API rated CVSS 10.0 and added to KEV on 11 September 2026, alongside CVE-2026-87719 (insecure deserialisation, 9.9) and CVE-2026-79708, which exposed protected CI/CD variables. Fixed versions were 19.3.2, 19.2.6 and 19.1.8, with backports to 18.11.12 and 19.0.9. Older KEV entries (CVE-2021-22205 remote code execution, with known ransomware use, and the SSRF flaws CVE-2021-22175 and CVE-2021-39935) show unpatched instances are exploited years later.
  • Exposure: do not expose a self-managed instance to the internet unless it must be; put it behind SSO, VPN or an identity-aware proxy where possible.
  • Restrict creation: restrict who can create top-level groups and who can create project and group access tokens.
  • SSH keys: on Ultimate, set a maximum SSH key lifetime.
  • Pipeline variables: set "Minimum role to use pipeline variables" (ci_pipeline_variables_minimum_override_role) to no_one_allowed or owner, and use pipeline inputs instead. The self-managed default is maintainer.

Protected branches, tags and variables

  • Protect deployment refs: set "Allowed to push and merge" to No one or Maintainers on default and release branches, leave "Allowed to force push" off, and require code owner approval (Premium and Ultimate) for sensitive paths such as .gitlab-ci.yml.
  • Protect tags too: GitLab advises protecting tags matching protected branch names, because a branch and a tag can share a name.
  • Variables: mark production secrets as protected and "masked and hidden" (single line, at least 8 characters; hidden can only be set at creation). Since 18.3 new variables default to masked. Masking is not a guarantee: the documentation warns that malicious code can compromise both masked and protected variables.
  • Merge request pipelines: pipelines from forks do not get the parent's variables by default, but running a fork's merge request pipeline in the parent project exposes all variables, so review the CI file changes first.
  • External secrets: keep only non-sensitive configuration in .gitlab-ci.yml and fetch secrets at runtime with ID tokens. See the Secrets Management Guide.

Runners

Executor or settingRisk per GitLab runner security docsUse for
ShellHigh: jobs run as the runner user and can reach other projects on the hostTrusted builds only
Docker, non-privilegedConsidered safe with a non-root user and dropped capabilitiesDefault for shared runners
Docker, privilegedNot advised: disables container isolation, root on host possibleOnly on isolated, ephemeral VMs, dedicated runners limited to protected branches
Ephemeral VM per job (autoscaled, MaxBuilds = 1)A compromised VM handles one jobPrivileged builds, untrusted code
pull_policy if-not-present on shared runnersCan reuse a cached private image for a user without registry accessUse always or never instead
GIT_STRATEGY: fetch on shared hostsReused working copies let one pipeline inject code into anotherOnly where everyone with access is trusted; enable FF_ENABLE_JOB_CLEANUP

Separate runners by trust level: production deployment runners should be project or group runners marked protected, so they only run jobs from protected branches. Place runners in their own network segment, block inbound SSH, and filter access to cloud metadata endpoints so a job cannot read the runner host's cloud role. Treat the glrt- runner authentication token as a secret: anyone holding it can register a clone and receive the runner's jobs. See the CI/CD Security Guide.

Secret detection and push protection

  • Secret push protection (Ultimate): scans the diffs of pushed commits in a pre-receive hook and rejects pushes containing known secret types. Self-managed administrators must allow it first, then Maintainers, Owners or Security Managers enable it per project. Binary files and files over 1 MiB are skipped.
  • Audit the bypasses: users can skip it with the secret_push_protection.skip_all push option or [skip secret push protection] in a commit message; each skip creates an audit event. Review them weekly.
  • Pipeline secret detection: scans after push and covers what push protection misses. Scan full history before making any project public. See the Git Secret Scanning Guide.

Rollout order

  1. Inventory: export all personal, project and group access tokens, service accounts, deploy tokens and keys, runners and their executors; flag tokens without expiry and with api or admin scopes.
  2. Identity basics: enforce 2FA, enable Admin Mode, turn on "Require expiration date", disable password Git authentication where SSO is in place.
  3. Observe before enforcing: read the job token authentication log for each project, then populate allowlists; run pipeline secret detection before turning on push protection.
  4. Move secrets: migrate cloud credentials to ID tokens project by project, keeping the old variable disabled but present for one release so you can roll back.
  5. Runners: move privileged and deployment jobs onto dedicated protected runners, then remove privileged mode from shared runners.
  6. Cap lifetimes: once owners have rotated, set a maximum token lifetime (for example 90 days) and accept that GitLab revokes longer-lived tokens three hours later.

Logging and detection

  • Audit event streaming (Ultimate): stream top-level group or instance audit events to an HTTP endpoint, AWS S3 or Google Cloud Logging. Verify the X-Gitlab-Event-Streaming-Token header on receipt and deduplicate on the event id, because events can be delivered more than once.
  • Token events: alert on token creation with api, sudo or admin_mode scope, on tokens created by administrators for other users (impersonation tokens), and on rotations or revocations outside change windows.
  • Pipeline and runner events: new runner registrations, changes to protected branch rules, protected variable edits, allowlist changes and push protection skips.
  • Exploitation indicators: GitLab provides security threat detections for local file inclusion attempts related to CVE-2026-85706; also watch web logs for unusual repository commits API requests from unauthenticated clients.
  • Public exposure: watch public code hosts for your glpat- and glrt- tokens; the GitLab public repositories secrets study found 406 valid GitLab tokens among 17,430 live secrets in public GitLab.com projects.
MetricTarget
Active tokens with no expiry dateZero
Pipelines using personal access tokens as CI/CD variablesTrending to zero
Cloud deployments authenticating with ID tokens rather than static keysAbove 90%
Shared runners allowing privileged modeZero
Secret push protection bypasses reviewed within 7 days100%
Days from GitLab critical patch release to self-managed upgradeUnder 3; under 1 for KEV-listed flaws

Framework mapping

GitLab control areaNIST SP 800-53 Rev. 5NIST CSF 2.0MITRE ATT&CK technique addressed
Token expiry, scopes, service accountsIA-5, AC-6PR.AA-01, PR.AA-05T1528 Steal Application Access Token
Secret detection, protected and hidden variablesIA-5PR.AA-01T1552.001 Credentials In Files, T1213.003 Code Repositories
Runner isolation, protected branchesCM-7, SR-3PR.PS-01T1195.002 Compromise Software Supply Chain
ID tokens to cloudIA-5, AC-6PR.AA-05T1078.004 Cloud Accounts
Patching self-managed instancesSI-2, RA-5ID.RA-01, PR.PS-02T1190 Exploit Public-Facing Application
Audit event streamingAU-2DE.CM-09Supports detection of all of the above

Common misconfigurations

  • Customer credentials in repositories: the Red Hat Consulting GitLab breach involved a self-managed instance used for consulting engagements; the attackers claimed the copied reports contained customer tokens and database connection strings, which Red Hat's statement did not confirm. Scan collaboration repositories, not only product code.
  • Expiry requirement turned off: service account and bot tokens created with no expiry date years ago still work and nobody knows what depends on them.
  • Privileged shared runners: privileged Docker mode enabled on a shared runner for one team's Docker-in-Docker builds gives every project's jobs a path to root on the host.
  • Wide job token access: the allowlist disabled on a self-managed instance, or whole top-level groups added, so any pipeline can read any project's packages.
  • Loose OIDC trust: cloud roles that trust any sub under a group path, or any ref, let a feature branch pipeline assume a production role.
  • Unpatched self-managed instance: internet-facing GitLab left several patch releases behind while KEV-listed flaws are exploited.

Practitioner checklist

  • 2FA is enforced for all users and Admin Mode is enabled on self-managed instances.
  • "Require expiration date" is on, and no active token, including service account tokens, lacks an expiry.
  • Pipelines use CI_JOB_TOKEN or ID tokens; no personal access tokens are stored as CI/CD variables.
  • Every project's job token allowlist lists specific projects, built from the authentication log.
  • Cloud trust policies for ID tokens pin aud, project and a protected ref or environment.
  • Production secrets are protected, masked and hidden variables or fetched from an external secrets manager.
  • Deployment refs and matching tags are protected, force push is off and .gitlab-ci.yml changes need code owner approval.
  • Privileged and deployment jobs run only on dedicated protected runners; shared runners are non-privileged with pull_policy always.
  • Secret push protection and pipeline secret detection are on, and bypass audit events are reviewed.
  • Audit events stream to the SIEM, and critical GitLab patch releases are applied within days.

Standards and references

Related NHI Mgmt Group resources: CI/CD Security Guide · CI/CD Pipeline Identity Security Guide · GitHub and GitHub Actions Security Guide · Secure Coding Guide · Web Application Firewall Guide

PDF for subscribers

Take this guide with you

Every section, table and checklist in one PDF, with links to the sources and related guides. Subscribe to our weekly NHI & AI Identity Journal and we’ll email you a download link.

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Written and reviewed by Lalit Choda, NHI Mgmt Group. Last updated 24 October 2026.
NHI Mgmt Group — the #1 independent authority on Non-Human Identity, IAM, and Agentic AI security. nhimg.org

PDF Downloads for Newsletter Subscribers

Get the guide

Enter your details and we’ll email you a download link.

The full guide is free to read online. The PDF is a thank you for subscribing.

Fill in all fields marked * and tick the PDF box to continue.