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 nokeysrow 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 sendsuser-{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_packMR 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)