Include SSH key details in Git operation audit events

Related to #599902 (closed) and supersedes the draft exploration !237337 (closed)

What does this MR do and why?

  • Adds the SSH key used for authentication to the streamed repository_git_operation audit event message: gl_key_type (key for user SSH keys, deploy_key for deploy keys) and gl_key_id.

  • This lets organizations relying on SAML/group membership with standard GitLab.com accounts correlate Git-over-SSH activity with a specific credential for compliance and incident response. It is the alternative agreed in the issue, instead of surfacing add_ssh_key/remove_ssh_key at group scope for non-enterprise users, which would expose personal credential data without administrative ownership of the account. See:

  • Both event emitters merge GitAccessActor#key_details into the message: API::Internal::Shellhorse (POST /internal/shellhorse/git_audit_event) and the legacy path in API::Internal::Base (POST /internal/allowed, used when the log_git_streaming_audit_events feature flag is disabled). See sequence diagram.

  • key_details returns {} when the actor is not key-backed (for example, HTTP with username/password or a deploy token), so those events are unchanged. This behaviour is already covered by unit specs in spec/lib/api/support/git_access_actor_spec.rb.

  • No gitlab-shell changes are needed. Rails has resolved key_id into the acting key since !37289 (merged), and gitlab-shell sends key_id to the audit endpoint since v14.49.0 (gitlab-shell!1411 (merged)). master pins GITLAB_SHELL_VERSION 14.56.1. Older gitlab-shell omits key_id, and those events simply carry no key fields.

Documentation Review

See !244631 (merged)

Screenshots or screen recordings

After

How to set up and validate locally

repository_git_operation is a streaming-only audit event (not saved to the database), so it must be verified at a streaming destination, not in the Audit events UI (see original git audit streaming events implementation MR).

NB: the steps below use this concrete setup, adjust names to your own GDK: top-level group enterprise-users, project enterprise-users/ent_proj, test user aud.

  1. Update gitlab-shell. Your local gitlab-shell must be v14.49.0 or later to send key_id to the audit endpoint (added in gitlab-shell!1411 (merged); see gitlab-shell CHANGELOG). Older versions send only username, and the event will show no key fields:

    cd <gdk-root>   # e.g. ~/gitlab-development-kit
    make gitlab-shell-update
    gdk restart sshd
    cat gitlab-shell/VERSION   # must print >= 14.49.0 (14.56.1 at time of writing)
  2. Enable SSH in GDK (GDK SSH how-to; gitlab-sshd listens on 127.0.0.1:2222 by default):

    gdk config set sshd.enabled true
    gdk reconfigure && gdk restart
  3. Simulate SaaS (docs). This matches the GitLab.com scenario from the issue: set GITLAB_SIMULATE_SAAS=1 (for example, via gdk.yml) and restart GDK. Then enable Admin > Settings > General > Account and limit > Allow use of licensed EE features, and in Admin > Overview > Groups, edit the top-level group enterprise-users and set its Plan to Ultimate.

  4. Set up the streaming destination. Create a request bin, for example at https://requestcatcher.com, and keep its inspection page open (only use this with synthetic GDK test data). Then in the top-level group enterprise-users, go to Secure > Audit events > Streams and add an HTTP streaming destination pointing at the request bin URL. Events stream to the root group's destinations, so this must be on the top-level group, not a subgroup or the project.

  5. Create the test user and their SSH key. Create user aud, add them with Developer access to enterprise-users/ent_proj. Generate a dedicated keypair. A fresh key is required, because GitLab rejects public keys already registered to another account, such as root's:

    ssh-keygen -t ed25519 -f ~/.ssh/gdk_aud_key -N ""
    cat ~/.ssh/gdk_aud_key.pub   # copy the whole output line (ssh-ed25519 AAAA... comment)

    Signed in as aud, paste it into the Key field at User Settings > SSH Keys and select Add key.

  6. Positive case (user SSH key): clone (or push) over SSH, forcing the aud key so the SSH agent cannot offer a key registered to another user (IdentitiesOnly=yes, see use different keys for different repositories, applied for a single invocation with git -c). Accept the host key fingerprint prompt on first connect. The rm -rf ent_proj makes the step easy to re-run:

    rm -rf ent_proj
    git -c core.sshCommand="ssh -o IdentitiesOnly=yes -i ~/.ssh/gdk_aud_key" clone ssh://git@127.0.0.1:2222/enterprise-users/ent_proj.git

    Expected: the request bin page shows an incoming repository_git_operation request whose details.custom_message includes "gl_key_type": "key" and a "gl_key_id".

  7. Positive case (deploy key): a deploy key is also a keypair generated on your machine. The UI only stores the public half. Generate one and add it to the project at Settings > Repository > Deploy keys (paste the .pub line):

    ssh-keygen -t ed25519 -f ~/.ssh/gdk_deploy_key -N ""
    cat ~/.ssh/gdk_deploy_key.pub   # paste into Settings > Repository > Deploy keys

    Clone with that key:

    rm -rf ent_proj
    git -c core.sshCommand="ssh -o IdentitiesOnly=yes -i ~/.ssh/gdk_deploy_key" clone ssh://git@127.0.0.1:2222/enterprise-users/ent_proj.git

    Expected: the request bin page shows an incoming request whose details.custom_message includes "gl_key_type": "deploy_key" and a "gl_key_id".

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 Hakeem Abdul-Razak

Merge request reports

Loading