Why does the tty command need to run if you can't enter your password?
I have configured sudo
to run without a password, but when I try to ssh 'sudo Foo'
, I still get the error message sudo: sorry, you must have a tty to run sudo
.
Why does this happen and how do i work around it?
That's probably because your /etc/sudoers
file (or any file it includes) has:
Defaults requiretty
...which makes sudo
require a TTY. Red Hat systems (RHEL, Fedora...) have been known to require a TTY in default sudoers
file. This provides no real security benefit and can be removed safely
Red Hat have acknowledged the problem and it will be removed in future releases.
If changing the configuration of the server is not an option, as a work-around for that mis-configuration, you could 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, LF
s will be converted to CRLF
s... (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 with both of them
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.
This is actually not an ideal work around
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
.
For example, with expect
.
ssh host 'expect -c "spawn -noecho sh -c {
exec sudo cmd >&4 2>&5 <&6 4>&- 5>&- 6<&-}
exit [lindex [wait] 3]" 4>&1 5>&2 6<&0'
Or with script
(here assuming the implementation from util-linux
).
ssh host 'SHELL=/bin/sh script -qec "
sudo cmd <&3 >&4 2>&5 3<&- 4>&- 5>&-
" /dev/null 3<&0 4>&1 5>&2'
(assuming (for both) that the login shell of the remote user is Bourne-like).