Lock Root Logins to Trusted Terminals with pam_securetty
pam_securetty stops root logging in from anywhere that isn't on an explicit allow-list, so a stolen password alone can't get root in over SSH. Non-root authentication is untouched. You'll also check the module's console exception and keep a rollback copy of the PAM configuration. Allow about 20 minutes, plus time to arrange an already-open root-capable session for recovery.
The route
Jump straight to the step you need, or tick off Done means at the end.
This guide describes the installed Linux-PAM pam_securetty module from package version 1.5.3-5ubuntu5.7. The local manual page is dated 7 May 2023. PAM stacks vary by distribution and service, so treat the example as a controlled pattern to adapt after inspecting your own files.
Security warning
A bad PAM edit can lock root out of a service or console. Do not test this over your only SSH session, and do not close an existing administrative session until a separate login has been verified. Reading files below needs no special privileges; editing PAM configuration, creating the securetty file and changing its ownership or mode all require them.
1. Check the module and the target PAM service
Confirm the module exists and identify the service whose root logins you intend to restrict. This module only provides the auth type, and the application has to supply the PAM_TTY value correctly for it to work at all:
$ dpkg-query -W -f='${Package} ${Version}\n' libpam-modules:amd64
libpam-modules 1.5.3-5ubuntu5.7
$ ls -l /lib/x86_64-linux-gnu/security/pam_securetty.so
-rw-r--r-- 1 root root ... /lib/x86_64-linux-gnu/security/pam_securetty.so
$ sed -n '1,120p' /etc/pam.d/login
The file path can differ on another architecture. If the module is missing, stop and install or repair the package through your normal system-management process; do not write a guessed module path into a PAM file.
Checkpoint
Decide whether the rule belongs in /etc/pam.d/login, a display-manager file, or another service file. Do not add it to every PAM service by copying a line blindly, and remember that a service which doesn't set PAM_TTY correctly can't be evaluated reliably by this module.
2. Inspect the secure terminal list
pam_securetty first checks /etc/securetty. If that file is absent and the module was built with vendor-directory support, it falls back to /securetty. Each entry names a terminal device without the /dev/ prefix. On this machine both paths are absent, so root logins have no file-based terminal allow-list at all:
$ for file in /etc/securetty /securetty; do
if test -e "$file"; then
ls -l "$file"
sed -n '1,120p' "$file"
else
printf '%s: absent\n' "$file"
fi
done
/etc/securetty: absent
/securetty: absent
/dev/tty1becomes the entrytty1./dev/ttyS0(a serial device) becomesttyS0.- An SSH session normally gets a pseudo-terminal name such as
pts/0, but allowing pseudo-terminals defeats the point of limiting root to physical or otherwise approved devices. Decide that policy on purpose, not by accident.
Common entries are host-specific: use the names reported by your own console and login setup rather than copying a list from somewhere else.
3. Create a minimal allow-list
Preserve any existing file first, then create the list with root privileges. Replace the sample terminal with a device you have deliberately approved. This command changes persistent authentication policy, so read the here-document before pressing Enter:
$ sudo install -o root -g root -m 0600 /etc/securetty /etc/securetty.bak 2>/dev/null || true
$ sudo sh -c 'printf "%s\n" tty1 > /etc/securetty'
$ sudo chown root:root /etc/securetty
$ sudo chmod 0600 /etc/securetty
$ sudo sed -n '1,40p' /etc/securetty
tty1
pam_securetty rejects the file outright if it isn't a plain file or is world-writable, so the restrictive mode above isn't paranoia, it's a requirement. The backup command may print nothing when no file existed to back up; in that case there's simply nothing to restore.
Checkpoint
Verify the metadata before connecting the PAM rule:
$ stat -c '%A %U:%G %n' /etc/securetty
-rw------- root:root /etc/securetty
4. Add the rule before sufficient authentication
Make a dated backup of the target PAM service, then edit it as root. The canonical order is a required pam_securetty line before any sufficient authentication method. Keep the existing stack and insert only the line that matches your service:
$ sudo cp -a /etc/pam.d/login /etc/pam.d/login.before-pam-securetty
$ sudoedit /etc/pam.d/login
Place this line before the first sufficient authentication line, and before the normal password module if that's how the service is structured:
auth required pam_securetty.so
To request diagnostics while testing, add the documented option:
auth required pam_securetty.so debug
debugis for temporary use while consulting the service's logging destination. It does not make an unauthorised terminal acceptable.noconsoleis a separate, stricter choice: it stops automatic acceptance of the kernel console unless that device is also listed in the securetty file. By default the module allows root on the console named by the kernelconsole=setting and on terminals listed by/sys/class/tty/console/active; addnoconsoleif your policy needs the file to be the complete allow-list.
auth required pam_securetty.so noconsole
Warning
Do not select noconsole without first confirming that your approved physical console is present in /etc/securetty. Get this wrong and your recovery path disappears the next time someone tries to log in.
5. Test from a second session
Keep the first administrative session open. From the approved terminal, test a root login using the exact service you changed. From a deliberately unapproved terminal, confirm root gets rejected. Do not paste a production password into a command, and do not run a password-testing loop.
A successful module decision lets PAM continue authenticating root on an accepted device; a rejected decision returns an authentication error. Later PAM modules, account policy or the service itself can still affect what you see, so keep the full service log handy when diagnosing a failure:
$ sudo sshd -t
... no output on success ...
$ sudo journalctl -b --no-pager | grep -i securetty
sshd -t only validates SSH daemon syntax, it does not test a PAM login. Use whatever non-destructive validation your service provides. The journal command is a read-only diagnostic and may show nothing if the module isn't logging at the current level.
6. Recover without guessing
Recovery
If an approved login fails, use the still-open administrative session or the provider's console. Restore the PAM file first, then the terminal list if needed:
$ sudo cp -a /etc/pam.d/login.before-pam-securetty /etc/pam.d/login
$ sudo cp -a /etc/securetty.bak /etc/securetty
$ sudo chmod 0600 /etc/securetty
$ sudo chown root:root /etc/securetty
If /etc/securetty.bak never existed because the original file was absent, don't copy it. Remove the new list only after double-checking the path is exactly the file you mean to remove:
$ sudo test -f /etc/securetty.bak || sudo rm -- /etc/securetty
That removal is irreversible unless another backup exists. If the module reports a service error, check the securetty path is readable, a normal file, and not world-writable. If root is rejected when it shouldn't be, check the PAM_TTY value and the exact terminal name before you touch the policy again.
Done means
- Service identified. You confirmed the exact PAM service and the installed Linux-PAM package version.
- Terminal list secured. Terminal names were chosen deliberately and the securetty file verified as root-owned and not world-writable.
- Rule placed correctly.
pam_securetty.sois anauth requiredrule before anysufficientauthentication method. - Console exception understood. You know the default kernel-console exception and chose
noconsoleonly when the file needs to be authoritative. - Second session verified first. A second session confirmed the intended result before the original administrative session was closed.
- Rollback kept. A tested rollback copy of both the PAM service file and the terminal list still exists.