Debian GNU/Linux · Network

Restrict SSH authentication to named accounts with strong methods

When it fails: The SSH server allows root logins, weak authentication methods or unlimited guessing. Direct root logins, trust-based or empty-password authentication and generous retry limits make SSH an easy entry point.

CB-DEB-NET-016highactiveautomatedautomated fix, lockout riskprofile core

As released in v0.1.0 beta · pre-release. Current state.

Description

This control sets the OpenSSH server options that decide who may log in and how:

  • PermitRootLogin no: administrators log in with their own account, then use sudo;
  • PermitEmptyPasswords no: accounts without a password cannot log in;
  • HostbasedAuthentication no and IgnoreRhosts yes: no trust based on the client host or on .rhosts/.shosts files;
  • GSSAPIAuthentication no: no Kerberos/GSSAPI logins unless the host is part of such a realm;
  • PermitUserEnvironment no: users cannot pass environment variables (such as LD_PRELOAD) through ~/.ssh/environment or authorized_keys;
  • UsePAM yes: account and session rules of PAM (locking, limits, logging) apply to SSH;
  • MaxAuthTries at most {{ ssh_max_auth_tries }} and LoginGraceTime between 1 and {{ ssh_login_grace_time }} seconds.

Debian ships PermitRootLogin prohibit-password (key-only root logins), MaxAuthTries 6 and LoginGraceTime 120. The fix writes the options in /etc/ssh/sshd_config.d/00-cirsec.conf, which is read first and therefore wins over later files. LoginGraceTime 0 (no limit) is not fixed automatically. UsePAM yes without password authentication still lets PAM check account expiry and locks.

Rationale

root exists on every system and is the first target of password guessing; logging in with named accounts also keeps a record of who did what. Host-based trust, empty passwords and user supplied environment variables bypass authentication or the restrictions placed on an account. Low retry and grace limits slow down guessing and free unauthenticated connections quickly.

Impact

Root can no longer log in over SSH, even with a key: create a named administrator account in the sudo group and check that it can log in before applying. Scripts and backup tools that connect as root must use a dedicated account. Hosts joined to a Kerberos realm that use GSSAPI logins must keep GSSAPIAuthentication yes (exclude that check through tailoring).

Applies when

Detected: when: OpenSSH server is installed A scanner reports the control not applicable when one is false.

Setting

Location/etc/ssh/sshd_config.d/00-cirsec.conf
DefaultPermitRootLogin prohibit-password, MaxAuthTries 6, LoginGraceTime 120
RecommendedPermitRootLogin no, MaxAuthTries <= {{ ssh_max_auth_tries }}, LoginGraceTime <= {{ ssh_login_grace_time }}

Parameters

ParameterCoreServerServer hardenedDesktopDesktop hardenedBounds
ssh_max_auth_triesinteger. Maximum authentication attempts per SSH connection.444441 to 6
ssh_login_grace_timeinteger, seconds. Maximum time to authenticate after connecting.60606060601 to 120

The value used is your override (tailoring file), else the profile's, else the one of the profile it extends, else the default. Bounds limit what an override may set.

Audit

Run sshd -T and check: permitrootlogin no, permitemptypasswords no, hostbasedauthentication no, ignorerhosts yes, gssapiauthentication no, permituserenvironment no, usepam yes, maxauthtries at most {{ ssh_max_auth_tries }}, logingracetime between 1 and {{ ssh_login_grace_time }}.

Automated: every check must pass. Scanners report each check by its id.

permit-root-login

PermitRootLogin.

key: permitrootlogin
value: no

permit-empty-passwords

PermitEmptyPasswords.

key: permitemptypasswords
value: no

hostbased-authentication

HostbasedAuthentication.

key: hostbasedauthentication
value: no

ignore-rhosts

IgnoreRhosts.

key: ignorerhosts
value: yes

gssapi-authentication

GSSAPIAuthentication.

key: gssapiauthentication
value: no

permit-user-environment

PermitUserEnvironment.

key: permituserenvironment
value: no

use-pam

UsePAM.

key: usepam
value: yes

max-auth-tries

MaxAuthTries.

key: maxauthtries
operator: <=
value: '{{ ssh_max_auth_tries }}'

login-grace-time

LoginGraceTime upper bound.

key: logingracetime
operator: <=
value: '{{ ssh_login_grace_time }}'

login-grace-time-set

LoginGraceTime is not 0 (unlimited).

key: logingracetime
operator: '>='
value: 1

Remediation

  1. Make sure a named account in the sudo group can log in with SSH.
  2. Create or edit /etc/ssh/sshd_config.d/00-cirsec.conf:
    PermitRootLogin no
    PermitEmptyPasswords no
    HostbasedAuthentication no
    IgnoreRhosts yes
    GSSAPIAuthentication no
    PermitUserEnvironment no
    UsePAM yes
    MaxAuthTries {{ ssh_max_auth_tries }}
    LoginGraceTime {{ ssh_login_grace_time }}
    
  3. Check with sshd -t, run systemctl reload ssh, and test a new connection before closing the current one.
Restartservice: ssh
Disruptionlow
Lockout riskyes: can cut off administrative access. Off by default in generated scripts and playbooks, opt in through a tailoring file.

Rollback

Remove the lines from /etc/ssh/sshd_config.d/00-cirsec.conf and run systemctl reload ssh.

permit-root-login

Generated from check permit-root-login: the engine writes the fix for every output, it sets the expected value and the check runs again after.

permit-empty-passwords

Generated from check permit-empty-passwords: the engine writes the fix for every output, it sets the expected value and the check runs again after.

hostbased-authentication

Generated from check hostbased-authentication: the engine writes the fix for every output, it sets the expected value and the check runs again after.

ignore-rhosts

Generated from check ignore-rhosts: the engine writes the fix for every output, it sets the expected value and the check runs again after.

gssapi-authentication

Generated from check gssapi-authentication: the engine writes the fix for every output, it sets the expected value and the check runs again after.

permit-user-environment

Generated from check permit-user-environment: the engine writes the fix for every output, it sets the expected value and the check runs again after.

use-pam

Generated from check use-pam: the engine writes the fix for every output, it sets the expected value and the check runs again after.

max-auth-tries

Generated from check max-auth-tries: the engine writes the fix for every output, it sets the expected value and the check runs again after.

login-grace-time

Generated from check login-grace-time: the engine writes the fix for every output, it sets the expected value and the check runs again after.

Each release turns these fixes into remediate.sh and an Ansible role per profile: dry run by default, a fix runs only when its check fails.

References

Mappings

FrameworkIDRelationNote
ANSSI-BP-028 - Recommandations de configuration d'un système GNU/Linux2.0R33partialR33 asks for administration actions to be attributable; forbidding direct root logins over SSH is part of it.
NIST SP 800-53 Rev. 55.1.1AC-17partial
NIST SP 800-53 Rev. 55.1.1AC-7related

How this control relates to the other framework's: equivalent, stricter, looser, partial or related.

Threats mitigated

People and lifecycle

Authors
Verified2026-10-18 by Sn0wAlice on 12, Proxmox VM 921, Debian 12 genericcloud image, kernel 6.1.0-54-cloud-amd64. Generated sshd drop-in fixes; key login of a non-root test user accepted before and after the fix and after reboot.
Verified2026-10-18 by Sn0wAlice on 13, Proxmox VM 931, Debian 13 genericcloud image, kernel 6.12.113+deb13-cloud-amd64. Generated sshd drop-in fixes; key login of a non-root test user accepted before and after the fix and after reboot.
Introduced inv0.1.0
Created, updated2026-10-18, 2026-10-18
Translationsfr