Aug 10, 2017

How to fix sudo: sorry, you must have a tty to run sudo

It is most-likely that you are running on a Linux distribution with sudo configured to require a tty. This is generally enforced by having Defaults requiretty in the /etc/sudoers.


To disable requiretty globally or to a single command, you have two options:


Replace Defaults requiretty by Defaults !requiretty in your /etc/sudoers. This will impact your global sudo configuration.

Alternatively, you can change this configuration at a per user, per group or per command basis

Defaults!/path/to/my/bin !requiretty
Defaults:myuser !requiretty

 Connect by ssh using -t options


From man ssh: text -t Force pseudo-tty 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.

If -t is not enough, you can use  ssh -t -t Or ssh -tt. 

Use the -t or -tt options to ssh which spawns a pseudo-terminal on the remote side, but beware that it has a number of side effects.
-tt is meant for interactive use. It puts the local terminal in raw mode so that you interact with the remote terminal. That means that if ssh I/O is not from/to a terminal, that will have side effects. For instance, all the input will be echoed back, special terminal characters (^?, ^C, ^U) will cause special processing; on output, LFs will be converted to CRLFs... (see this answer to Why is this binary file being changed? for more details.
To minimise the impact, you could invoke it as:
ssh -tt host 'stty raw -echo; sudo ...' < <(cat)
The < <(cat) will avoid the setting of the local terminal (if any) in raw mode. And we're using stty raw -echo to set the line discipline of the remote terminal as pass through (effectively so it behaves like the pipe that would be used instead of a pseudo-terminal without -tt, though that only applies after that command is run, so you need to delay sending something for input until that happens).
Note that since the output of the remote command will go to a terminal, that will still affect its buffering (which will be line-based for many applications) and bandwidth efficiency since TCP_NODELAY is on. Also with -tt, ssh sets the IPQoS to lowdelay as opposed to throughput. You could work around both with:
ssh -o IPQoS=throughput -tt host 'stty raw -echo; sudo cmd | cat' < <(cat)
Also, note that it means the remote command cannot detect end-of-file on its stdin and the stdout and stderr of the remote command are merged into a single stream.
So, not so good a work around after all.

If you've a got a way to spawn a pseudo-terminal on the remote host (like with expect, zsh, socat, perl's IO::Pty...), then it would be better to use that to create the pseudo-terminal to attach sudo to (but not for I/O), and use ssh without -t.