背景:
我在一家创业的公司, 公司开发机使用的是一台linux服务器, 多人公用, svn服务各内部管理平台的服务器也在这台机器上, 因此这台机器变得非常的重要. 虽然svn和数据库有定期备份,但不是天天备份. 数据一旦出现问题, 影响还是很大的.
作为小组的核心成员我拥有root密码, 偶尔会在这台开发机上用root权限装一些库, 升级工具, 部署几个管理平台(包括一些数据库的操作)什么的. 操作安全就是一个很重要的事情.
小心问题:
在之前的公司做服务器软件开发的时候, 受到过很多教育指导, 深知不要乱用root权限. 那个时候公司的权限管理不是很严格, 而server组作为核心组和以技术相对比较强健自称(也是事实), 相对来说使用root权限就更多 以前有同事在开发机上干过rm -rf / 这种事情, 当时我自己作为新人也干过用root手工替换升级stdc++的库文件失败导致用光盘去恢复系统的情况. 因此每每使用rm的时候都是小心翼翼, 后来也是非常的有信心, 不会乱删东西.
问题还是出现了:
今天早上, 我在公司用自已的账号去做一个源代码目录转移的工作, 转移完毕后. 为了检查结果, 做了一下make, 实然就哗哗的开始刷屏, 赶紧定睛一看, 卖糕的, 居然是在删除服务器上的根目录下的数据, 还好是用自己的账号登录的, 并没有权限删除那些数据. 还好我只是转移自己的代码. 要是整理公司代码使用root并没有换回普通账号就直接编译, 后果将不堪设想. 回想起以前有时su后忘了退出, 偶尔还是会用root编译自己的代码 . 就直冒冷汗, 于是马上决定解决这个问题.
找到原因 :
仔细看了一下makefile, 找到问题, 这是一个很多人喜欢写的makefile的方式, 包括几大互联网公司, 这种makefile的代码到处都是, 关键部分如下:
include ../makefile.common
SUB_PROJECT: sub1 sub2 .....
.....
.....
all: dir clean
....
dir:
if [ ! -d $(OUTPUT_PATH) ]; then mkdir $(OUTPUT_PATH); fi
if [ ! -d $(TARGET_INC_PATH) ]; then mkdir $(TARGET_INC_PATH); fi
if [ ! -d $(TARGET_LIB_PATH) ]; then mkdir $(TARGET_LIB_PATH); fi
clean: dir
@for d in $(SUB_PROJECT) ; do /
make clean -C $$d ; /
echo "" ;/
done
@echo ""
rm -rf $(OUTPUT_PATH) /*
作一个简单说明:
1. 部分的关键路径的定义, 包含在另一个文件中. 比如这里的$(OUTPUT_PATH)
2. 使用rm -rf这样的命令删除特定目录下的所有的字目录, 有的公司会把rm用别名的方式换成"rm -i", 所以有人为了在删目录时方便, 使用了-rf 这样危险的参数.
3 .这些命令还经常会被用到. 特别是在预发布程序时. 这些可能目录会上传到编译机上编译, 甚至用高权限编译(比如某用"掌"为图标的公司)
这次的问题就在这里. 我没有把这个包含的文件放到正确的路径下, 因此直接导致了$(OUTPUT_PATH)为未定义的. 而那个rm语句很显然就成了rm -rf /*.... 让人汗颜.
反省自己的坏习惯并分析:
平时因为不光使用公司的linux开发机, 自己有也装了多个ubuntu系统的机器作为开发机, ubuntu默认是不会开root密码的. 所有的高权限命令要使用sudo来完成, 但是却开放了sudo su这种命令, 有时我一次要装多个库时, 为了图方便, 经常使用sudo su升级到root做操作, 但与sudo直接执行不同, sudo一次后, 只有一小段时间是有权限不用输入密码做高级操作的. 而sudo su后, 因为其实su开启的shell只要不退出, 就会一直有高权限, 之后的操作会非常危险. 而在公司, 我也经常直接su进行系列的操作, 可能产生的破坏性就更大了.
解决办法
1 . 改正习惯, 但这个很难说, 因此要用下面更强硬的手段, 让自己就算不方便 也不能出现危险事件.
2. 严格限制可以sudo的账号,
包括
1>尽量减少可以使用sudo的相关人员, 即: 使用ubuntu的方式, 并不是使用root密码来sudo, 而是开一个可以sudo的账号, 必须先su成这个账号, 再才能sudo,
2>将root改成随机密码, 防止直接su到root, 禁用sudo的权限,防止通过sudo su来得到高权限.
3. 把make改名. 然后写程序对make进行封装, 程序流程如下:
<1> 检查自己是不是root, 如果不是, 直接调用原make, 结束
<2> 如果是root, 强制要求输入一串文本比如"我知道我用的是ROOT,并对危险可以预见", 否则不能进行
<3> 打开特定的文件, 比如/var/make/pid,
(a)防止多个终端用root编译, 在这个特定文件上是要加锁, 并写入TTY信息, 若已经有tty信息且不相符, 则中止此次编译
(b)考虑到make是可以嵌套的, 写入一个计数, 可以写入本次嵌套的相关信息.
(c)标志已经是root, 这样在嵌套编译时, 不用再次输入
<4> 每次一个嵌套的make进入和退出时, 做一下附加的检查, 看是否这个文件在make过程中被改动和信息是否正确, 如果有任何改动, 则提示并退出编译. 这主要是考虑到: 1 很难限制有人用root更改此文件, 2 有时必须要更改此文件, 比如编译过程中死机或异常退出导致文件没有被清理. 应当手动删除. 3 其它TTY下的ROOT账户在不知正在进行编译的情况下删除或更改此文件
<5> 编译完毕后, 删除此文件或标记已经退出 . 下次make的时候 必须要重新检查和输入
4. 封装rm命令, 检查少数特定的安全目录(可以从配置文件读取), 如果有, 则强制要求确认. 再执行删除操作
通过以上几点, 基本上可以防止误操作, 甚至是上文提到隐藏的误操作带来的意外,
当然, 恶意的破坏还是很难防的. 就不在这次的讨论范围内了