DAP central image pull fails under inbound CI_JOB_TOKEN scope: add companion auth on duo_workflows_default_image_registry

Everyone can contribute. Help move this issue forward while earning points, leveling up and collecting rewards.

Problem to solve

duo_workflows_default_image_registry is hostname-only with no companion auth field. The GitLab runner pulls the central image with the calling project's CI_JOB_TOKEN. The registry's /jwt/auth?service=container_registry endpoint rejects this token under enforce_ci_inbound_job_token_scope_enabled=true, which is the default on hardened self-managed instances.

Result: any project that does not pre-allowlist the central image's host project hits a 401 on image pull, before a single line of the flow runs.

Reproducer

On a self-managed instance with inbound CI_JOB_TOKEN scope enforced:

  1. Set Admin > GitLab Duo > Image registry to a host project's registry hostname.
  2. Run a DAP flow from any project that is not in the host's ci_inbound_job_token_scope_allowlist.
  3. Observe HTTP 401 from /jwt/auth?service=container_registry in the runner job log.

Field trace: cs.gitlabdemo.cloud, 2026-04-29, runner job 10623081.

Customer Impact

  • A US financial institution (Ultimate, Self-Managed): worked around via Artifactory path-rewrite, fragile pattern.
  • A European insurance customer: blocked, the hostname-only setting cannot point at their external Artifactory.
  • A German financial-services customer: PoC blocked at the same step.

(SFDC links: see internal Slack thread.)

Why internal-visibility doesn't fix it

Empirical PAT-vs-CI_JOB_TOKEN test on cs.gitlabdemo.cloud (2026-04-29):

  • PAT auth on /jwt/auth: succeeds, returns JWT with pull action.
  • CI_JOB_TOKEN auth on /jwt/auth: fails 401 even when the host project has container_registry_access_level=enabled and visibility=internal.

The "internal visibility bypasses inbound scope" guarantee in docs.gitlab.com/ci/jobs/ci_job_token/ applies only to the PAT auth path, not the runner's CI_JOB_TOKEN path.

Proposal

Ranked:

  1. Companion service-account / deploy-token field on the admin form. Stored encrypted, injected into the runner pull credentials when the image is pulled from the configured registry.
  2. Auto-allowlist the instance root group when duo_workflows_default_image_registry is set, so every project's inbound scope already includes the host.
  3. Document DOCKER_AUTH_CONFIG instance variable as the supported pattern, with explicit warning that consumers must rotate it manually.

Option 1 is the cleanest, option 2 has the best ergonomics, option 3 is the cheapest stopgap.

Acceptance Criteria

  • Admin can configure pull credentials alongside the image registry hostname.
  • Pulls succeed from any project on the instance without per-project allowlist edits.
  • Documented in the existing Custom container registry docs page.
  • Backwards compatible with existing 18.9+ deployments that already set the hostname.

Out of scope

  • Full image path/tag overridability (covered by #594208).
  • Virtual-registry support (covered by sibling issue under ~"group::package registry", link added once filed).

Other links/references

cc @bastirehm @fdegier

Edited by 🤖 GitLab Bot 🤖