当前位置: 首页 > 科技观察

写了一个bug,错误的执行了rm-fr--,瞬间爽了!

时间:2023-03-13 04:53:12 科技观察

前几天,五一节将至,想着5天的假期,我开始飘飘然了。于是就顾不上写了一个简单的Bash脚本,写完也不去检查,直接拖到物理服务器上跑一下。图片来自Pexels。它一开始运行,就出现了问题。为什么一个简单的脚本跑了10秒没跑完,我直接Ctrl+C一下就停止了正在运行的脚本。然后,习惯性的输入ls,结果是什么?找不到ls命令?一瞬间背后一凉,慌张的打开了剧本。找到问题了,我写了一个巨大的愚蠢的bug,间接执行了rm-fr/*,这是否意味着我删除了数据库?这是公司的授权服务器,通过公司的历史授权。记录和其他重要信息不会丢失吗?我惊慌失措,把这件事告诉了我的朋友。朋友建议我尽快向领导反映,不要隐瞒删库。于是,我把我删库的事情告诉了领导,以为会被批评。结果,Leader笑道:“没关系,你看看重要的文件还在不在。不过等你整理了这么一会,我突然想起来编译服务器已经半年没有备份了,我删除了库。”我:”????”各位吃瓜群众,你们觉得我是不是要删库跑路了?哈哈哈,我不是跑路了,而是恢复了,所以说说我是怎么恢复删库的。在初步调查案发现场,先看看我写的垃圾代码,是怎么造成这个数据库删除的:既然出现了rm-fr/*的现象,那么变量new_lic_dir肯定是空的,所以,当语句rm-fr$new_lic_dir/*执行后,就变成了删除库的语句rm-fr/*太好了,凶器找到了,那为什么new_lic_dir是空的呢,细心的朋友一定注意到了,因为when用了反引号给new_lic_dir变量赋值没错,就是反引号的由来。反引号在LinuxShell命令行中有特殊的含义:反引号之间的内容会先被Shell执行,它的输出放到main中后命令,然后执行主命令。也就是说,new_lic_dir的值是执行${lic_path}/new_license命令的结果。问题是,这不是一个命令,所以它必须向new_lic_dir变量返回一个空值。我写的温柔的代码变成了恶意的删库代码。原因是我找到了,反引号要改成双引号。(内心OS:你真好,这么简单的赋值命令都写错了!)哈哈哈,确实不错,我都说了,都快5月1日了,仓促写下这段代码。于是习惯性的打开程序员内容的第一大武功:Crtl+C和Crtl+V。我复制粘贴了第一个assignmentlic_path=`pwd`语句,然后只改了变量名,没注意把反引号改成双引号,结果造成了删库的悲剧。保留案发现场由于库已被删除,所以不要重启服务器或关闭SSH连接的会话,而是保留案发现场,然后查看还剩下什么。PS:这不是吹炮吗?ls不见了,我怎么查?好在这次我运气不错,因为我在执行脚本的时候,第一时间发现了不对,立马把还在运行的脚本给砍掉了,所以才没有把Linux的所有文件都删掉。只要我捏得快,rm-fr/*就打不死我。虽然ls删掉了,幸好发现cd命令还能用。只要用好cd,也可以使用ls效果。很简单,只需cd+Tab键就会自动出现指定目录下的所有文件。通过cd+Tab键,我们可以查看各个目录下的文件,这样我们就可以一步步确认哪些系统文件被删除了。经过一番确认对比,发现删除的主要目录有4个:/bin、/boot、/dev都被删除了。/lib目录中的动态库部分已被删除。我们再回顾一下上面四个目录主要存放的是什么:/bin存放的是常用的系统命令,比如ls、cp、rm、chmod等都在这个目录下。/boot系统启动目录,保存与系统启动相关的文件,如内核文件和bootloader。/dev设备文件存放位置。/lib存放程序需要的动态库和静态库文件。/boot已被删除。幸运的是,小林并没有重启服务器。重启服务器就完了,系统肯定启动不了。cd命令在/sin目录下,/sin还健在,所以cd可以正常使用。好在重要的数据库信息和文件没有被删除,所以小林的首要目标是恢复/bin、/boot、/dev、/lib这四个目录。恢复文件由于删除了/bin目录和/lib目录下的一些动态文件,导致常用的文件传输方式无法使用,如ftp、scp、mount等。找了半天,发现wget可以用。wget命令在/usr/bin目录中。幸运的是,/usr/bin仍然健康。于是,我用了一个取巧的方法,先放另外一个正常的服务器,把/bin目录放到Web服务器的Web目录下,然后通过wget下载。有戏,便见成功的曙光。但是新的问题来了。我下载的命令文件没有执行权限。而chmod命令在/bin目录下,也被删除了,不能用来赋予文件权限。尽管如此,我还是在网上找到了一个很棒的命令perl,可以用来给文件权限:perl-e"chmod777,'ls'"真是一个神奇的命令。好了,分配权限的问题也解决了,成功在望。wget不能直接下载/bin目录,只能下载一个文件。但是我不可能一个一个下载恢复。要多少年几个月才能完成……于是想到了一个方法:先通过wget下载tar命令,通过perl权限给tar命令。然后另一台服务器将/bin目录打包成压缩文件,然后通过wget下载bin目录的压缩文件。最后,使用tar命令解压bin存档。/bin就这样恢复了,其余的目录也通过同样的操作恢复了。我的笑容渐渐恢复了,哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈!遇到rm-fr/*数据库删除事件,一定要冷静,稳住心态!之所以能够幸运的从这次删库事件中恢复过来,是因为有两点很重要:如果发现脚本执行异常,果断立即切断,不会导致重要的数据库信息被删除。如果以后再剪掉,可能真的就没了。当发现不能使用常用命令时,服务器没有重启,否则服务器不会启动,SSH会话也没有关闭,否则无法重新连接SSH会话,无法进行操作。如果以上两点没做好,服务器恢复的难度会增加很多,更严重的是五一就没了。防止误执行rm-fr/*既然rm-fr/*是一件残忍的武器,那么防止它是很有必要的。接下来,我们将讨论几种预防方法。选项一:rm-rf删除目录判断目录#!/bin/bashwork_path=`pwd`#如果目录不为空则进行删除操作if[${work_path}!=""];thenrm-fr${work_path}/*fi在执行删除目录操作之前,首先判断要删除的目录是否为空,不为空才执行删除操作。方案二:shell脚本指定set-u执行脚本时,如果遇到不存在的变量,Bash默认忽略。#!/bin/bashecho$aechohello上面代码中$a是一个不存在的变量,执行结果如下。$bashtest.shhello可以发现echo$a输出的是空行,Bash忽略不存在的$a,然后继续执行echohello。最好遇到不存在的变量,脚本应该报错,而不是默默执行。set-u用于更改此行为。如果添加到脚本中,遇到不存在的变量就会报错并停止执行。#!/bin/bashset-urm-fr$a/*echohello运行结果如下:$bashtest.shtest.sh:line4:a:unboundvariable可以看到因为a是未定义变量,所以脚本报错并且不再执行下面的语句。解决方案3:safe-rm替换rmsafe-rm是一个开源软件工具。这个名字听起来很安全,所以用来代替不太安全的rm。可以在/etc/safe-rm.conf中配置路径黑名单,定义哪些不能被safe-rm删除。我们可以将safe-rm重命名为rm。假设不能删除/etc/,删除/etc时会报错:$rm-rf/etc/safe-rm:skipping/etc/方案四:创建回收站机制Windows有回收站,即使误删除了,可以在回收站中恢复。因此,我们也可以在Linux中实现回收站的机制。实现思路:删除文件时,并不真正执行删除操作,而是将文件移动到特定目录,可以设置定时清空回收站,或者当回收站中的文件大小达到一定的时候capacity(或者用时间??来判断)执行删除操作腾出空间。可以写一个shell脚本来代替rm命令,或者在需要删除文件的时候使用mv命令将文件移动到回收站。①创建回收站目录mkdir/home/.trash②编写remove.sh脚本,内容如下:③修改~/.bashrc,将rm命令替换为我们自建的remove.sh:aliasrm="sh/home/remove.sh"④设置crontab定时清空垃圾桶,比如每天0:00清空垃圾桶***rm-rf/home/.trash/*⑤最后执行如下命令使生效:source~/.bashrc解决方法5:在/etc/fstab文件中以只读方式挂载根文件,将/文件系统以只读方式挂载。其中remount,ro表示以只读方式挂载。只读方式挂载后,删除操作无法成功:事后反思rm-fr命令相关代码,多多留意,反复检查,做好防止rm-误执行的工作fr/*,并在测试机验证通过后,拖到物理机上运行。不要大意。即使rm-fr/*发生了,也尽快停止,做好三件事:不要惊慌,不要心跳加速(保持平稳)不要隐藏数据库删除事件(不要被惭愧)不要重新启动服务器或断开ssh会话(保持现场)只是立即切断rm-fr/*,它不会杀死我们。使用当前环境中剩余的命令,冷静分析,有机会恢复。我现在是删库不跑的男人,再见,下次见。作者:小林编辑:陶佳龙来源:转载自微信公众号小林编码(ID:CodingLin)