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 usesudo;PermitEmptyPasswords no: accounts without a password cannot log in;HostbasedAuthentication noandIgnoreRhosts yes: no trust based on the client host or on.rhosts/.shostsfiles;GSSAPIAuthentication no: no Kerberos/GSSAPI logins unless the host is part of such a realm;PermitUserEnvironment no: users cannot pass environment variables (such asLD_PRELOAD) through~/.ssh/environmentorauthorized_keys;UsePAM yes: account and session rules of PAM (locking, limits, logging) apply to SSH;MaxAuthTriesat most {{ ssh_max_auth_tries }} andLoginGraceTimebetween 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 |
| Default | PermitRootLogin prohibit-password, MaxAuthTries 6, LoginGraceTime 120 |
| Recommended | PermitRootLogin no, MaxAuthTries <= {{ ssh_max_auth_tries }}, LoginGraceTime <= {{ ssh_login_grace_time }} |
Parameters
| Parameter | Core | Server | Server hardened | Desktop | Desktop hardened | Bounds |
|---|---|---|---|---|---|---|
ssh_max_auth_triesinteger. Maximum authentication attempts per SSH connection. | 4 | 4 | 4 | 4 | 4 | 1 to 6 |
ssh_login_grace_timeinteger, seconds. Maximum time to authenticate after connecting. | 60 | 60 | 60 | 60 | 60 | 1 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
- Make sure a named account in the
sudogroup can log in with SSH. - 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 }} - Check with
sshd -t, runsystemctl reload ssh, and test a new connection before closing the current one.
| Restart | service: ssh |
| Disruption | low |
| Lockout risk | yes: 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
- sshd_config(5), Debian manual pages Debian, vendor-doc, accessed 2026-10-18
- ANSSI-BP-028 v2.0 - Recommandations de configuration d'un système GNU/Linux (R33) ANSSI, guide, accessed 2026-10-18
- NIST IR 7966 - Security of Interactive and Automated Access Management Using Secure Shell (SSH) NIST, guide, accessed 2026-10-18
Mappings
| Framework | ID | Relation | Note |
|---|---|---|---|
| ANSSI-BP-028 - Recommandations de configuration d'un système GNU/Linux2.0 | R33 | partial | R33 asks for administration actions to be attributable; forbidding direct root logins over SSH is part of it. |
| NIST SP 800-53 Rev. 55.1.1 | AC-17 | partial | |
| NIST SP 800-53 Rev. 55.1.1 | AC-7 | related |
How this control relates to the other framework's: equivalent, stricter, looser, partial or related.
Threats mitigated
Related controls
- related
CB-DEB-AUTH-011Lock accounts after repeated failed logins
People and lifecycle
| Authors |
|
| Verified | 2026-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. |
| Verified | 2026-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 in | v0.1.0 |
| Created, updated | 2026-10-18, 2026-10-18 |
| Translations | fr |