Allow sshd fast SSH key lookup under SELinux

What does this MR do?

Fixes fast SSH key lookup (AuthorizedKeysCommand) failing on EL 9 and EL 10 with SELinux enforcing. This is the minimal "Part A" split-out requested by @lucus.li in !9541 (comment 3780242022).

Root cause

The AVC denial that breaks gitlab-shell-authorized-keys-check:

type=AVC msg=audit(...): avc: denied { connectto }
  for pid=... comm="gitlab-shell-au"
  scontext=system_u:system_r:sshd_t:s0-s0:c0.c1023
  tcontext=system_u:system_r:unconfined_service_t:s0
  tclass=unix_stream_socket permissive=0

sock_file write on the workhorse socket already passes (that permission was granted in an earlier module). The only missing permission was connectto on the unix_stream_socket class — the IPC-level handshake that actually establishes the connection.

Why no new types or fcontext changes are needed

The workhorse socket is already labelled unconfined_service_t by the system policy. No fcontext relabelling is required. No new SELinux types are introduced. selinux_helper.rb gains one helper method (see 3) but no fcontext logic changes.

Changes

1. files/gitlab-selinux/gitlab-10.5.0-ssh-authorized-keys.te (bumped to 1.1)

This module already holds the other fast-lookup grants (http_cache_port_t name_connect and var_log_t open for sshd_t). Added:

  • allow sshd_t unconfined_service_t:unix_stream_socket connectto; — the core bug fix for EL 9.
  • allow sshd_t var_log_t:file { open append };open was already present; append is added so gitlab-shell.log writes succeed.
  • Two optional {} blocks (raw, no m4 macros — pattern copied from gitlab-19.4.0-sshd-session.te) for sshd_session_t (OpenSSH 9.8+/EL10) and sshd_auth_t (OpenSSH 10+/Fedora 43), each granting:
    • unconfined_service_t:unix_stream_socket connectto
    • http_cache_port_t:tcp_socket name_connect (TCP transport for key lookup, currently missing for these domains)
    • var_log_t:file { open append } (fixes gitlab-shell.log open denial for sshd_session_t on EL10)

2. files/gitlab-selinux/gitlab.te (unified policy, bumped to 1.2.0)

Mirrors exactly the same additions: connectto for all three domains, name_connect and var_log_t { open append } for the sshd_session_t/sshd_auth_t optional blocks. Only rules that were previously absent are added.

3. Reload the module on upgraded hosts (selinux.rb + SELinuxHelper.module_installed_and_current?)

The semodule -i execute resources for gitlab-10.5.0-ssh-authorized-keys and the unified gitlab module were guarded with a name-only check:

not_if "semodule -l | grep -E '^#{module_name}([[:space:]]|$)'"

A host that already has the module installed at an older version would therefore never reload the updated .pp, and the connectto fix would be inert on upgraded hosts.

A version-aware semodule -l grep is not an option: on libsemanage >= 2.4 (EL 8+) semodule -l prints module names only, so such a guard never matches and semodule -i rebuilds the policy on every reconfigure (as observed on AlmaLinux 9 in the review).

Instead, SELinuxHelper.module_installed_and_current?(name, pp_path) runs semodule -E <name> in a temporary directory to extract the installed module's original .pp and byte-compares it with the shipped one under /opt/gitlab/embedded/selinux/:

not_if { SELinuxHelper.module_installed_and_current?(module_name, "#{selinux_policy_dir}/#{module_name}.pp") }
  • Module missing or different from the shipped .pp: semodule -i runs once.
  • Module byte-identical: skipped, so reconfigure stays idempotent.
  • No version strings are duplicated into the recipe; the .te remains the single source of truth. checkmodule output is deterministic, so an unchanged .te never triggers a spurious reinstall.
  • The temporary directory is removed when the check completes; nothing is written to the policy store or /var/opt/gitlab.

All other modules (no policy change in this MR) retain the name-only guard to keep the change targeted.

4. Specs

  • spec/chef/cookbooks/package/libraries/helpers/selinux_helper_spec.rb: unit tests for module_installed_and_current? (identical, differing, missing module, semodule unavailable, missing shipped .pp).
  • spec/chef/cookbooks/gitlab/recipes/selinux_spec.rb and spec/chef_helper.rb: stub the helper and verify that the module is not reinstalled when it matches the shipped package and is reinstalled when it is missing or differs.

Backport notes

gitlab-10.5.0-ssh-authorized-keys.te and gitlab.te exist on all four stable branches (19.0–19.3), so this MR backports cleanly with no prerequisite MRs for the EL 9 fix.

For EL 10 (the sshd_session_t path), the baseline SSH git access additionally requires !9672 (merged) / issue #10048 (closed) on stable branches; that is separately backportable. The EL 9 fix (sshd_t connectto) stands alone.

Manual verification

EL 9 (sshd_t path):

# /etc/ssh/sshd_config snippet
AuthorizedKeysCommand /opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell-authorized-keys-check git %u %k
AuthorizedKeysCommandUser git

# After package upgrade:
sudo gitlab-ctl reconfigure
# Expected: execute[semodule -i /opt/gitlab/embedded/selinux/gitlab-10.5.0-ssh-authorized-keys.pp] action run (once)

sudo gitlab-ctl reconfigure
# Expected: the same resource is skipped (up to date); no policy rebuild

# Installed module matches the shipped package:
cd "$(mktemp -d)" && sudo semodule -E gitlab-10.5.0-ssh-authorized-keys && cmp gitlab-10.5.0-ssh-authorized-keys.pp /opt/gitlab/embedded/selinux/gitlab-10.5.0-ssh-authorized-keys.pp && echo identical

ssh -T git@<host>
# Expected: Welcome to GitLab, @<user>!

sudo ausearch -m avc -ts recent | grep sshd
# Expected: no output (no denials)

EL 10 (sshd_session_t path, requires !9672 (merged) on stable):

cd "$(mktemp -d)" && sudo semodule -E gitlab && cmp gitlab.pp /opt/gitlab/embedded/selinux/gitlab.pp && echo identical
# Expected: identical (unified policy path)

ssh -T git@<host>
# Expected: Welcome to GitLab, @<user>!

sudo ausearch -m avc -ts recent | grep sshd
# Expected: no output

Closes gitlab#438140 (closed)

Checklist

See Definition of done.

For anything in this list which will not be completed, please provide a reason in the MR discussion.

Required

  • MR title and description are up to date, accurate, and descriptive.
  • MR targeting the appropriate branch.
  • Latest Merge Result pipeline is green.
  • When ready for review, MR is labeled workflowready for review per the Distribution MR workflow.

For GitLab team members

If you don't have access to this, the reviewer should trigger these jobs for you during the review process.

  • The manual Trigger:ee-package jobs have a green pipeline running against latest commit.
  • If config/software or config/patches directories are changed, make sure the build-package-on-all-os job within the Trigger:ee-package downstream pipeline succeeded.
  • If you are changing anything SSL related, then the Trigger:package:fips manual job within the Trigger:ee-package downstream pipeline must succeed.
  • If CI configuration is changed, the branch must be pushed to dev.gitlab.org to confirm regular branch builds aren't broken.

Expected (please provide an explanation if not completing)

  • Test plan indicating conditions for success has been posted and passes.
  • Documentation created/updated. N/A — SELinux policy change, no user-facing docs needed.
  • Tests added. selinux_helper_spec.rb and selinux_spec.rb updated.
  • Integration tests added to GitLab QA. N/A — SELinux policy, not testable in QA suite.
  • Equivalent MR/issue for the GitLab Chart opened. N/A — Kubernetes deployments do not use SELinux modules.
  • Validate potential values for new configuration settings. N/A — no new configuration settings.
Edited by João Alexandre Cunha

Merge request reports

Loading
Loading