今天分别登录15台服务器共3次,在每台服务器上su到root敲一遍密码,再敲同样的命令,严重考验耐心,折腾2个小时,头一次加班到9点。还好明天可以睡懒觉。
需求是批量给15台服务器加sudo权限账号。问题是:可以批量登陆,但是没sudo,也不能交互,自然无法切换到root。有root,但是只能单点登录。权衡一番,决定先单点登录给自己加sudo,然后再批量执行。
第一次折腾,给自己加sudo,直接echo重定向到/etc/sudoers就行了。在windows上用notepad++写好脚本,scp到rsync服务器,再同步到待检查机器上。接下来就是一台台的登录,su root,敲密码,执行脚本。敲到手抽。然后单点测试添加sudo的脚本:
grep "SUDOER" data.txt | sed 's/\ /\n/g' | sed '/SUDOER/d' >sudoer.txt cat sudoer.txt | while read sudoer do grep "$sudoer" /etc/sudoers &>/dev/null && r=0 || r=1 if [ $r -eq 1 ]; then echo -e "Sudoers: $sudoer will be added! \n" echo "$sudoer ALL=(ALL) NOPASSWD: ALL" >>/etc/sudoers fi done
流程是从data.txt中读出需要添加的账号,然后在/etc/sudoers中搜索该账号,如果找不到则添加。执行完成后批量sudo了另一个脚本,刚才单点测试的机器报错如下:
sudo: >>> /etc/sudoers: syntax error near line 152 <<< sudo: parse error in /etc/sudoers near line 152 sudo: no valid sudoers sources found, quitting sudo: unable to initialize policy plugin
cat /etc/sudoers一看,最后一个账号居然分成了2行,cat -v /etc/sudoers,发现 ^M 字符,原来因为data.txt是windows格式创建的文本,换行符是 \r\n 风格的,而类Unix系统则是 \n 。
修改脚本,用tr替换特殊字符:
grep "SUDOER" data.txt | sed 's/\ /\n/g' | sed '/SUDOER/d' |tr -s "[\015]" "[\n]" >sudoer.txt cat sudoer.txt | while read sudoer do grep "$sudoer" /etc/sudoers &>/dev/null && r=0 || r=1 if [ $r -eq 1 ]; then echo -e "Sudoers: $sudoer will be added! \n" echo "$sudoer ALL=(ALL) NOPASSWD: ALL" >>/etc/sudoers fi done
这一段还算顺利,然而接下来发生的事情就有些让人不解了。脚本单点测试通过后,直接批量执行,却出现如下报错:
sudo: >>> /etc/sudoers: Alias `USER_SJ' already defined near line 152 <<< sudo: parse error in /etc/sudoers near line 152 sudo: no valid sudoers sources found, quitting sudo: unable to initialize policy plugin
重复定义的别名。
返回查看,确实是重复定义了,可是,我的脚本绝对不会添加这么一行定义啊! 一开始以为2行一样,用uniq /etc/sudoers |wc -l 和cat /etc/sudoers |wc -l 结果却一样,仔细一看,才发现最后一个账号一个是 account ,另一个是 account1,本着简单的原则,写了个包含一个命令的删除带1的那个(带1的具有唯一性好定位)的脚本,第二次折腾,scp,rsync,批量同步,单点登录,su root,敲密码,执行脚本。干完后sudo了一下添加账号的脚本和服务器检查的脚本,恩,一切正常:
长舒一口气,立即给管理员发邮件交付。
然后,发完邮件,意犹未尽,再次批量sudo一下................................................................................................................................................................................................那个错误怎么又TMD出现了!
重复定义的别名,我不是刚刚删除么?刚才还正常呢!
单点登录,打开一看,确实删除的那行又多出来了.....一身冷汗。难道我今天不能下班了?
还好有前辈还没走,告诉我是cfengine同步的,应该删除那个不带1的。好吧,第二次折腾点太背了。第三次折腾。不带1,不好定位,只好一台台的 visudo 了,折腾完,暂时正常了。下班!回到住处,再次检查,恩,还是正常的,终于可以安心了。
感觉这次的问题不算是我造成的,但是仍然应该得到教训,线上脚本一定要经过测试,如果没经过严格的测试就直接批量执行了,像我这次还算好的,只是批量sudo暂时用不了,如果是其他操作,比如配bond,把网卡配置文件修改错了,又重启了网卡,连不上机器那就只能到机房,或者登录管理卡一台台的搞了。
附:/etc/sudoers的别名设置
别名主要包括这几种:User_Alias,Host_Alias,Runas_Alias,Cmnd_Alias,分别是用户别名,主机别名,运行用户别名,命令别名。设置这几个别名的命令主要是为了配置方便,这个很类似linux用户群组或是数据库中角色的作用,有了别名便可以批量的赋予权限。
设置方法 Alias_Type alias_name = name1, name2, name3, 例如User_Alias User1 = user1, user2, user3。其他类型的alias设置方法类似。
参考:http://blog.csdn.net/zbszhangbosen/article/details/7526692
如果确实需要给脚本文件设 suid,常见的做法是用 C 写一个封装程序,在这个程序里调用脚本。这样,给封装程序(二进制可执行程序)设上 suid (chmod 4755 a.out) 就可以达到给脚本文件设 suid 的效果了。
前面的都没用。。。
出于安全考虑1,现代的大部分 UNIX 类系统都会忽略掉脚本的 suid ,只有二进制可执行程序的 suid 有效 。所以直接给脚本文件设置 suid 是达不到预期效果的。
suid/guid是什么? suid意味着如果A用户对属于他自己的 shell脚本文件设置了这种权限,那么其他用户在执行这个脚本的时候就拥有了A用户的权限。所以,如果 root用户对某一脚本设置了这一权限的话则其他用户执行该脚本的时候则拥有了root用户权限。同理,guid意味着执行相应脚本的用户则拥有了该文件 所属用户组中用户的权限。举个例子:要对数据库系统进行备份需要有系统管理权限,那么我可以写几个脚本,并设置了它们的guid,这样我指定的一些用户只要执行这些脚本就能 够完成相应的工作,而无须以数据库管理员的身份登录,以免不小心破坏了数据库服务器。通过执行这些脚本,他们可以完成数据库备份及其他管理任务,但是在这 些脚本运行结束之后,他们就又回复到他们作为普通用户的权限。
赖半仙注:其实最好的办法还是,用root用户编写脚本,最后在设置脚本权限的时候给这个脚本加上suid,命令很简单chmod 4755 ooxx.sh 或者是chmod +s也行设置了suid后,切换回普通用户就可以执行这个脚本了,但切记有一点的是,脚本中调用root用户特权命令一定要像下面那样写可执行文件的绝对路径,不然普通用户找不到。#!/bin/shqqid=$(/sbin/pidof qq)echo "qq_pid=$qqid"
貌似可以实现在脚本里输入root密码,那 rsync ip::directory . && script.sh 一下子就可以搞定了。。。。