3

I mean not just using PermitRootLogin no. If not, how can it be achieved with minimal effort and without impacting access for other users, both local and remote, who may have the credentials to log in as root via SSH if they have logged in as regular users? The PermitRootLogin no setting is already in place.

I also don't want to create new users or groups, or apply group restrictions. What other options are available besides these?

I found these online, but they could disrupt normal operation if the user or others are locally on the machine.

sudo configuration: "/etc/sudoers":
username ALL=(ALL:ALL) ALL, !/bin/su, !/usr/bin/sudo -u root

su configuration in pam: "/etc/pam.d/su":
auth required pam_wheel.so use_uid

login configuration in pam: "/etc/pam.d/login":
auth required pam_wheel.so use_uid

I simply want to prevent root login after a user has logged in normally. However, if they are physically present on the machine, they should be able to log in as root after logging in with their own account.

Is there a setting in sshd_config or a very simple solution for this, without the given requirements above?

3
  • 1
    The simplest solution is to follow the recommended practice: Nobody logs in directly as the root user. Everyone logs in as their own user and uses sudo to run commands as the root user. Commented Aug 1, 2024 at 11:51
  • 2
    Your terminology is very confusing. sudo and su are not "root login". It's a normal login followed by executing commands as root, and there are ways to restrict that (/etc/sudoers allows very fine-grained control, so it's generally best to prohibit su entirely). Commented Aug 1, 2024 at 14:24
  • 1
    Your requirements are not clear. It sounds like you don't want to prevent login as root "after" they have "logged in normally" (this "after" condition would apply if they logged out and later tried to login from somewhere remote), but prevent escalation to root (via su/sudo? also ssh to localhost?) from a local console login session or similar. Commented Aug 1, 2024 at 16:34

2 Answers 2

13

(Originally the question was stricter: is this possible using sshd_config?, but the OP has since then edited it to allow other "very simple" solutions.)

No, it is not possible.

Once a regular user has been allowed a normal shell session, the sshd_config configuration cannot place any restrictions to the commands used in that session.

To achieve what you want, you would need something that would prevent switching to root from succeeding if the user is on a SSH session. Simply requiring the session to be based on a non-pseudo TTY device (i.e. a serial port or a virtual console, as might be achieved with pam_securetty) goes too far, as it will block root access from local GUI logins too.

This would require something like a PAM module for blocking sudo and su authentication (and any other PAM-based user switching methods, if you have them), and something else to block polkit-based user switching methods like pkexec. The sshd_config has nothing to do with either of those mechanisms, so the answer to your question must be "no".

The sudo configuration line you found on the internet:

username ALL=(ALL:ALL) ALL, !/bin/su, !/usr/bin/sudo -u root

has an error: it would block sudo sudo -u root [...], but would allow plain sudo -i, sudo bash or any number of other means of achieving root.

In general, any attempt to "allow all commands except (a limited number of 'bad' commands)" is likely to be trivially circumventable, as regular shell users can normally make their own copies of standard command binaries under names of their own choosing, allowing the execution of them under sudo by the username ALL=(ALL:ALL) ALL part of the configuration.

The most straightforward way to get what you want would be to lock root access behind some form of physical authentication token, such as a smart card or Yubikey. A remote user cannot possibly achieve the effect of plugging in an authentication token to the physical system. It would also provide a second authentication factor, which would significantly increase the security of your root access.

4
  • Apart from the very valid issues you point out about that sudoers configuration not blocking sudo -i, or sudo -s or sudo sh etc, isn't it also entirely pointless to being with? I mean, you need to explicitly give a user sudo access, so if you don't want users to have said access, then you wouldn't add them to wheel or sudo or whichever group you have configured for sudo access, or just not configure such a group at all. Is there any scenario at all where ALL, !/usr/bin/sudo would be useful? You'd need to explicitly give sudo access only to then remove it. Commented Jul 31, 2024 at 22:34
  • 1
    @terdon Indeed, calling sudo within sudo is totally pointless... unless some admin has been silly enough to have configured a "maze" of authentications: "first switch to user A, then from there you can switch to user B..." The ALL, !/usr/bin/sudo is just one special case of the allow all except some bad commands, and I explained why any such sudoers configuration is a bad idea. Commented Aug 1, 2024 at 6:37
  • Whilst it's very difficult for a remote user to "achieve the effect of plugging in an authentication token to the physical system", they may well be in a position to wait for such an event to happen (using inotify perhaps, or simple polling) and to take advantage of it as soon as it happens. So that's not a panacea either. Commented Aug 1, 2024 at 6:53
  • 1
    @TobySpeight Sure... but that is a significantly more complex attack, and there is a standard countermeasure of requiring a token-specific PIN (or any other knowledge-based authentication factor) to be entered within the session that is going to get the privileges. Also, modern systems may only allow locally-logged-in users to access dynamically plugged-in devices such as authentication tokens or card readers, using either something like the plugdev group on Debian, or even per-device ACLs managed by systemd-logind. Commented Aug 1, 2024 at 7:07
8

It is possible, but I'm not sure how easy it is. On Linux (and pretty much only Linux, not any other Unix-like), there is a flag called "no new privileges", which effectively turns off setuid binaries for the calling thread, and all of its descendants, irrevocably. I couldn't find anything in the OpenSSH documentation that would suggest that SSH can enable it for you, so you'll likely have to do it by hand. That would consist of writing a small C program that runs prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); and then does something like execl("/bin/bash", "-bash", (char*) NULL) to execute the user's shell (which might or might not actually be bash, so you could use getpwuid(getuid())->pw_shell to look up the correct shell - error checking elided for brevity). You would then set ForceCommand to your program, so that users are always forced to invoke this wrapper.

This will break the ability of users to execute arbitrary commands by passing them as an extra argument to SSH, which is occasionally useful. If you want to support that use case, use getenv to look up the value of the environment variable SSH_ORIGINAL_COMMAND, and exec that if it is nonempty. Note that SSH normally executes commands by running the user's login shell with the -c flag (followed by the requested command), so you should do that as well.

It is also not completely foolproof. Programs like run0 work by asking a privileged daemon (in this case, systemd) to execute commands on behalf of an unprivileged user, without using setuid, so "no new privileges" has no effect on such a command. If you are running systemd (or anything resembling systemd), you will need to take appropriate steps to lock that functionality down separately (for example, by putting the user's SSH session inside of a container of some sort, or by otherwise removing their privileges to start and stop service units through the systemd CLI).

Finally, be aware that this is a very big hammer. It completely disables sudo and anything that works like sudo (other than run0 as discussed above). There is no selectivity at all - this is an all-or-nothing flag. But on the positive side, that also means that there are (almost) no loopholes you have to worry about. For example, you don't need to force interactive binaries into some sort of "restricted" mode that prevents the user from getting a shell (as is often necessary with more selective sudo restrictions).

16
  • 1
    You can do the equivalent without compiling any additional software using the unshare(1) utility to run the entire session in a new user namespace. Commented Aug 1, 2024 at 16:37
  • 2
    @R..GitHubSTOPHELPINGICE: unshare, in general, is significantly more complicated than no_new_privs. What you describe will likely work if given appropriate flags, but it is also marginally more likely to run into compatibility issues or require some trial and error to get right. I would suggest that, rather than trying to use unshare by hand, it may be preferable to use an off-the-shelf container solution such as Docker. Whether that's an appropriate solution will depend on the exact problem you're trying to solve, of course. Commented Aug 1, 2024 at 18:11
  • 2
    @R..GitHubSTOPHELPINGICE: Yes, but it also removes all supplementary group IDs as seen from processes inside the namespace (try e.g. unshare -c id). Any process that inspects supplementary group IDs will break. Commented Aug 1, 2024 at 19:22
  • 1
    @R..GitHubSTOPHELPINGICE: That's not the point. An application can call getgroups(2) and inspect the results to decide how to behave based on which groups are present. If your container (unshare is a very primitive container, but a container nonetheless) lies to the application, then it will behave differently. Commented Aug 2, 2024 at 23:49
  • 1
    @R..GitHubSTOPHELPINGICE: getgroups(2) is specified by POSIX. It is fully portable. You may disagree with the application's design decision to use that syscall, but your disagreement is not going to make a broken application work. POSIX specified it so that applications could use it. The fact that an application might need to know a thing or two about the groups on the system does not automatically imply that it is completely impossible to use in a portable fashion, nor that it is "buggy" for an application to do so. Commented Aug 3, 2024 at 3:53

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.