{
  "id": "CB-UBU-NET-016",
  "standard": "ubuntu",
  "version": "main",
  "url": "https://baselines.cirsec.org/b/ubuntu/CB-UBU-NET-016",
  "json": "https://baselines.cirsec.org/b/ubuntu/CB-UBU-NET-016.json",
  "yaml": "https://baselines.cirsec.org/b/ubuntu/CB-UBU-NET-016.yml",
  "profiles_effective": [
    "core",
    "server",
    "server-hardened",
    "desktop",
    "desktop-hardened"
  ],
  "license": "Text CC BY 4.0, code Apache-2.0",
  "attribution": "CIRSEC Baselines, https://baselines.cirsec.org",
  "control": {
    "id": "CB-UBU-NET-016",
    "title": "Restrict SSH authentication to named accounts with strong methods",
    "finding": {
      "title": "The SSH server allows root logins, weak authentication methods or unlimited guessing",
      "description": "Direct root logins, trust-based or empty-password authentication and generous retry limits make SSH an easy entry point."
    },
    "status": "active",
    "profiles": [
      "core"
    ],
    "risk": {
      "severity": "high"
    },
    "assessment": "automated",
    "tags": [
      "ssh",
      "authentication",
      "root"
    ],
    "applicability": {
      "facts": [
        "openssh-server"
      ]
    },
    "description": "This control sets the OpenSSH server options that decide who may log in and how:\n\n- `PermitRootLogin no`: administrators log in with their own account, then use `sudo`;\n- `PermitEmptyPasswords no`: accounts without a password cannot log in;\n- `HostbasedAuthentication no` and `IgnoreRhosts yes`: no trust based on the client host or on\n  `.rhosts`/`.shosts` files;\n- `GSSAPIAuthentication no`: no Kerberos/GSSAPI logins unless the host is part of such a realm;\n- `PermitUserEnvironment no`: users cannot pass environment variables (such as `LD_PRELOAD`)\n  through `~/.ssh/environment` or `authorized_keys`;\n- `UsePAM yes`: account and session rules of PAM (locking, limits, logging) apply to SSH;\n- `MaxAuthTries` at most {{ ssh_max_auth_tries }} and `LoginGraceTime` between 1 and\n  {{ ssh_login_grace_time }} seconds.\n",
    "rationale": "`root` exists on every system and is the first target of password guessing; logging in with\nnamed accounts also keeps a record of who did what. Host-based trust, empty passwords and user\nsupplied environment variables bypass authentication or the restrictions placed on an account.\nLow retry and grace limits slow down guessing and free unauthenticated connections quickly.\n",
    "impact": "Root can no longer log in over SSH, even with a key: create a named administrator account in\nthe `sudo` group and check that it can log in before applying. Scripts and backup tools that\nconnect as root must use a dedicated account. Hosts joined to a Kerberos realm that use GSSAPI\nlogins must keep `GSSAPIAuthentication yes` (exclude that check through tailoring).\n",
    "notes": "Ubuntu ships `PermitRootLogin prohibit-password` (key-only root logins), `MaxAuthTries 6` and\n`LoginGraceTime 120`. Cloud images add `/etc/ssh/sshd_config.d/60-cloudimg-settings.conf`, and\ncloud-init may write `50-cloud-init.conf`, both setting `PasswordAuthentication`. The fix writes\nthe options in `/etc/ssh/sshd_config.d/00-cirsec.conf`, which is read first and therefore wins\nover those files, since sshd keeps the first value it reads for each option. On 24.04 and later,\nsshd is started by `ssh.socket`: the checks need `/run/sshd`, created when `ssh.service` first\nruns, and report an error before the first connection after boot. `LoginGraceTime 0` (no limit) is not\nfixed automatically. `UsePAM yes` without password authentication still lets PAM check account\nexpiry and locks.\n",
    "setting": {
      "location": "/etc/ssh/sshd_config.d/00-cirsec.conf",
      "default_value": "PermitRootLogin prohibit-password, MaxAuthTries 6, LoginGraceTime 120",
      "recommended_value": "PermitRootLogin no, MaxAuthTries <= {{ ssh_max_auth_tries }}, LoginGraceTime <= {{ ssh_login_grace_time }}"
    },
    "parameters": [
      {
        "name": "ssh_max_auth_tries",
        "type": "integer",
        "description": "Maximum authentication attempts per SSH connection.",
        "default": 4,
        "minimum": 1,
        "maximum": 6
      },
      {
        "name": "ssh_login_grace_time",
        "type": "integer",
        "description": "Maximum time to authenticate after connecting.",
        "unit": "seconds",
        "default": 60,
        "minimum": 1,
        "maximum": 120
      }
    ],
    "audit": {
      "manual": "Run `sshd -T` and check: `permitrootlogin no`, `permitemptypasswords no`,\n`hostbasedauthentication no`, `ignorerhosts yes`, `gssapiauthentication no`,\n`permituserenvironment no`, `usepam yes`, `maxauthtries` at most {{ ssh_max_auth_tries }},\n`logingracetime` between 1 and {{ ssh_login_grace_time }}.\n",
      "checks": [
        {
          "id": "permit-root-login",
          "engine": "sshd-config",
          "description": "PermitRootLogin.",
          "spec": {
            "key": "permitrootlogin",
            "value": "no"
          }
        },
        {
          "id": "permit-empty-passwords",
          "engine": "sshd-config",
          "description": "PermitEmptyPasswords.",
          "spec": {
            "key": "permitemptypasswords",
            "value": "no"
          }
        },
        {
          "id": "hostbased-authentication",
          "engine": "sshd-config",
          "description": "HostbasedAuthentication.",
          "spec": {
            "key": "hostbasedauthentication",
            "value": "no"
          }
        },
        {
          "id": "ignore-rhosts",
          "engine": "sshd-config",
          "description": "IgnoreRhosts.",
          "spec": {
            "key": "ignorerhosts",
            "value": "yes"
          }
        },
        {
          "id": "gssapi-authentication",
          "engine": "sshd-config",
          "description": "GSSAPIAuthentication.",
          "spec": {
            "key": "gssapiauthentication",
            "value": "no"
          }
        },
        {
          "id": "permit-user-environment",
          "engine": "sshd-config",
          "description": "PermitUserEnvironment.",
          "spec": {
            "key": "permituserenvironment",
            "value": "no"
          }
        },
        {
          "id": "use-pam",
          "engine": "sshd-config",
          "description": "UsePAM.",
          "spec": {
            "key": "usepam",
            "value": "yes"
          }
        },
        {
          "id": "max-auth-tries",
          "engine": "sshd-config",
          "description": "MaxAuthTries.",
          "spec": {
            "key": "maxauthtries",
            "operator": "<=",
            "value": "{{ ssh_max_auth_tries }}"
          }
        },
        {
          "id": "login-grace-time",
          "engine": "sshd-config",
          "description": "LoginGraceTime upper bound.",
          "spec": {
            "key": "logingracetime",
            "operator": "<=",
            "value": "{{ ssh_login_grace_time }}"
          }
        },
        {
          "id": "login-grace-time-set",
          "engine": "sshd-config",
          "description": "LoginGraceTime is not 0 (unlimited).",
          "spec": {
            "key": "logingracetime",
            "operator": ">=",
            "value": 1
          }
        }
      ]
    },
    "remediation": {
      "manual": "1. Make sure a named account in the `sudo` group can log in with SSH.\n2. Create or edit `/etc/ssh/sshd_config.d/00-cirsec.conf`:\n   ```\n   PermitRootLogin no\n   PermitEmptyPasswords no\n   HostbasedAuthentication no\n   IgnoreRhosts yes\n   GSSAPIAuthentication no\n   PermitUserEnvironment no\n   UsePAM yes\n   MaxAuthTries {{ ssh_max_auth_tries }}\n   LoginGraceTime {{ ssh_login_grace_time }}\n   ```\n3. Check with `sshd -t`, run `systemctl reload ssh`, and test a new connection before closing\n   the current one.\n",
      "effects": {
        "restart": "service",
        "services": [
          "ssh"
        ],
        "disruption": "low",
        "lockout_risk": true
      },
      "rollback": "Remove the lines from `/etc/ssh/sshd_config.d/00-cirsec.conf` and run `systemctl reload ssh`.\n",
      "scripts": [
        {
          "id": "permit-root-login",
          "from_check": "permit-root-login"
        },
        {
          "id": "permit-empty-passwords",
          "from_check": "permit-empty-passwords"
        },
        {
          "id": "hostbased-authentication",
          "from_check": "hostbased-authentication"
        },
        {
          "id": "ignore-rhosts",
          "from_check": "ignore-rhosts"
        },
        {
          "id": "gssapi-authentication",
          "from_check": "gssapi-authentication"
        },
        {
          "id": "permit-user-environment",
          "from_check": "permit-user-environment"
        },
        {
          "id": "use-pam",
          "from_check": "use-pam"
        },
        {
          "id": "max-auth-tries",
          "from_check": "max-auth-tries"
        },
        {
          "id": "login-grace-time",
          "from_check": "login-grace-time"
        }
      ]
    },
    "references": [
      {
        "title": "sshd_config(5), Ubuntu manual pages",
        "url": "https://manpages.ubuntu.com/manpages/noble/man5/sshd_config.5.html",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      },
      {
        "title": "ANSSI-BP-028 v2.0 - Recommandations de configuration d'un système GNU/Linux (R33)",
        "url": "https://messervices.cyber.gouv.fr/documents-guides/fr_np_linux_configuration-v2.0.pdf",
        "publisher": "ANSSI",
        "kind": "guide",
        "accessed": "2026-10-19"
      },
      {
        "title": "NIST IR 7966 - Security of Interactive and Automated Access Management Using Secure Shell (SSH)",
        "url": "https://csrc.nist.gov/pubs/ir/7966/final",
        "publisher": "NIST",
        "kind": "guide",
        "accessed": "2026-10-19"
      }
    ],
    "mappings": [
      {
        "framework": "anssi-linux",
        "framework_version": "2.0",
        "id": "R33",
        "relation": "partial",
        "note": "R33 asks for administration actions to be attributable; forbidding direct root logins over SSH is part of it."
      },
      {
        "framework": "nist-800-53r5",
        "framework_version": "5.1.1",
        "id": "AC-17",
        "relation": "partial"
      },
      {
        "framework": "nist-800-53r5",
        "framework_version": "5.1.1",
        "id": "AC-7",
        "relation": "related"
      }
    ],
    "threats": [
      {
        "framework": "mitre-attack",
        "id": "T1110"
      },
      {
        "framework": "mitre-attack",
        "id": "T1021.004"
      },
      {
        "framework": "cwe",
        "id": "CWE-307"
      }
    ],
    "related_controls": [
      {
        "id": "CB-UBU-AUTH-011",
        "type": "related"
      }
    ],
    "authors": [
      {
        "name": "Alice",
        "github": "Sn0wAlice"
      }
    ],
    "verified": [
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "20.04"
        ],
        "environment": "Proxmox VM, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic, BIOS boot",
        "notes": "Root key-only login, MaxAuthTries 6 and LoginGraceTime 120 reported; generated drop-in fixes in 00-cirsec.conf; root login refused, SSH by key of a non-root user accepted on localhost and port 22 reachable from the lab host after the fix and after reboot."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "22.04"
        ],
        "environment": "Proxmox VM, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic, BIOS boot",
        "notes": "Root key-only login, MaxAuthTries 6 and LoginGraceTime 120 reported; generated drop-in fixes in 00-cirsec.conf; root login refused, SSH by key of a non-root user accepted on localhost and port 22 reachable from the lab host after the fix and after reboot."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "24.04"
        ],
        "environment": "Proxmox VM, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic, BIOS boot",
        "notes": "Root key-only login, MaxAuthTries 6 and LoginGraceTime 120 reported; generated drop-in fixes in 00-cirsec.conf; root login refused, SSH by key of a non-root user accepted on localhost and port 22 reachable from the lab host after the fix and after reboot."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "26.04"
        ],
        "environment": "Proxmox VM, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic, BIOS boot",
        "notes": "Root key-only login, MaxAuthTries 6 and LoginGraceTime 120 reported; generated drop-in fixes in 00-cirsec.conf; root login refused, SSH by key of a non-root user accepted on localhost and port 22 reachable from the lab host after the fix and after reboot."
      }
    ],
    "introduced_in": "0.1.0",
    "created": "2026-10-19",
    "updated": "2026-10-19"
  }
}