Linux – Allowing automatic command execution as root on Linux using SSH

linuxrootsshsusudo

What is a good setup that allows automatic execution of a command or a script on a remote server with root privileges using SSH?

I'm aware (only vaguely for some the options) of the following options:

  • Allowing a direct login by root (PermitRootLogin) (and possibly forcing key authentication).
  • Configuring sudo not to require a password (NOPASSWD flag in sudoers) and TTY (requiretty flag).
  • Configuring sudo to allow an execution of specific commands/scripts, when authenticated with a specific private key.
  • Setting the script owner as root and setting setuid permission.

But first, I'm not sure what are security consequences of these. For example I know that allowing root login is frowned upon. But I'm not sure, if that is not an obsolete point of view. From what I've understood, it looks like a password authentication is the danger. With public key authentication, the direct root login might be ok. And for some of the options, particularly the sudo, I'm not sure even about the configuration needed. While I am able to google all that, there might be security considerations that I may miss, that's why I'm asking for experts' opinion.

Note, that I'm asking for a server-side setup. The execution will be triggered by a program, not a tool like ssh, so I'm not looking for things like automatic client authentication.


Background: Being active in ssh tag on Stack Overflow, one of frequent questions that come up, are about various hacks that people attempt, while trying to execute a command/script (or even an SFTP server) over an SSH on a remote Unix/Linux server server using a root account using various programming languages (C#, Java, VB.NET, Python, etc.) and SSH libraries (SSH.NET, JSch, Paramiko, etc.).

The implementations, that the people attempt, usually try using su or sudo. These then prompt for a password. So the implementations then try to feed the password to the command input. As su and sudo often require terminal emulation for the password prompt, the implementation have to require PTY. Which in turn causes further troubles, as sessions with the terminal emulation often employ interactive features, like ANSI escape codes, pagination, etc. All these lead to loads of further unreliable hacks that attempt to remove or even interpret the ANSI escape codes or simulate large enough terminal to avoid pagination.

Few examples out of many:

While I usually can provide a help with implementing these hacks, I also usually add a suggestion that there are better ways than automating sudo/su. But I'm not actually confident about providing details of those purported "better ways". A related question: Is sudo almost useless?

So I'm looking for a canonical answer from a Super User perspective, which can then be referred to and adapted for Stack Overflow purposes.

Best Answer

General Considerations

  • Whenever using SSH, password authentication should be avoided, i.e. the /etc/ssh/sshd_config file should contain the following lines:

    PermitRootLogin no
    PasswordAuthentication no
    
  • However, if one - for some reason - has to use password authentication, one should use established, well known and tested tools, like sshpass. Do not start to pipe around passwords by yourself.

  • If using pubkey authentication it does not make sense to protect the private key by a passphrase, if the passphrase is in turn stored inside a config file or alike. If an attacker is able to gain filesystem read access to steal the private key file, he will also be able to steal the config file.

  • All commands that can run with user privileges, should run with user instead of root privileges.

  • The used programming language will not be considered in this answer, since there is no difference between Python's subprocess.call, Java's java.lang.Runtime.exec or a subshell environment inside a shell script with regards to security.

  • Further hardening the remote host against external attackers by configuring a firewall, putting IDS/IPS systems in place and the like will also not be considered, since it is out of scope.

That said, let us consider different scenarios:

Running Commands of Which a Finite Subset Requires Root Privileges

One should use a separate user (adduser --shell /bin/rbash --disabled-password remcmdexec will create a user with a restricted shell, not able to do local but only remote logins, see man adduser and man rbash) in combination with a sudoers file that allows this user to only run the required, finite set of commands as root (see man sudoers) on the remote host:

# /etc/sudoers

remcmdexec ALL = (root:root) NOPASSWD: /usr/bin/systemctl reload some.service

Requiring a password to run these commands with sudo does not make sense, since local login was disabled (--disabled-password parameter) and an attacker who was able to steal the key file will also be able to steel the required password (see answer's first section).

The authorized_keys file should contain further restrictions to prevent e.g. port forwarding, see man sshd:

restrict ssh-rsa <BASE64-PUBKEY-REPRESENTATION> remcmdexec

It should further be owned by root and being read- but not writable by the remcmdexec user, to prevent removal of SSH restrictions:

$ ls -l /home/remcmdexec/.ssh/authorized_keys
-rw-r--r-- 1 root root 739 Sep  18 22:47 /home/remcmdexec/.ssh/authorized_keys

Invoke e.g. ssh remcmdexec@remhost sudo /usr/bin/systemctl reload some.service for a command that requires root privileges. For all other commands skip sudo.

Running Commands of Which a Nonfinite but Proper Subset Requires Root Privileges

The same setup as presented at the answer's previous section can be used, but the sudoers file needs to be adapted:

remcmdexec ALL = (ALL:ALL) NOPASSWD: ALL

This is, because remcmdexec finally needs to gain root privileges. Understand that an attacker who is able to login as remcmdexec is now able to remove each and every restriction put in place on the remote host, no matter how intricately the way to achieve root privileges was designed. Therefore all those restrictions are futile in regards to security (A willing attacker). However, they are not futile in regards to safety (System failures).

Running a Nonfinite Set of Commands of Which All Require Root Privileges

It does no longer make sense to use sudo. Instead login in as a user with root privileges to run a command: ssh root@remhost /usr/bin/somecmd

Therefore the following line has to be present at the /etc/ssh/sshd_config file:

PermitRootLogin prohibit-password

However, keep the restrictions inside the authorized_keys file to preserves some basic safety.

Related Question