问题描述:
在使用Perl写服务器服务群集监控程序时遇到这么个问题,在同一服务器上可能运行同名服务但是用不同端口(比如我们启动了两个nginx一个做反 向代理一个做WEBSRV,再如起两个mysqld一个跑3306,一个跑3307端口,所以这种情况是比较常见的),那么我们在区分两个同名服务时只能 以端口号来区分,当然,在root用户下我们很容易就可以通过shell命令获取,如netstat -anp, lsof -i :端口号, fuser -n tcp 端口号,但是当你使用其他普通用户执行时你会发现三个命令都没有权限获取相关的进程ID。
解决思路:
1.使用sudo去执行netstat -anp,lsof -i :端口号, fuser -n tcp 端口号,但是这么做的话需要输密码,那么你的脚本里不就是要写密码明文了?当然可以在/etc/soduers设置这个普通用户针对上述三个命令的某一个 NOPASSWORD:netstat fuser lsof 但是这个监控程序的客户端将运行在几百乃至上千台服务器上,我若使用这种方法我则必须要在这么多台机器上使用root进行设置
chmod +w /etc/sudoers && echo "%用户        ALL=(ALL)       NOPASSWD:netstat" && chmod -w /etc/sudoers
这样做的话会累死我,而且增加了这个程序的环境依赖性导致了程序的不完整性,抛弃
2.分析netstat,fuser或者lsof,刚开始我是以为它们都是从/proc/net/下读文件进行分析的,后来翻看了几页后没找到,没时间花这么多时间去完整的读这些代码,放弃(有读过的或者知道怎么实现的,请告诉我,万谢!)
3.使用linux的setuid()实现,在C语言中有setuid(),seteuid(),setgid(),setegid(),四个函数(关于setuid()就不赘述了,网上文档多的去了),但是Perl中是没有的,后来发现在Perl特殊变量中有
$< 用户真实ID也就是uid
$> 进程有效ID也就是euid
$( 当前进程的组ID gid
$) 当前进程的有效组ID egid
在shell下的id命令可以查看当前用户的这些属性,在perl中可以轻易的指定:$< = 500;但是在当前脚本文件下的话只允许root用户降权使用,普通用户至$< = 0;是不会生效的。但是可以使用调用带有setuid权限位的脚本去提升权限并执行系统命令netstat,fuser或lsof,具体实施过程为:
使用root用户创建setuid.pl
#!/usr/bin/perl -w
use strict;
#必须要设置环境变量,否则普通用户仍然是不允许执行的
$ENV{"PATH"} = "/bin:/usr/bin:/sbin:/usr/sbin";
$ENV{"BASH_ENV"}="";
$< = 0;
system("/bin/netstat -anp");
然后chmod 4755 setuid.pl
这时候你 ls -la setuid.pl查看到其权限应该是 -rwsr-xr-x,那么这就对了
这个时候你可以使用perl脚本或者shell脚本去调用setuid.pl,然后使用普通用户去执行这个perl或shell,而得到的输出将与你使用root用户直接执行netstat命令是一样的

转自:http://renylai.blogbus.com/logs/42494233.html