← All rules
SOCLE-CLD-GEN-046// Hardening (misc)mediuminventory state

Ensure that System Accounts Do Not Run a Shell Upon Login

Ensures every non-root system account (UID < 1000) has a non-login shell, denying interactive sessions to service accounts.

Checked against what is installed or registered, packages present/absent, account databases.

A pass proves✓ running now✓ on disk✓ survives rebootthe qualified verdict →
Debian 12CIS 1.1.0Debian 13CIS 1.0.0FedoraRHEL 10 / Rocky 10 / AlmaLinux 10RHEL 8 / Rocky 8 / AlmaLinux 8CIS 4.0.0RHEL 9 / Rocky 9 / AlmaLinux 9CIS 2.0.0Ubuntu 22.04CIS 3.0.0Ubuntu 24.04CIS 1.0.0Ubuntu 26.04
One check, maps to 3 standards

A mapping is a cross-reference to where each standard places this requirement, anchored and cross-validated, not a claim of equivalence. A passing check is evidence toward these references, how to read it.

Why this rule matters

System accounts (UID below 1000: daemon, bin, www-data, mysql, …) exist to own files and run services, not to be logged into. If such an account keeps an interactive login shell (e.g. /bin/bash), it becomes a usable foothold: an attacker who compromises the matching service, or who guesses/cracks the account, can open a real session. Every system account other than root should have a non-login shell (nologin/false) or a purpose-built one (sync/shutdown/halt).

What Pavois checks

Pavois reads each account's UID and its effective login shell (field 7 of the passwd database) and flags any system account (UID < 1000, not root) whose shell is not nologin/false or a benign special shell (sync/shutdown/halt). Evaluating the resolved passwd entry reflects exactly what the system would grant at login, including accounts provisioned by packages.

describe command('awk -F: \'($3<1000 && $1!="root" && $7 !~ /nologin|false|sync|shutdown|halt/){print $1}\' /etc/passwd') do
  its('stdout.strip') { should eq '' }
end

How to verify it is applied

List any system account with an interactive shell:

  • awk -F: '($3<1000 && $1!="root" && $7 !~ /nologin|false|sync|shutdown|halt/){print $1":"$7}' /etc/passwd, expected output is empty.
  • getent passwd <account>, the shell field (last column) should read /usr/sbin/nologin or /bin/false.

Inspect & investigate

Session attempts and shell changes are logged by the auth stack:

  • grep -E 'session opened|chsh|usermod' /var/log/auth.log (Debian/Ubuntu) / /var/log/secure (RHEL), shows any session opened for a system account and any shell modification.
  • getent passwd <account> is the authoritative current-state probe of the assigned shell.

Remediation

No automated harden plan is defined for this rule yet, so it must be applied manually: for each flagged account set a non-login shell with usermod -s /usr/sbin/nologin <account> (use /sbin/nologin on RHEL). Do not touch root, and leave the special sync/shutdown/halt accounts as they are.

Pavois applies this with its own harden engine, the plan below, not a shell script:

commandawk -F: '($3<1000 && $1!="root" && $7 !~ /nologin|false|sync|shutdown|halt/){print $1}' /etc/passwd | while read u; do usermod -s /usr/sbin/nologin "$u" 2>/dev/null; done; true
namenologin-sysaccounts
not_iftest -z "$(awk -F: '($3<1000 && $1!="root" && $7 !~ /nologin|false|sync|shutdown|halt/){print $1}' /etc/passwd)"
resourceexec
pavois harden plan local

where the target is local, a user@host SSH alias, or a container , Docs

Impact & precautions

Risk if not applied: a service account with a real shell is a ready-made foothold for lateral movement and persistence.

Precautions before applying: confirm the account is genuinely non-interactive, some accounts (e.g. a CI or backup user, or a DB account used for scheduled su - <user> -c ... maintenance) legitimately need a shell. Changing the shell of such an account will break those jobs. Verify the owning service still starts and that no cron/systemd unit runs as that user with a login shell before switching it to nologin.

Standards mapping

StandardReferenceTypeVersionConfidence
CIS5.4.2.7, 8.2.2directper OS, see the benchmark tablehigh
NISTAC-6, CM-6, CM-6(a), CM-6(b)supporting800-53 Rev 5 · 800-171 Rev 2 (pinned)medium
PCI DSS8.2.2supporting4.0.1medium

Each reference is a cross-reference anchored in the upstream benchmark and cross-validated against the SCAP Security Guide and ansible-lockdown, not a claim of equivalence. Direct = a prescriptive, line-level requirement; supporting = an abstract control family (NIST) the check provides evidence toward. How to read a mapping.

Sources & references