Add application setting to block JWT for reclaimed project paths

What does this MR do and why?

Adds an application setting block_jwt_for_reclaimed_paths (default: true) that allows administrators of self-managed and Dedicated instances to disable the security restriction introduced in GitLab 19.1 that blocks CI/CD JWT/OIDC token generation for projects that have reclaimed a previously used namespace path.

The restriction is on by default for all instance types. This setting gives Dedicated customers a supported path to unblock legitimate CI/CD pipelines that are affected by the fix for https://gitlab.com/gitlab-org/gitlab/-/issues/600358, without weakening the default security posture.

Changes:

  • New boolean key block_jwt_for_reclaimed_paths (default: true) in the ci_cd_settings JSONB column — no DB migration needed
  • JSON schema updated to allow the new key
  • JwtV2#verify_path_not_burned! is a no-op when the setting is false
  • Ci::RegisterJobService#id_token_burned_project_path? returns false when the setting is false
  • Admin Area UI toggle in the CI/CD settings section (hidden on GitLab.com)
  • API write path blocked on GitLab.com (see below)
  • On GitLab.com the restriction is hardcoded to true regardless of the stored value (see below)
  • Documentation for the API and Admin Area settings page
  • Specs covering default value, bypass behavior, and SaaS enforcement

Why EE code is involved despite the setting being available in CE

The setting definition, enforcement logic, Admin UI, and API write parameter are all in CE (app/models/, lib/, app/views/, lib/api/helpers/settings_helpers.rb). CE self-managed instances get full read/write access through these code paths with no changes needed.

The EE additions serve two purposes:

  • Hardcoding enforcement on GitLab.comee/app/models/ee/application_setting.rb overrides block_jwt_for_reclaimed_paths to always return true when Gitlab::Saas.feature_available?(:gitlab_com_subscriptions), regardless of the stored value. Both CE enforcement points read this accessor, so no changes are needed there.
  • Blocking writes on GitLab.comee/lib/ee/api/settings.rb strips the attribute in filter_attributes_using_license using Gitlab::Saas.feature_available?(:gitlab_com_subscriptions), preventing a GitLab.com admin from changing it via the API even if they know the attribute name.

Known limitation — field appears in GitLab.com GET response

The CE entity exposes all visible_attributes unconditionally via expose(*exposed_attributes). There is no clean way for the EE module to suppress a field already registered by the CE entity without either breaking CE read/write parity or relying on undocumented Grape::Entity duplicate-expose behaviour. Since:

  • the accessor always returns true on GitLab.com regardless of the stored value,
  • writes are already blocked by filter_attributes_using_license, and
  • the Admin UI hides the toggle via unless Gitlab.com?,

the field appearing in the GitLab.com GET response is an accepted known limitation with no security impact.

References

Screenshots or screen recordings

Before After
No toggle available See !254058 (comment 3820924623)

How to set up and validate locally

  1. In the Admin area, go to Settings > CI/CD and expand Continuous Integration and Deployment.
  2. Verify the new Block JWT generation in reclaimed project paths checkbox is visible and checked by default.
  3. In rails console, create a burned project route and verify the setting bypasses the check:
    # With setting enabled (default), JWT minting raises for burned paths
    ApplicationSetting.current.block_jwt_for_reclaimed_paths # => true
    
    # Disable the setting
    ApplicationSetting.current.update!(block_jwt_for_reclaimed_paths: false)
    
    # Now JWT minting succeeds even for burned paths

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Fabien Catteau

Merge request reports

Loading
Loading