Allowing automatic command execution as root on Linux using SSH

8

3

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".

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.

Martin Prikryl

Posted 2019-09-11T06:47:11.417

Reputation: 13 764

You may already know, but If you run sudo visudo and add username ALL=(ALL) NOPASSWD:ALL then that user will be able to execute sudo commands with no prompt or password. I have my internal apache user running like this. I have an internal dashboard that has a few buttons to start up game servers, it just executes the relevant bash command with the apache user – Panomosh – 2019-09-11T10:25:16.193

1@Panomosh Thanks for your comment. Yes, that's what I was referring to by "Configuring sudo not to require a password ..." - Though I believe that this is probably one of the less secure options. But definitely, I this option should be included in the answer I'm hoping for :) – Martin Prikryl – 2019-09-11T11:05:56.670

The ALL= directive can be refined to allow sudo without password only for the listed command-lines (including arguments) when run by a particular user. This looks secure enough for me. – harrymc – 2019-09-16T08:10:45.433

@harrymc Yes. That's my favorite option now. But as I have commented already at the answer by davidgo, I'd like the answer to show how to do that. – Martin Prikryl – 2019-09-16T08:12:45.410

There is already an answer for that in Unix Stackexchange. I could add it here if you like.

– harrymc – 2019-09-16T08:16:42.033

@harrymc It would be nice. I'm bit confused by the example amended to the davidgo answer. It does not seem to match the answer you have linked. But it can just me not understanding the sudoers syntax. Also it would be nice if someone compares the use of sudo with "Setting the script owner as root and setting setuid permission.", which seem quite similar to me (from a functional and security point of view). Does sudo allow executing a script/command with any arguments (what root-owner script does)? – Martin Prikryl – 2019-09-16T09:19:36.023

Answers

1

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.

dirdi

Posted 2019-09-11T06:47:11.417

Reputation: 1 860

Nice answer. Thanks. – Martin Prikryl – 2019-09-18T12:31:33.210

3

I believe an appropriate solution for a majority of simple cases [ ie if a framework like Puppet/Chef/CfEngine/Ansible is overkill ], but a high level of control of the remote system is required is to

(a) Require key based authentication (b) lock down the locations that can be used by SSH - particularly to lock down the IP addresses that root users can use (c) Ensure that the machines with this trust relationship are properly secured.

I do not believe "logging in as root is frowned upon", as much as "logging in as root to do things which do not require root permissions" is frowned upon. Having a guessable password makes it possible for the account to be brute-forced, so that is an issue to contend with, and by allowing people to log in as root they can make stupid errors with a wider scope, and do questionable things which hide malicious activity - but depending on the architecture this may not actually be meaningful.

This XKCD commic points out that depending on the environment, it may not actually matter if a root account is compromised.

Probably the simpist and most effective solution for the general case is to limit exactly who can log in as root by controlling the keys in conjunction with specifying an appropriate AllowUsers line in /etc/ssh/sshd.conf. An appropriate line to allow normal users but lock root access down might look like:

  AllowUsers normaluser1 normaluser2 root@ip.add.re.ss

Of-course, it is important that password based login is disallowed, or that the root account does not have a password (ie has a "!" or "*" in the password field of the password file.)

As you have postulated, if only a single (or small number of) program(s) need to be run, you might be better off setting up a specific user with key based authentication and allowing appropriate sudo access to the required commands.

There are, of-course, a lot of things that can be done to "lock down" access further, depending on the value of the data - selinux, remote logging, limited shell, time-of-day constraints, notifications immediately on logins, active log monitoring like fail2ban [ in addition to network restrictions which I see as non-optional] can all be part of a solution. That said, if you are simply controlling a system which can be blown away and recreated easily, much of this is likely overkill.

Please remember that security is built up in layers, in order to be able to get root access, an account should need to get through a number of layers. Locking down access, SSH private keys, firewalls, time-of-day restrictions [ and principle of least access, ie only providing access to what is needed, no more, logging, backups ] should all function together as part of good security.

Additional - SUDO access

Sudo is a mechanism to allow a regular user to run certain commands with elevated access, and is quite flexible and varied in scope. While a rundown of sudo is not appopriate here (but is well documented if you type man sudo in most distributions, the appropriate steps might be -

  1. Edit /etc/sudoers - Most variants of Linux have a special program to do this called "visudo" which needs to be run as root, and roughly equivalent to using the system editor to edit /etc/sudoers (which is another way to do this - although probably fround upon)

  2. Add/change appropriate lines to let a given user do particular things. If, for example, you wanted them to be able to mount directories for example and without having to enter a password, you would enter a line like :

    username    ALL=/sbin/mount NOPASSWD: ALL
    

To then run this command the appropriate user would run something like

  sudo mount /dev/xxx /path/to/mount

You can list multiple lines for multiple commands, uses groups rather then uses - sudoers is a fairly flexible subsystem. In many distributions it is also possible to add files with commands to a subdirectory like /etc/sudoers.d

davidgo

Posted 2019-09-11T06:47:11.417

Reputation: 49 152

Thanks for your answer. I was hoping that the ideal answer would show the developers (inexperienced with a server administration), how to set up the stuff. For example the "allowing appropriate sudo access to the required commands". – Martin Prikryl – 2019-09-16T08:06:07.727

1@MartinPrikryl I've modified my answer to cover sudo basics ,Forgive me but if a system administrator does not know their way around the command line and man pages that are "baseline" parts of the Unix infrastructure (sudo predates ssh and was taught to me in basic unix classes in the early 2000s), they probably should not be attempting distrubuted ss deployment - and indeed implementing a more complex framework like Puppet - while such a framework is overkill it would force the appropriate learnings and/or protect users from mistakes a seasoned admin would not make. – davidgo – 2019-09-16T08:55:08.057

2

The following is an answer from the Unix Stackexchange post
How to remote execute ssh command a sudo command without password.

This method lets the administrator of the server to allow your account to execute one command as sudo without specifying the password. The command is probably the call to your script, and it is only allowed when run under your account. As the permitted command is entirely specified including arguments, I believe that this method is pretty secure.

you can tell sudo to skip password for some command.

e.g. in /etc/sudoers

archemar  ALL = (www-data) NOPASSWD: /bin/rm -rf /var/www/log/upload.*

this allow me to use

sudo -u www-data /bin/rm -rf /var/www/log/upload.*

as archemar without password.

Note that

sudo -u www-data rm -rf /var/www/log/upload.*

won't work (will ask a password) as rm differ from /bin/rm.

Be sure to edit /etc/sudoers using visudo command.

Once you've reach advanced level, you might wish to have your own sudo files in /etc/sudoers.d.

harrymc

Posted 2019-09-11T06:47:11.417

Reputation: 306 093

1

My setup in similar situation is /home/blesseduser/.ssh/authorized_keys:

no-port-forwarding,command="/usr/local/bin/audited-command" ssh-rsa AAAAB...

Inside that script I do some sanitization of SSH_ORIGINAL_COMMAND variable (plus some auditing) and extract the command user wanted to run. All allowed commands are in users /home/blesseduser/bin as symlinks to /usr/local/bin/sudo-runner:

#!/bin/bash -e
sudo /usr/local/bin/$(basename $0) "$@"

The symlinks are generated by script from the sudoers file. When no command is present, content of his bin directory is listed. If you do not restrict port forwarding bad things can happen.

brablc

Posted 2019-09-11T06:47:11.417

Reputation: 1 102

1

Here's an answer about the security considerations, which I think haven't been addressed in the other answers.

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

There are multiple parts to this: allowing the execution of a single script or command with elevated privileges (root), and using SSH. These are not necessarily dependent.

  • Allowing a direct login by root (PermitRootLogin) (and possibly forcing key authentication).

This will allow anyone to have full root privileges (not restricted to your single command/script). It will also allow anyone to try to break into your system remotely, by trying passwords etc.

  • Configuring sudo not to require a password (NOPASSWD flag in sudoers) and TTY (requiretty flag).

This will allow any user to have full root privileges, as long as he is logged in. In terms of security it's not much better than allowing root login, though there'll be less automated attempts to break into other user accounts.

  • Configuring sudo to allow an execution of specific commands/scripts, when authenticated with a specific private key.

I am not sure how this would be implemented - sudo doesn't know anything about SSH keys, at least the sudo variants I am familiar with.

Configuring sudo to only allow execution of specfic commands/scripts is much better from a security point of view (because it's more restrictive).

  • Setting the script/command owner as root and setting setuid permission.

This will allow any user to execute this script as root. You can't restrict this to particular users, and setuid bits have their own potential security weaknesses.


In the end, I don't think there's a single canonical answer - the best solution depends on your circumstances and requirements. So, like in all security-related things, you first sit down and spell out the requirements: How permissive and flexible do you want to be? What is the actual use case? How tightly do you need to tie the system down? Etc.

That said, let me add a very restrictive alternative you haven't mentioned:

Create a specific user account that is only used to execute that particular script/command. Only allow SSH login into this user with a key pair (disable passwords). Configure sudo to allow this user to execute the particular/script command as root.

That has the following security consequences:

  • External hackers can't crack the password of the specific account using brute force.

  • Anyone gaining access to this account will only be able to execute the single script/command.

  • Other users don't get elevated privileges through this construction.

  • No setuid weaknesses.

dirkt

Posted 2019-09-11T06:47:11.417

Reputation: 11 627

Thanks for your answer. Yes, your last option seems the best. – Martin Prikryl – 2019-09-18T12:26:42.610

1

Regarding the "sudo doesn't know anything about SSH keys, at least the sudo variants I am familiar with" – There seems to be such an option: https://wiki.gentoo.org/wiki/Pam_ssh_agent_auth

– Martin Prikryl – 2019-09-18T12:28:19.003

1

The last option (Setting the script owner as root and setting setuid permission.) will not work, because the setuid bit is ignored for scripts, see https://unix.stackexchange.com/questions/364/allow-setuid-on-shell-scripts

– dirdi – 2019-09-18T13:43:04.577

0

Ansible is the best tool for automation and on remote hosts. Using available methods of Ansible can execute the commands as a superuser (root) on remoter machines --become & --become_method to switch to root via sudo without enable permit root login and NOPASSWD for sudo.

And moreover there is no need of any installations on client hosts — the only requirement is remote hosts should support Python >= 2.7.

AFAIK Ansible have two methods of accessing one is with SSH and Paramiko.

Das D

Posted 2019-09-11T06:47:11.417

Reputation: 1

Thanks for your answer. I assume you need to install Ansible on the server (and that would require root privileges), right? – Martin Prikryl – 2019-09-11T16:41:11.210

not required, can install with normal user using pip command – Das D – 2019-09-11T16:49:57.173

OK, but it looks like the "become" uses su/sudo under the hood: https://docs.ansible.com/ansible/latest/plugins/become.html#plugin-list - so I'm not sure it can solve the problem.

– Martin Prikryl – 2019-09-11T17:02:05.147

1And how would you use Ansible, when you are developing a C# or Java application? Is there Ansible client library for .NET or Java? – Martin Prikryl – 2019-09-11T17:48:44.213