Using sudo over ssh

1. Using sudo over ssh

When using ssh to execute commands on a remote system, it can be tricky to do so while using sudo. Sudo is set to ask for a password but the password dialog needs to be transferred to the local tty over ssh, not the remote tty. So how does one do this. This is a fairly simple example to list the contents of the root directory.:

ssh -tq user@<remote-machine> "sudo bash -c 'ls -la /root'"

To make it easy, the ssh params as explained in the manual.

-t      Force pseudo-terminal allocation.  This can be used to execute arbitrary screen-based programs
        on a remote machine, which can be very useful, e.g. when implementing menu services.
        Multiple -t options force tty allocation, even if ssh has no local tty.
-q      Quiet mode.  Causes most warning and diagnostic messages to be suppressed.

It means we want the ssh connection to allocate a pseudo-terminal so we get a password dialog to enter the sudo password. -q is for quiet mode and can be omitted.

We prepend the bash command with sudo, -c to specify the command.

Another thing of note is what happens with single and double quotes in bash.

3.1.2.2 Single Quotes

Enclosing characters in single quotes (‘'’) preserves the literal value of each
character within the quotes. A single quote may not occur between single quotes,
even when preceded by a backslash.

2. How about sudo in scripts?

How can we use this in a script?

First of all, it is generally a good idea to not use sudo in the script at all but use the sudo command to execute the script. This will execute the commands with elevated priviliges. If you want to run a command in that script without sudo priviliges, use the following line:

sudo -u username command

2.1. local script

When executing the script on a local machine, the result would be:

sudo myscript.sh

2.2. remote script

To execute such a script with sudo remotely, is not that straightforward. Here are 2 solutions you could use.

2.2.1. solution 1: base64 encode

First we put the commands we want to execute in a script and do a base64 encoding of that script. The -w0 (zero) stands for no line wrapping. We store the result in a environment variable. To remote execute the commmand, we decode the base64 string on the other end end and use bash to execute it:

CMD=$(base64 -w0 myscript.sh)
ssh -t user@<remote-machine> "echo $CMD | base64 -d | sudo bash"

2.2.2. solution 2: sudoers file

Another way to do this, is to put the script on the remote machine, and to add a line in the sudoers file for the script. Using what we learned previously, we will edit the sudoers file via a remote command:

scp myscript.sh user@<remote-machine>:/home/user/
ssh -t user@<remote-machine> "sudo bash -c 'visudo'"
...
user ALL=(ALL:ALL) NOPASSWD:/home/user/myscript.sh

# Execute the script
ssh -t user@<remote-machine> "sudo bash -c 'visudo'"