问题

crontab中使用sudo切换用户执行命令sudo -u user command时,会报sudo: sorry, you must have a tty to run sudo的错误。

原因

在一些Linux发行版中,会默认设置运行sudo需要在tty中运行。以CentOS 7.0为例,在/etc/sudoers中, 有一个默认设置Defaults requiretty

1
2
3
4
5
#
# Disable "ssh hostname sudo <cmd>", because it will show the password in clear.
# You have to run "ssh -t hostname sudo <cmd>".
#
Defaults requiretty

解决方案

如下提供两个解决方案:

修改/etc/sudoers

修改/etc/sudoers,注释掉Defaults requiretty。事实上,红帽已经在最新版本的RedHat和Fedora中移除了该默认配置,详细讨论可以参照Bug 1020147。比如在CentOS 7.6.1810中,已经没有了requiretty的默认设置。

改为使用su -c

可以使用su来替换sudo来运行命令。su user -c "command"

实验

设置Defaults requiretty时

使用root用户,在crontab中设置如下任务

1
2
3
* * * * * echo "`date`" >> /tmp/root_echo.txt 2>&1
* * * * * sudo -u deployer echo "`date`" >> /tmp/deployer_echo.txt 2>&1
* * * * * su deployer -c "echo \"`date`\"" >> /tmp/deployer_su_echo.txt 2>&1

隔两分钟后查看/tmp下的对应文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@localhost tmp]# tail -n 10 /tmp/*echo*.txt
==> /tmp/deployer_echo.txt <==
sudo: sorry, you must have a tty to run sudo
sudo: sorry, you must have a tty to run sudo
==> /tmp/deployer_su_echo.txt <==
Thu Jun 25 17:54:02 CST 2020
Thu Jun 25 17:55:01 CST 2020
==> /tmp/root_echo.txt <==
Thu Jun 25 17:54:02 CST 2020
Thu Jun 25 17:55:01 CST 2020
[root@localhost tmp]#

可以看到:

  • 直接echo可以执行成功.
  • sudo -u deployer command执行失败,会报错误sudo: sorry, you must have a tty to run sudo
  • su deployer -c command的形式可以成功执行。

去除Defaults requiretty时

使用root用户,在crontab中设置如下任务

1
2
3
* * * * * echo "`date`" >> /tmp/root_echo.txt 2>&1
* * * * * sudo -u deployer echo "`date`" >> /tmp/deployer_echo.txt 2>&1
* * * * * su deployer -c "echo \"`date`\"" >> /tmp/deployer_su_echo.txt 2>&1

隔两分钟后查看/tmp下对应的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@localhost tmp]# tail -n 10 /tmp/*echo*.txt
==> /tmp/deployer_echo.txt <==
Thu Jun 25 18:02:02 CST 2020
Thu Jun 25 18:03:01 CST 2020
==> /tmp/deployer_su_echo.txt <==
Thu Jun 25 18:02:02 CST 2020
Thu Jun 25 18:03:01 CST 2020
==> /tmp/root_echo.txt <==
Thu Jun 25 18:02:02 CST 2020
Thu Jun 25 18:03:01 CST 2020
[root@localhost tmp]#

可以看到,三种形式都可以执行成功。

延伸

/etc/sudoersDefaults requiretty的配置,也会影响使用ssh命令的执行。

去除Defaults requiretty时

去除Defaults requiretty时,使用ssh加sudo命令,可以直接执行成功。
在Mac上执行ssh + command

1
2
3
4
$ ssh root@192.168.187.181 'sudo -u deployer echo "hello, world" >> /tmp/hello.txt'
root@192.168.187.181's password:
$

在Linux上可以看到/tmp/hello.txt的内容

1
2
3
[root@localhost tmp]# cat /tmp/hello.txt
hello, world
[root@localhost tmp]#

设置Defaults requiretty时

设置Defaults requiretty时,直接使用ssh加sudo命令,会报错。

在Mac上执行ssh + command

1
2
3
4
$ ssh root@192.168.187.181 'sudo -u deployer echo "hello, world. -- with default requiretty" >> /tmp/hello.txt'
root@192.168.187.181's password:
sudo: sorry, you must have a tty to run sudo
$

此时,会报sudo: sorry, you must have a tty to run sudo的错误。

在ssh时候,加上-t参数,参数显式的告诉 ssh,我们需要一个 TTY 远程 shell 进行交互,命令就可以成功执行。

1
2
3
4
$ ssh -t root@192.168.187.181 'sudo -u deployer echo "hello, world. -- with default requiretty" >> /tmp/hello.txt'
root@192.168.187.181's password:
Connection to 192.168.187.181 closed.
$

再查看Linux上/tmp/hello.txt的内容,可以看到刚echo的内容hello, world. -- with default requiretty成功被写入了文件。

1
2
3
4
[root@localhost tmp]# cat /tmp/hello.txt
hello, world
hello, world. -- with default requiretty
[root@localhost tmp]#

Reference

留言