mediumaccess-controlverified ✓rollback-safe
Set permissions on SSH private host keys
ssh-private-key-permissions · RHEL ≥ 8 · 1 impl
Description
SSH private host key files must have restrictive permissions to prevent unauthorized read access. The private keys under /etc/ssh/ are used to prove the server's identity to connecting clients and must be protected from disclosure.
Rationale
If an attacker can read a server's SSH private host key, they can impersonate the server in man-in-the-middle attacks, intercepting all traffic between clients and the legitimate server. Restricting file permissions to owner root with group ssh_keys ensures only the SSH daemon's privilege separation process can access the keys.
Check → Remediate
Checkcommand
# STIG (rhel8/9) requires mode 0640-or-less-permissive; CIS accepts either
# root:ssh_keys with mode <=0640 or root:root with mode <=0600. Enforce the
# coupled ownership/mode logic that satisfies both (no other access; owner
# root with no execute; group read only permitted for the ssh_keys group).
fail=0
for k in /etc/ssh/ssh_host_*_key; do
[ -e "$k" ] || continue
owner=$(stat -c '%U' "$k" 2>/dev/null)
group=$(stat -c '%G' "$k" 2>/dev/null)
perm=$(stat -c '%a' "$k" 2>/dev/null)
# Take the last three octal digits (owner/group/other), ignoring any
# setuid/setgid/sticky high bit. Prepend zeros then trim — never run the
# value through printf %d, which would reinterpret e.g. "040" as octal.
m=$(printf '00%s' "$perm" | sed 's/.*\(...\)$/\1/')
o=$(printf '%s' "$m" | cut -c1); g=$(printf '%s' "$m" | cut -c2); t=$(printf '%s' "$m" | cut -c3)
if [ "$owner" != "root" ]; then echo "FAIL: $k owner=$owner (must be root)"; fail=1; continue; fi
if [ "$t" != "0" ]; then echo "FAIL: $k is accessible by others (mode $perm)"; fail=1; continue; fi
case "$o" in 0|4|6) ;; *) echo "FAIL: $k owner has execute (mode $perm)"; fail=1; continue ;; esac
if [ "$group" = "ssh_keys" ]; then
case "$g" in 0|4) ;; *) echo "FAIL: $k group ssh_keys but mode $perm > 0640"; fail=1 ;; esac
elif [ "$group" = "root" ]; then
case "$g" in 0) ;; *) echo "FAIL: $k group root but mode $perm > 0600"; fail=1 ;; esac
else
echo "FAIL: $k group=$group (must be root or ssh_keys)"; fail=1
fi
done
if [ "$fail" -eq 0 ]; then
echo "OK: all SSH private host keys have compliant ownership and mode"
exit 0
fi
exit 1
- expected_exit:
- 0
Remediatefile_permissions
- path:
- /etc/ssh/ssh_host_*_key
- owner:
- root
- group:
- ssh_keys
- mode:
- 0640
- glob:
- ssh_host_*_key
Framework references
CIS
rhel10 5.1.2rhel8 5.1.4rhel9 5.1.2
STIG
V-258000 / RHEL-09-255120V-230287 / RHEL-08-010490
NIST 800-53
AC-6SC-28
Live verification
rhel10:checkrhel8:checkrhel9:check
#ssh#file-permissions#host-keys