即使在 /etc/sudoers 中使用 NOPASSWD,PAM 也需要 exec/shell_exec 的密码

PAM requires password for exec/shell_exec even with NOPASSWD in /etc/sudoers

我想做什么:

允许网络服务器用户 (http) 运行 显式 iptables 命令或(最好)在将输入传递给 iptables 之前检查语法的脚本,而不需要密码。

问题:

尽管我对手头的问题进行了所有搜索,但对 visudo、sudoers.d 或 php 配置的任何更改似乎都对我尝试做的事情产生了影响。

我服务器的相关情况:

[weasel@darwyn ~]$ uname -a
Linux darwyn 5.3.7-arch1-1-ARCH #1 SMP PREEMPT Fri Oct 18 00:17:03 UTC 2019 x86_64 GNU/Linux

nginx version: nginx/1.16.1

PHP 7.3.10 (cli) (built: Sep 26 2019 13:40:03) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.10, Copyright (c) 1998-2018 Zend Technologies

Username:                           http
UID:                                33
Gecos field:
Home directory:                     /srv/http
Shell:                              /usr/bin/nologin
No login:                           yes
Primary group:                      http
GID:                                33
Hushed:                             no
Running processes:                  3

Last logs:
09:29 sudo[648]: pam_unix(sudo:auth): auth could not identify password for [http]
09:29 sudo[649]: pam_unix(sudo:account): account http has expired (account expired)
09:29 sudo[650]: pam_unix(sudo:account): account http has expired (account expired)

我已采取的解决问题的步骤:

/var/log/php-fpm.log 告诉我呼叫正在启动但需要密码:

"sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper"

从类似的问题中,我了解到 sudoers 文件优先考虑与用户相关的最后一个配置。所以我第一次去的地方

root ALL=(ALL) ALL
weasel ALL=(ALL) ALL
http ALL=(ALL) NOPASSWD: /usr/local/bin/blacklist_ip, /usr/bin/iptables

我将条目下移到

root ALL=(ALL) ALL
weasel ALL=(ALL) ALL

## Uncomment to allow members of group wheel to execute any command
# %wheel ALL=(ALL) ALL

## Same thing without a password
# %wheel ALL=(ALL) NOPASSWD: ALL

## Uncomment to allow members of group sudo to execute any command
# %sudo ALL=(ALL) ALL

## Uncomment to allow any user to run sudo if they know the password
## of the user they are running the command as (root by default).
# Defaults targetpw  # Ask for the password of the target user
# ALL ALL=(ALL) ALL  # WARNING: only use this together with 'Defaults targetpw'

##
##  Relocated user http permissions for testing
##
http ALL=(ALL) NOPASSWD: /usr/local/bin/blacklist_ip, /usr/bin/iptables

## Read drop-in files from /etc/sudoers.d
## (the '#' here does not indicate a comment)
#includedir /etc/sudoers.d

我删除了 visudo 中的 http 条目并将其移动到 /etc/sudoers.d/90-MyOverrides.

http ALL=(ALL) NOPASSWD: /usr/local/bin/blacklist_ip, /usr/bin/iptables
http ALL=NOPASSWD: /usr/local/bin/blacklist_ip, /usr/bin/iptables
http ALL=(http:http) NOPASSWD: /usr/local/bin/blacklist_ip, /usr/bin/iptables
http ALL=(ALL) NOPASSWD: /usr/local/bin/blacklist_ip *, /usr/bin/iptables *
http ALL=NOPASSWD: /usr/local/bin/blacklist_ip *, /usr/bin/iptables *
http ALL=(http:http) NOPASSWD: /usr/local/bin/blacklist_ip *, /usr/bin/iptables *
http ALL=(ALL) NOPASSWD: /usr/bin/iptables -L
http ALL=NOPASSWD: /usr/bin/iptables -L
http ALL=(http:http) NOPASSWD: /usr/bin/iptables -L

为了确保这会产生某种效果,我在自己的用户身上进行了测试

weasel ALL=(ALL) NOPASSWD: /usr/bin/iptables

我能够以用户 weasel 的身份在没有密码的情况下执行 iptables 命令,但是 php-fpm 日志仍然需要用户 http 的密码。 我还尝试使用 'sudo -S' 命令,希望密码的空值可以工作,但它也会出错。

"[sudo] password for http: "
"sudo: no password was provided"

我检查了 /etc/php/php.ini 和 /etc/php/php-fpm.conf 以确保 exec/shell_exec 命令未被禁用并且安全模式未激活。 /var/log/php-fpm.log 中生成的调试信息已经证明了这一点,但我想更彻底。

这有点多余,但只是为了排除它重要的可能性。

exec("sudo blacklist_ip 185.189.12.135");
shell_exec("sudo blacklist_ip 185.189.12.135");
return:
"sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper"

exec("blacklist_ip 185.189.12.135");
shell_exec("blacklist_ip 185.189.12.135");
exec("/usr/bin/iptables -A INPUT -s 185.189.12.135 -j DROP");
shell_exec("/usr/bin/iptables -A INPUT -s 185.189.12.135 -j DROP");
return:
"Fatal: can't open lock file /run/xtables.lock: Permission denied"

exec("sudo /usr/bin/iptables -A INPUT -s 185.189.12.135 -j DROP");
shell_exec("sudo /usr/bin/iptables -A INPUT -s 185.189.12.135 -j DROP");
return:
"sudo: Account expired or PAM config lacks an "account" section for sudo, contact your system administrator"

经过一些搜索发现,根据 https://www.sudo.ws/troubleshooting.html:

Q) Sudo says 'Account expired or PAM config lacks an "account" section for sudo, contact your system administrator' and exits but I know my account has not expired.

A) Your PAM config lacks an "account" specification. On Linux this usually means you are missing a line like:
    account    required    pam_unix.so
in /etc/pam.d/sudo.

在与此主题相关的任何其他问答中,没有人将此作为原因提及。尽管如此,为了彻底 I looked up information regarding PAM and sudo:

#%PAM-1.0
auth            include         system-auth
account         required        pam_unix.so
#account        include         system-auth
session         include         system-auth

没有区别。

我没主意了。我需要有关适当 PAM 模块的更多信息,使 PAM 可以通过 http 用户,或者我需要一些重定向以进行进一步评估。

编辑 为了简洁起见,已更新以简化信息。

编辑 添加了 /etc/php/php-fpm.d/www.conf

的内容
[www]

user = http
group = http

listen = /run/php-fpm/php-fpm.sock

listen.owner = http
listen.group = http

pm = dynamic

pm.max_children = 5

pm.start_servers = 2

pm.min_spare_servers = 1

pm.max_spare_servers = 3


access.log = /var/log/php/$pool.access.log

access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"

catch_workers_output = yes

php_flag[display_errors] = on
php_admin_value[error_log] = /var/log/php/fpm-php.www.log
php_admin_flag[log_errors] = on

根据评论中的 ,事实证明,在某些时候,http 帐户的到期日期被设置为过去的某个时间,因此它在功能上永远无法用于 sudo 命令。也许这是一个决定,以防止系统在未来通过在网络服务器中使用 PHP 注入 exec/shell_exec 命令的可能性。

解决方法:

chage -E -1 http

这将用户的到期时间设置为 'never' 并使其再次可用。 sudo 设置现在按预期工作。

很抱歉没有给出你的问题的答案,但与这个问题相关,在这里值得一提。我在使用

时遇到了类似的问题

GitHub actions to run sudo command in your-file-name.yaml file

所有脚本运行成功,但是运行到最后

sudo supvervisor restart all

由于 sudo 命令需要密码和 returns 错误 sudo: no tty present and no askpass program specified

,它失败了

下面是解决这个问题的步骤:

如果您的 your-user-name 没有退出,您可以使用以下命令将其添加到 sudoer 组:

sudo adduser your-user-name sudo

现在您可以告诉系统将其添加为不需要密码的用户

sudo sh -c "echo 'your-user-name ALL=NOPASSWD: ALL' >> /etc/sudoers"