Geo: Support SSH cert gl_id formats in proxied Git requests

What does this MR do and why?

When a Geo secondary proxies a Git-over-SSH request to the primary (a push, or a pull of an out-of-date repository), the primary identifies the user from the gl_id embedded in the Geo JWT. Geo::PushUser only decoded the key-{id} format, so any other format was rejected with Geo push user is invalid.

This broke two cases:

  • SSH certificate authentication — gitlab-shell sends username-{username} (the principal from the CA-signed certificate; there is no keys row to look up). This is the customer-reported bug.
  • The direct-to-primary flow behind geo_proxy_fetch_ssh_to_primary / geo_proxy_push_ssh_to_primary — the secondary sends user-{id} for certificate-authenticated users. This means the feature-flag workaround suggested on the issue does not actually work for SSH certificate users (verified locally: user-{id} also returned 403 before this change).

The fix teaches Geo::PushUser#user to dispatch on the gl_id format (key-, user-, username-), adding Gitlab::Identifier#identify_using_username for the certificate case.

Security note: the username-{username} value originates from gitlab-shell, which derived it from a CA-signed SSH certificate principal (AuthorizedPrincipalsCommand), and reaches the primary inside a Geo-JWT-signed request. The username lookup follows the same trust model as gitlab-shell's existing /api/v4/internal/allowed username handling (API::Support::GitAccessActor).

Closes #585913 (closed)

How to set up and validate locally

On a Geo GDK pair, from the secondary's Rails console (simulates the exact production path minus sshd — JWT signing, primary auth, actor resolution):

project = Geo::ProjectRepositoryRegistry.synced.first.project
primary_repo = "#{Gitlab::Geo.primary_node.internal_url.chomp('/')}/#{project.full_path}.git"

# SSH certificate format — 403 before this MR, 200 after
Gitlab::Geo::GitSSHProxy.new({ 'gl_id' => 'username-root', 'primary_repo' => primary_repo }).info_refs_upload_pack

# Direct-to-primary flow format — 403 before this MR, 200 after
Gitlab::Geo::GitSSHProxy.new({ 'gl_id' => 'user-1', 'primary_repo' => primary_repo }).info_refs_upload_pack

# Regression check — 200 before and after
Gitlab::Geo::GitSSHProxy.new({ 'gl_id' => "key-#{Key.first.id}", 'primary_repo' => primary_repo }).info_refs_upload_pack

# Invalid identities still rejected with 403
Gitlab::Geo::GitSSHProxy.new({ 'gl_id' => 'username-does-not-exist', 'primary_repo' => primary_repo }).info_refs_upload_pack

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.

Related to #585913 (closed)

Edited by Scott Murray

Merge request reports

Loading
Loading