Loading
Audit failed SSH certificate authentication
What does this MR do and why?
Addresses "Gap 2" in https://gitlab.com/gitlab-org/gitlab/-/work_items/611349#gap-2-failed-authentications-are-not-audited
GitLab now records an audit event when SSH certificate authentication fails. This gives administrators a trail of rejected certificate logins.
- Add the
ssh_certificate_authentication_failedaudit event. It is recorded once, after both the group-level and instance-level CA lookups in/api/v4/internal/authorized_certsfail. A group denial followed by a successful instance fallback is a successful login, so it is not audited. - Recording is gated on the
ssh_certificate_authentication_rate_limitflag, so audit writes only happen alongside the per-IP limit from !263054 (merged). The endpoint is reachable without a GitLab account. A throttled request is rejected before resolution and is not audited. - The event is instance-scoped, authored by an unauthenticated author named after the attempted identity. It records the signing CA fingerprint, the attempted identity, the source IP, and the reason the endpoint already uses for its error:
certificate_not_found,user_not_found,feature_not_available, ornot_enterprise_user. The fingerprint and identity are limited to 255 characters. No other request parameters are recorded. - GitLab Shell sends the SSH client IP in a new optional
check_ipparameter (companion MR). Rails records it if it is a single valid IP address. An IPv6 zone ID (for examplefe80::1%eth0) is stripped first, because the PostgreSQLinettype rejects zone IDs. - If
check_ipis missing (older GitLab Shell versions) or invalid, the audit framework's normal fallback applies. The event then records the request IP, which is GitLab Shell's address. This matches how Git audit events fromgit_audit_eventalready behave. Authn::UnauthenticatedSecurityEventAuditoraccepts optionaladditional_details:andip_address:arguments; existing callers are unchanged. Like its other events, this one includesfailed_login: 'SSH_CERTIFICATE'.- Auditing follows normal audit event licensing, not SSH certificate licensing. Without an audit license, nothing is recorded.
- HTTP responses, and which error wins when both lookups fail, are unchanged. No event is written when the database is read-only (Geo secondary or maintenance mode).
- Auditing is synchronous, like existing failed-login events. Successful authentication returns before the audit code runs.
- Docs: new row in the audit event types page, and
check_ipadded to the internal API docs. No database migration.
When check_ip is missing or invalid, the event records GitLab Shell's IP.
Why
- This is how Git audit events from
git_audit_eventalready behave, so the two stay consistent. - Rails and GitLab Shell versions are pinned together within a release (
GITLAB_SHELL_VERSION). A missingcheck_iponly happens during version skew. - It uses the shared audit framework as it is, with no special handling for this one caller.
References
- https://gitlab.com/gitlab-org/gitlab/-/work_items/611349
- Companion GitLab Shell MR: gitlab-shell!1578 (merged)
Screenshots or screen recordings
No UI changes.
How to set up and validate locally
Prerequisites
- GDK running GitLab EE with an Ultimate license, using
gitlab-sshdwithout PROXY protocol. - GitLab Shell at the version in
GITLAB_SHELL_VERSION(v14.60.0 or later includes the companion MR). No separate Shell branch or feature flag is needed. - A second loopback address for the SSH client, such as
127.0.0.2, so the client IP can be told apart from GitLab Shell's IP. On macOS, add it withsudo ifconfig lo0 alias 127.0.0.2 up. - A group with an SSH certificate authority (CA), an enterprise user of that group, a group member who is not an enterprise user, and a user outside the group.
Validation
- With
ssh_certificate_authentication_rate_limitdisabled, a certificate from an unregistered CA is rejected and no audit event is recorded. - With the flag enabled, a certificate from an unregistered CA is rejected and records an
ssh_certificate_authentication_failedevent with reasoncertificate_not_foundand the SSH client's IP (127.0.0.2). - A group CA certificate for an enterprise user of the group authenticates and records no audit event.
- A group CA certificate for a user outside the group, or for a user that does not exist, is rejected and records reason
user_not_found. - A group CA certificate for a group member who is not an enterprise user is rejected and records reason
not_enterprise_user. - When the group rejects a user but the same CA is also registered as an instance CA, the user authenticates and no audit event is recorded.
- Each recorded event has an unauthenticated author named after the certificate identity, instance scope, the CA fingerprint, the certificate identity, and
failed_login: SSH_CERTIFICATE. - When
check_ipis missing, invalid, a CIDR range, or an array, the event records GitLab Shell's IP (127.0.0.1). - IPv6 values for
check_ipare recorded correctly: a zone ID is stripped (fe80::1%eth0becomesfe80::1), an IPv4-mapped address is unwrapped (::ffff:10.1.2.3becomes10.1.2.3), and a plain address is kept as is. - An identity and fingerprint longer than 255 characters are cut to 255 in the event.
- HTTP responses from
/api/v4/internal/authorized_certsare unchanged for every case above.
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 Fred Reinink