Search code examples
sshsudotty

Piping the output of ssh sudo


I sometimes have a need to run commands as root on a remote server, and parse the output of the command on my local server. The remote server does not allow root login by ssh, but has sudo configured in a way that requires a password. A simplified example of what I need to do is

ssh remote sudo echo bar | tr bar foo

(Obviously in this simplified example, there's no good reason to need to run echo on a different machine to tr: this is just a toy example to explain what I'm trying to do.)

If I run the command above, I get an error that sudo has no way to prompt for a password:

richard@local:~$ ssh remote sudo echo bar | tr bar foo
sudo: no tty present and no askpass program specified

One way I can try to fix this is by adding the -t option to ssh. If I do that, sudo does wait for and accept a password, but the output of ssh's pseudo-terminal goes to stdout, meaning the sudo prompt message is piped to tr and not displayed to the user. If the user doesn't know sudo is waiting for a password, they will think the script has hung, and passing the prompt message to the pipe probably breaks further processing:

richard@local:~$ ssh -t remote sudo echo bar | tr bar foo
[sudo] posswood foo oichood: 
foo

(This admittedly silly example shows the prompt has been processed by the tr command the output is piped to.)

The other way I can see to try to fix this is by adding the -S option to sudo. If I do that, sudo prompts on stderr for the password, so the prompt is not passed down the pipeline. That's good, but sudo also accepts the password on standard input meaning it's echoed to the terminal where anyone looking over the user's shoulder can read it:

richard@local:~$ ssh remote sudo -S echo bar | tr bar foo
[sudo] password for richard: p8ssw0rd
foo

I've found inelegant ways of working around the problems with these two options, but my workarounds hit a problem if the user gets their password wrong the first time. That in itself is a problem. Examples of this are:

richard@local:~$ echo "[sudo] password for $USER:"; \
  ssh -t remote sudo echo bar | tail +2 | tr bar foo

richard@local:~$ (read -s password; echo $password; echo >&2) \
  | ssh remote sudo -S echo bar | tr bar foo

I'm sure there must be a good solution to this, as it doesn't seem an uncommon thing to want to do. Any ideas?


Solution

  • The best solution I've come up with is to use sudo -S and disable local echo so the password isn't shown as you type it:

    $ { stty -echo; ssh remote sudo -S echo hello; stty echo; echo 1>&2; }
    [sudo] password for user:
    hello
    

    This leaves sudo in charge of the password prompting, so it works properly if the user types the password wrong.

    I don't think any solution using ssh -t can ever work properly, since it combines stderr and stdout.