网上有各种chroot资料,但是似乎没有完整明确的chroot MySQL的介绍,结合工作,将我进行chroot MySQL的方法说明一下。
因为Linux发行版本众多,首先说明运行环境:CentOS 5.9, MySQL 5.5,所有chroot环境下的文件都从父环境拷贝过来,适用于无网络或网络受限环境下的安全加固。
chroot的作用不做过多介绍,部分内容可以参考我以前翻译的博文《Unix chroot最佳实践》。
在CentOS下,MySQL服务调用关系大概是/etc/init.d/mysql==>/etc/init.d/mysql-real==>/usr/bin/mysqld_safe,当然这只是最主要的一条线。其实,如果不使用service命令或者/etc/init.d/mysql来启停服务的话, 我们运行MySQL只需要下面的命令:
/usr/bin/mysqld_safe --datadir="/var/lib/mysql" --pid-file="/var/lib/mysql/mysqld.pid"
启动后就可以使用mysql命令来操作数据库了。
选择在哪一个点来做chroot直接决定了难易程度,理论上来说,chroot环境越小越好,也就是在调用关系的越后面越好,因为越往前,需要放到chroot监狱中的东西也就越多。但是发现这种方案直接导致工作量成倍增加,因为越往后,脚本的分支越多,需要处理的点也就越多,潜在的问题也会越多,这需在安全和成本之间做个妥协。最终,我选择直接在/etc/init.d/mysql中进行chroot。CentOS的chroot命令很简单:chroot NEWROOT [COMMAND...],第一个参数是chroot的路径,第二个参数是要执行的命令,默认第二个参数是sh -i。
详细步骤说明如下:
选择/var/chroot/mysql作为根目录,参照父环境,将顶层目录依次建立起来,包括:/etc, /bin, /sbin, /tmp/, /dev, /lib64, /usr, /var,用到的次级目录后面会有介绍。完成后目录结构下图所示:
这一步可能因系统而异,下面列出的大部分都是在验证过程中通过错误信息逐步加进去的。根据chroot最佳实践,chroot监狱要越小越好,切忌将所有库文件及可执行文件一股脑全部复制过来,因为这些程序很可能被黑客所利用,降低了系统安全性。
依赖的库文件如下,库文件可以使用ldd命令查询得到:
/lib64/libtermcap.so.2 /lib64/librt.so.1 /lib64/libdl.so.2 /lib64/libssl.so.6 /lib64/libcrypto.so.6 /lib64/libpthread.so.0 /lib64/libcrypt.so.1 /lib64/libnsl.so.1 /lib64/libm.so.6 /lib64/libgcc_s.so.1 /lib64/libc.so.6 /lib64/ld-linux-x86-64.so.2 /lib64/libcom_err.so.2 /lib64/libresolv.so.2 /lib64/libkeyutils.so.1 /lib64/libselinux.so.1 /lib64/libsepol.so.1 /lib64/libnss_compat.so.2 /lib64/libnss_files.so.2 /usr/lib64/libz.so.1 /usr/lib64/libaio.so.1 /usr/lib64/libstdc++.so.6 /usr/lib64/libgssapi_krb5.so.2 /usr/lib64/libkrb5.so.3 /usr/lib64/libk5crypto.so.3 /usr/lib64/libkrb5support.so.0 /var/chroot/mysql/usr/lib64/以及/usr/lib64/mysql目录下全部文件。
可执行文件被MySQL脚本调用,包括:
/sbin/pidof /bin/sh /bin/hostname /bin/basename /bin/sleep /bin/nice /bin/sed /bin/cut /bin/touch /bin/chown /usr/bin/wc /bin/rm /bin/date /bin/grep /bin/chmod /bin/cat /usr/bin/expr /usr/bin/test /usr/bin/dirname /usr/bin/innochecksum /usr/bin/perror /usr/bin/replace /usr/bin/resolve_stack_dump /usr/bin/resolveip /usr/sbin/mysqld以及 /usr/bin/下所有已my开头的文件。
配置文件包括:/etc/hosts, /etc/my.cnf, /etc/passwd, /etc/group,这里要注意,passwd和group文件需要将除mysql用户和组以外的内容全部删除。
其实这一步完成后,就可以执行chroot命令,使用shell进入chroot环境了,当然只能使用已经复制过来的系统命令。因为没有ls,所有你就看不了目录内容,调试的时候可以多拷贝一些系统命令过来,通过命令行直接启动mysql,看错误提示,进行修改。
因为从/etc/init.d/mysql就开始chroot,所以后面用到的脚本都要拷贝过来,如/etc/init.d/mysql-real。
还有MySQL的数据文件,在父系统的/var/lib/mysql目录中。
到目前为止,发现只需要建立/dev/null就可以
mknod -m 666 /var/chroot/mysql/dev/null c 1 3
这里可以根据安全需要进一步收紧权限。
#!/bin/bash REALSCRIPT=/etc/init.d/mysql-real ARGS='' # don't double-start, as mysql kills the pid file if [ "$1" = "start" ]; then /usr/sbin/chroot /var/chroot/mysql $REALSCRIPT status >/dev/null 2>/dev/null if [ $? -eq 0 ]; then /usr/sbin/chroot /var/chroot/mysql $REALSCRIPT status exit $? fi fi # pass additional parameter to mysql on start / restart if [ "$1" = "start" ]\ ||[ "$1" = "restart" ]\ ||[ "$1" = "reload" ]\ ; then # reverting additional argument ARGS="--max_allowed_packet=128M $ARGS" : fi /usr/sbin/chroot /var/chroot/mysql $REALSCRIPT $1 $ARGS
这一步完成后,如果没什么问题,我们已经可以通过"service mysql start"来启动mysql,使其运行在chroot环境下。但是你会发现当你用mysql来试图连接数据库时却返回“ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)”的错误。这是因为mysql客户端程序会默认读取my.cnf中的sock配置来连接数据库,而我们配的是/var/lib/mysql/mysql.sock,很明显,mysql运行在chroot环境中,在父环境的/var/lib/mysql目录中并没有sock文件,怎么办呢?建立软链接,当然你也可以指定socket参数。搞定!
在chroot环境下使用软链接时注意,只能将chroot监狱中的文件链接到外面,而不能反过来,反过来的话在chroot监狱中是无法读取的。
这种方案除了修改了/etc/init.d/mysql脚本外,其他配置文件和脚本都不需要修改,工作量应该是最小的。但是这种方案的安全性如何呢?自己不是黑客,没有尝试过,以后如果发现有漏洞的话会即使更新本文。但上次一个安全专家确实给我们演示过通过一个安全性不强的MySQL攻入系统,获得root权限,他怎么做到的呢?不好意思,都忘了。
再简单介绍一下如何判断应用程序是否被成功chroot,找一下男人(man),有这么一说:
/proc/[pid]/root
UNIX and Linux support the idea of a per-process root of the filesystem, set by the chroot(2) system call. This file is a symbolic link that points to the process's root directory, and behaves in the same way as exe, and fd/*.
说白了就是你先用ps找到应用程序的PID,然后查看/proc/[pid]/root这个文件,如果是"/",说明你没有chroot成功,反之,可能搞定了。我的环境下
如果有高手路过,请不吝赐教如何验证chroot环境下mysql的安全性,叩谢。