由SNMP传递调用时,Shell脚本未捕获命令输出

7

问题

SNMPD正确地将SNMP轮询请求委派给另一个程序,但该程序的响应无效。使用相同参数手动运行该程序会正确响应。

详细信息

我在服务器上安装了正确的LSI RAID驱动程序,并希望配置SNMP。根据说明,我已经将以下内容添加到/etc/snmp/snmpd.conf中,以将具有给定OID前缀的SNMP轮询请求重定向到一个程序:

pass .1.3.6.1.4.1.3582 /usr/sbin/lsi_mrdsnmpmain

它不能正确地处理SNMP轮询请求:

snmpget -v1 -c public localhost .1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.1

我得到了以下响应:
Error in packet
Reason: (noSuchName) There is no such variable name in this MIB.
Failed object: SNMPv2-SMI::enterprises.3582.5.1.4.2.1.2.1.32.1

我的尝试

SNMPD传递两个参数,-g<oid>,并期望三行响应<oid><data-type><data-value>

如果我手动运行以下命令:

/usr/sbin/lsi_mrdsnmpmain -g .1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.0

我正确地获得了一个正确的三行响应:

.1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.0
integer
30

这意味着在这个例子中,pass命令正在正确运行,/usr/sbin/lsi_mrdsnmpmain程序也在正确工作。
我尝试用一个Bash脚本替换/usr/sbin/lsi_mrdsnmpmain。Bash脚本将调用委托并记录提供的参数和来自委托调用的输出。
#!/bin/bash
echo "In: '$@" > /var/log/snmp-pass-test
RETURN=$(/usr/sbin/lsi_mrdsnmpmain $@)
echo "$RETURN"
echo "Out: '$RETURN'" >> /var/log/snmp-pass-test

我修改了pass命令,使其重定向到bash脚本。如果我手动运行bash脚本/usr/sbin/snmp-pass-test -g .1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.0,我会得到正确的三行响应,就像我手动运行/usr/sbin/lsi_mrdsnmpmain时一样,并且我会得到以下记录:

In: '-g .1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.0
Out: '.1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.0
integer
30'

当我重新运行snmpget测试时,我仍然收到相同的Error in packet...错误,并且bash脚本的日志显示捕获的代理调用输出为空:
In: '-g .1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.0
Out: ''

如果我修改bash脚本仅回显一个空行,我也会收到相同的“Error in packet...”消息。
我还尝试确保当我手动调用/usr/sbin/lsi_mrdsnmpmain时存在的环境变量与bash脚本相同,但是我得到了相同的空输出。
最后,我的问题是:
1. 为什么在这两种情况下bash脚本的行为不同? 2. 原来注意到的问题(手动运行程序与SNMPD运行程序具有不同的输出)是否可能与bash脚本存在相同的问题?
更新:
eewanco的建议:
“What user is running the program in each scenario?”
我向bash脚本添加了“echo "$(whoami)" > /var/log/snmp-pass-test”,并将“root”添加到日志中。
“Maybe try executing it in cron”
将以下内容添加到root的crontab中,正确的三行响应已记录:
* * * * * /usr/sbin/lsi_mrdsnmpmain -g .1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.1 >> /var/log/snmp-test-cron 2>&1

Grisha Levit的建议

尝试记录stderr日志

没有任何错误被记录

检查/var/log/messages

当我通过SNMPD运行它时,会记录MegaRAID SNMP AGENT: Error in getting Shared Memory(lsi_mrdsnmpmain)。但是当我直接运行它时,不会出现这个问题。我已经做了一些谷歌搜索,并且可能需要安装lm_sensors,我将尝试这样做。

我已经安装了lm_sensors和compat-libstdc++-33.i686(后者是因为从指令中得知它是前提条件,而我缺少它),卸载并重新安装了LSI驱动程序,但遇到了相同的问题。

SELinux

我意外地发现了一个关于使用脚本扩展snmpd的页面,它说要检查脚本是否具有正确的SELinux上下文。我在运行snmpget之前和之后运行了grep AVC /var/log/audit/audit.log | grep snmp,以下条目是直接由运行snmpget添加的:

type=AVC msg=audit(1485967641.075:271): avc:  denied  { unix_read unix_write } for  pid=5552 comm="lsi_mrdsnmpmain" key=558265  scontext=system_u:system_r:snmpd_t:s0 tcontext=system_u:system_r:initrc_t:s0 tclass=shm

我现在假设SELinux导致了调用失败;我会进一步调查...解决方案请见答案。
strace(eewanco的建议)
尝试使用带有和不带有snmp的strace,看看是否可以捕获系统调用失败或其他提示。
为了完整起见,我想看看strace是否会暗示SELinux被拒绝。我必须使用 semodule -r <policy-package-name>删除策略包以重新引入问题,然后运行以下内容:
strace snmpget -v1 -c public localhost .1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.1 >> strace.log 2>&1

strace.log 的结尾如下所示,除非我漏看了什么,否则它似乎没有提供任何提示:

...
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(161),     sin_addr=inet_addr("127.0.0.1")}, msg_iov(1)=    [{"0;\2\1\0\4\20public\240$\2\4I\264-m\2"..., 61}], msg_controllen=32,     {cmsg_len=28, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0},     MSG_DONTWAIT|MSG_NOSIGNAL) = 61
select(4, [3], NULL, NULL, {0, 999997}) = 1 (in [3], left {0, 998475})
brk(0xab9000)                           = 0xab9000
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(161),     sin_addr=inet_addr("127.0.0.1")}, msg_iov(1)=    [{"0;\2\1\0\4\20public\242$\2\4I\264-m\2"..., 65536}],     msg_controllen=0, msg_flags=0}, MSG_DONTWAIT) = 61
write(2, "Error in packet\nReason: (noSuchN"..., 81Error in packet
Reason: (noSuchName) There is no such variable name in this MIB.
) = 81
write(2, "Failed object: ", 15Failed object: )         = 15
write(2, "SNMPv2-SMI::enterprises.3582.5.1"..., 48SNMPv2-        SMI::enterprises.3582.5.1.4.2.1.2.1.32.1
) = 48
write(2, "\n", 1
)                       = 1
brk(0xaa9000)                           = 0xaa9000
close(3)                                = 0
exit_group(2)                           = ?
+++ exited with 2 +++

在每种情况下,哪个用户正在运行程序?如果使用su以不同的用户身份运行snmpd中正在运行程序的用户,则会发生什么?如果与执行环境有关,可以尝试在cronat中执行它,看看是否有类似的问题。 - Vercingatorix
在这两种情况下都是根源问题。我注意到两者的“env”输出不同,因此在bash脚本中,在委派调用之前作为测试,我确保为每个变量都有一个“export”。但即使环境变量存在,我仍然收到了相同的“packer错误”响应。 - PhilJ
尝试将stderr也记录下来,例如RETURN=$(/usr/sbin/lsi_mrdsnmpmain $@ 2>&1) - Grisha Levit
尝试使用strace,带或不带snmp,看看是否可以捕获系统调用失败或其他提示。您将需要使用2>&1或类似的方法重定向stderr。愚蠢的问题:您是否正在使用chroot监狱?我认为您没有,但如果是这样,并且在其中未挂载/proc/sys,则可能会遇到问题。 - Vercingatorix
1个回答

3

SELinux拒绝了snmpd对/usr/sbin/lsi_mrdsnmpmain(以及可能更多)的委派调用。

为了识别它,我运行了grep AVC /var/log/audit/audit.log,并针对每个条目运行了以下命令:

echo "<grepped-output>" | audit2allow -a -M <filename>

这将创建一个SELinux策略包,应该允许委派调用通过。然后使用以下方式加载该包:

semodule -i <filename>.pp

我必须重复此操作5次,因为拒绝的原因不同(unix_read unix_write、associate、read write)。我将尝试将模块合并为一个。
现在当我运行snmpget时,我会得到正确的委派输出:
SNMPv2-SMI::enterprises.3582.5.1.4.2.1.2.1.32.1 = INTEGER: 34

太好了!那真的很难懂。恭喜你找出来了! - Vercingatorix
谢谢。SELinux是我以前没有学习过的盲点之一;我想现在可能是学习它的时候了! - PhilJ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接