我已配置sudo
为无需密码即可运行,但是当我尝试这样做时ssh 'sudo Foo'
,我仍然收到错误消息sudo: sorry, you must have a tty to run sudo
。
为什么会发生这种情况?我该如何解决?
答案1
这可能是因为您的/etc/sudoers
文件(或其包含的任何文件)具有:
Defaults requiretty
...这使得sudo
需要 TTY。众所周知,Red Hat 系统(RHEL、Fedora...)在默认sudoers
文件中需要 TTY。这没有提供真正的安全优势,可以安全地删除。
红帽已经承认这个问题并且它将在未来的版本中删除。
如果无法更改服务器的配置,作为该错误配置的解决方法,您可以使用-t
或-tt
选项ssh
在远程端生成一个伪终端,但要注意它有多个端影响。
-tt
用于交互式使用。它将本地终端置于raw
模式下,以便您与远程终端交互。这意味着如果ssh
I/O 不是来自终端或发往终端,则会产生副作用。例如,所有的输入都会被回显,特殊的终止符(^?
, ^C
, ^U
)会引起特殊的处理;输出时,LF
s 将转换为CRLF
s... (参见这个答案为什么这个二进制文件会被更改?更多细节。
为了最大限度地减少影响,您可以将其调用为:
ssh -tt host 'stty raw -echo; sudo ...' < <(cat)
这< <(cat)
将避免在模式下设置本地终端(如果有)raw
。我们使用stty raw -echo
将远程终端的线路规则设置为通过(实际上,它的行为类似于将使用的管道,而不是不带 的伪终端-tt
,尽管这只适用于运行该命令之后,所以您需要延迟发送某些内容以供输入,直到发生这种情况)。
请注意,由于远程命令的输出将发送到终端,因此这仍然会影响其缓冲(对于许多应用程序来说是基于行的)和带宽效率,因为它TCP_NODELAY
是打开的。另外-tt
,ssh
将 IPQoS 设置为lowdelay
与 相对throughput
。您可以通过以下方式解决这两个问题:
ssh -o IPQoS=throughput -tt host 'stty raw -echo; sudo cmd | cat' < <(cat)
另请注意,这意味着远程命令无法检测其 stdin 上的文件结尾,并且远程命令的 stdout 和 stderr 会合并到单个流中。
所以,这毕竟不是一个很好的解决方法。
如果您有办法在远程主机上生成伪终端(例如使用expect
, zsh
, socat
,perl
的IO::Pty
...),那么最好使用它来创建要附加的伪终端sudo
(但是不适用于 I/O),并且ssh
不使用-t
.
例如,使用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'
或者使用script
(这里假设实现来自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'
(假设(对于两者)远程用户的登录 shell 与 Bourne 类似)。
答案2
默认情况下,SUDO 配置为需要 TTY。也就是说,SUDO 应该从登录 shell 运行。您可以通过将-t
开关添加到 SSH 调用来满足此要求:
ssh -t someserver sudo somecommand
强制-t
分配伪终端。
如果要全局执行此操作,请修改/etc/sudoers
以指定!requiretty
。这可以针对每个用户、每个组或所有级别来完成。
答案3
使用-t
标志 tossh
强制分配 tty。
$ ssh luci tty
not a tty
$ ssh luci -t tty
/dev/ttys003
$
答案4
一个有趣的替代方案是运行 FreeIPA 或 IdM 来集中管理您的用户和 sudoer 规则。然后您可以创建 sudo 规则并分配选项
!要求
在规则中。然后该命令将按预期运行。您还将受益于通过一组配置管理所有服务器和用户。