书摘:《Ubuntu Linux实用指南》第三版

作者:LJ Staff
摘自《Ubuntu Linux实用指南》第三版。
作者:Mark G. Sobell
由 Prentice Hall 出版
ISBN-10: 0-13-254248-X
ISBN-13: 978-0-13-254248-7

服务器安全

保护服务器的两种方法是使用 TCP wrappers 和设置 chroot jail。本节将介绍这两种技术。

TCP Wrappers:保护服务器(hosts.allow 和 hosts.deny)

当您开放本地系统以允许远程系统访问时,请遵循以下准则

  • 仅对您希望允许访问的系统开放本地系统。

  • 仅允许每个远程系统访问您希望其访问的数据。

  • 仅允许每个远程系统以适当的方式(只读、读/写、只写)访问数据。

libwrap

作为客户端/服务器模型的一部分,TCP wrappers 可以用于任何链接到 libwrap 的守护进程,它依赖于 /etc/hosts.allow/etc/hosts.deny 文件作为简单访问控制语言 (ACL) 的基础。这种访问控制语言定义了规则,根据客户端的地址和客户端尝试访问的守护进程,有选择地允许客户端访问本地系统上的服务器守护进程。ldd 的输出显示 sshd 的共享库依赖项之一是 libwrap

$ ldd /usr/sbin/sshd | grep libwrap
		libwrap.so.0 => /lib/libwrap.so.0 (0xb7ec7000)

hosts.allowhosts.deny

hosts.allowhosts.deny 文件中的每一行都具有以下格式

daemon_list : client_list [: command]

其中 daemon_list 是一个或多个服务器守护进程(例如 portmapvsftpdsshd)的逗号分隔列表,client_list 是一个或多个客户端的逗号分隔列表,可选的 command 是当 client_list 中的客户端尝试访问 daemon_list 中的服务器守护进程时执行的命令。

当客户端请求连接到服务器时,将按以下顺序查阅服务器系统上的 hosts.allowhosts.deny 文件,直到找到匹配项

  1. 如果守护进程/客户端对与 hosts.allow 中的一行匹配,则授予访问权限。

  2. 如果守护进程/客户端对与 hosts.deny 中的一行匹配,则拒绝访问。

  3. 如果在 hosts.allowhosts.deny 文件中没有匹配项,则授予访问权限。

第一个匹配项确定是否允许客户端访问服务器。当 hosts.allowhosts.deny 不存在时,就好像该文件为空。虽然不建议这样做,但您可以通过删除这两个文件来允许所有客户端访问所有守护进程。

示例

为了获得更安全的系统,请在 hosts.deny 中放入以下行以阻止所有访问

$ cat /etc/hosts.deny
...
ALL : ALL : echo '%c tried to connect to %d and was blocked' >> /var/log/tcpwrappers.log

此行阻止任何客户端连接到任何服务,除非在 hosts.allow 中明确允许这样做。当此规则匹配时,它会在名为 /var/log/tcpwrappers.log 的文件中添加一行。%c 扩展为客户端信息,%d 扩展为客户端尝试连接的守护进程的名称。

在配置了上述 hosts.deny 文件后,您可以在 hosts.allow 中包含显式允许访问某些服务和系统的行。例如,以下 hosts.allow 文件允许任何人连接到 OpenSSH 守护进程 (ssh, scp, sftp),但仅允许来自与本地系统和 192.168 子网上的用户相同的网络的 telnet 连接

$ cat /etc/hosts.allow
sshd: ALL
in.telnet: LOCAL
in.telnet: 192.168. 127.0.0.1
...

第一行允许从任何系统 (ALL) 连接到 sshd。第二行允许从与服务器 (LOCAL) 位于同一域中的任何系统连接。第三行匹配 IP 地址以 192.168. 开头的任何系统以及本地系统。

设置 chroot Jail

在早期的 UNIX 系统中,根目录是文件系统中的一个固定点。在现代 UNIX 变体(包括 Linux)中,您可以按进程定义根目录。chroot 实用程序允许您使用除 / 之外的根目录运行进程。

根目录出现在目录层次结构的顶部,并且没有父目录。因此,进程无法访问根目录之上的文件,因为不存在任何文件。例如,如果您运行一个程序(进程)并将其根目录指定为 /tmp/jail,则该程序将没有 /tmp 或更高级别目录中任何文件的概念:jail 是程序的根目录,并标记为 /(而不是 jail)。

通过创建一个人为的根目录,通常称为 (chroot) jail,您可以防止程序访问、执行或修改(可能是恶意地)从其根目录开始的目录层次结构之外的文件。您必须正确设置 chroot jail 才能提高安全性:如果您未正确设置 chroot jail,则可能会使恶意用户比没有 chroot jail 更容易访问系统。

使用 chroot

创建 chroot jail 很简单:使用 root 权限,给出命令 /usr/sbin/chroot directory. directory 成为根目录,进程尝试运行默认 shell。以下命令在(已存在的)/tmp/jail 目录中设置 chroot jail

$ sudo /usr/sbin/chroot /tmp/jail
/usr/sbin/chroot: cannot run command '/bin/bash': No such file or directory

此示例设置了一个 chroot jail,但当系统尝试运行 bash shell 时,操作失败。一旦 jail 设置完成,名为 jail 的目录就会采用根目录 / 的名称。因此,chroot 找不到由路径名 /bin/bash 标识的文件。在这种情况下,chroot jail 工作正常,但没有用。

让 chroot jail 以您想要的方式工作更加复杂。要使前面的示例在 chroot jail 中运行 bash,请在 jail (/tmp/jail/bin) 中创建一个 bin 目录,并将 /bin/bash 复制到此目录。由于 bash 二进制文件是动态链接到共享库的,因此您还需要将这些库复制到 jail 中。这些库放在 lib 中。

下一个示例创建必要的目录,复制 bash,使用 ldd 显示 bash 的共享库依赖项,并将必要的库复制到 liblinux-gate.so.1 文件是内核提供的动态共享对象 (DSO),用于加速系统调用;您无需复制它。

$ pwd
/tmp/jail
$ mkdir bin lib
$ cp /bin/bash bin
$ ldd bin/bash
        linux-gate.so.1 =>  (0x0032c000)
        libncurses.so.5 => /lib/libncurses.so.5 (0x00d4d000)
        libdl.so.2 => /lib/tls/i686/cmov/libdl.so.2 (0x0091d000)
        libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00110000)
        /lib/ld-linux.so.2 (0x0026a000)
$ cp /lib/{libncurses.so.5,ld-linux.so.2} lib
$ cp /lib/tls/i686/cmov/{libdl.so.2,libc.so.6} lib

现在再次启动 chroot jail。虽然所有设置都可以由普通用户完成,但您必须使用 root 权限才能运行 chroot

$ sudo /usr/sbin/chroot /tmp/jail
bash-4.1# pwd
/
bash-4.1# ls
bash: ls: command not found
bash-4.1# exit
exit
$

这次 chroot 找到并启动了 bash,它显示其默认提示符 (bash-4.1#)。pwd 命令有效,因为它是 shell 内置命令。但是,bash 找不到 ls 实用程序,因为它不在 chroot jail 中。如果您希望 jail 中的用户能够使用 ls,则可以将 /bin/ls 及其库复制到 jail 中。exit 命令允许您从 jail 中退出。

如果您为 chroot 提供第二个参数,它会将该参数作为要在 jail 中运行的程序的名称。以下命令与前面的命令等效

$ sudo /usr/sbin/chroot /tmp/jail /bin/bash

要设置有用的 chroot jail,首先确定 chroot jail 的用户需要哪些实用程序。然后将相应的二进制文件及其库复制到 jail 中。或者,您可以构建二进制文件的静态副本,并将它们放在 jail 中,而无需安装单独的库。(静态链接的二进制文件比其动态对应文件大得多。包含 bash 和核心实用程序的基本系统的大小超过 50 兆字节。)您可以在 bashcoreutils 源代码包中找到大多数常用实用程序的源代码。

除非您使用 root 权限运行 chroot 实用程序,否则它将失败——前面的示例使用 sudo 来获得这些权限。使用 root 权限运行 chroot 的结果是 root shell(具有 root 权限的 shell)在 chroot jail 内运行。由于具有 root 权限的用户可以突破 chroot jail,因此必须在 chroot jail 中以降低的权限(即,非 root 权限)运行程序。

有几种方法可以降低用户的权限。例如,您可以将 su 或 sudo 放入 jail 中,然后在 jail 中启动 shell 或守护进程,使用这些程序之一来降低在 jail 中工作的用户的权限。如下命令在 jail 内启动具有降低权限的 shell

$ sudo /usr/sbin/chroot jailpath /usr/bin/sudo -u user /bin/bash &

其中 jailpath 是 jail 目录的路径名,user 是 shell 在其权限下运行的用户名。此方案的问题在于,为 Ubuntu 编译的 sudo 和 su 会调用 PAM。要运行这些实用程序之一,您需要将所有 PAM(包括其库和配置文件)与 sudo(或 su)和 /etc/passwd 文件一起放入 jail 中。或者,您可以重新编译 su 或 sudo。但是,源代码会调用 PAM,因此您需要修改源代码以使其不调用 PAM。这两种技术都很耗时,并且会引入可能导致不安全 jail 的复杂性。

以下 C 程序[1] 在 chroot jail 中以降低的权限运行程序。由于此程序在调用 chroot() 之前获取您在命令行上指定用户的 UID 和 GID,因此您无需将 /etc/passwd 放入 jail 中。该程序将指定程序的权限降低到指定用户的权限。此程序作为前面问题的简单解决方案提供,以便您可以尝试 chroot jail 并更好地了解其工作原理。

$ cat uchroot.c

/ See svn.gna.org/viewcvs/etoile/trunk/Etoile/LiveCD/uchroot.c for terms of use. /

#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>

int main(int argc, char  argv[])
{
	if(argc < 4)
	{
		printf("Usage: %s {username} {directory} {program} [arguments]\n", argv[0]);
		return 1;
	}
	/ Parse arguments /
	struct passwd  pass = getpwnam(argv[1]);
	if(pass == NULL)
	{
		printf("Unknown user %s\n", argv[1]);
		return 2;
	}
	/ Set the required UID /
	chdir(argv[2]);
	if(chroot(argv[2])
		||
		setgid(pass->pw_gid)
		||
		setuid(pass->pw_uid))
	{
		printf("%s must be run as root.  Current uid=%d, euid=%d\n", 
				argv[0],
				(int)getuid(),
				(int)geteuid()
				);
		return 3;
	}
	char buf[100];
	return execv(argv[3], argv + 3);
}

以下命令中的第一个编译 uchroot.c,创建一个名为 uchroot 的可执行文件。后续命令将 uchroot 移动到 /usr/local/bin 并赋予其适当的所有权。

$ cc -o uchroot uchroot.c
$ sudo mv uchroot /usr/local/bin
$ sudo chown root:root /usr/local/bin/uchroot
$ ls -l /usr/local/bin/uchroot
-rwxr-xr-x 1 root root 7922 2010-07-17 08:26 /usr/local/bin/uchroot

使用本节前面部分的设置,给出以下命令以在 chroot jail 内以用户 sam 的权限运行 shell

$ sudo /usr/local/bin/uchroot sam /tmp/jail /bin/bash

维护多个 chroot jail - 如果您计划部署多个 chroot jail,最好将 binlib 目录的干净副本保存在活动 jail 之外的其他位置。

在 chroot Jail 中运行服务

在 jail 中运行 shell 的实用性有限。实际上,您更可能希望在 jail 中运行特定服务。要在 jail 中运行服务,请确保该服务所需的所有文件都在 jail 中。使用 uchroot,在 chroot jail 中启动服务的命令格式为

$ sudo /usr/local/bin/uchroot user jailpath daemonname

其中 jailpath 是 jail 目录的路径名,user 是运行守护进程的用户名,daemonname 是提供服务的守护进程的路径名(在 jail 内)。

某些服务器已经设置为利用 chroot jail。例如,您可以设置 DNS 以便 named 在 jail 中运行,并且 vsftpd FTP 服务器可以自动为客户端启动 chroot jail。

安全注意事项

某些服务需要由具有 root 权限的用户或进程运行,但在启动后会释放其 root 权限(例如 Apache、Procmail 和 vsftpd)。如果您正在运行此类服务,则无需使用 uchroot 或将 su 或 sudo 放入 jail 中。

root 权限运行的进程可能会从 chroot jail 中逃脱。因此,您应该在启动 jail 内运行的程序之前降低权限。此外,请注意您允许在 jail 中使用的 setuid 二进制文件——其中一个中的安全漏洞可能会损害 jail 的安全性。此外,请确保用户无法访问他上传到 jail 的可执行文件。

脚注

[1]感谢 David Chisnall 和 Étoilé Project (etoileos.com) 提供 uchroot.c 程序。

 

此摘录来自 Mark Sobell 的《Ubuntu Linux实用指南》第三版,由 Pearson/Prentice Hall Professional 出版,2010年8月,ISBN 013254248X,版权 2011 Mark G. Sobell,要查看完整目录,请访问:www.informit.com/title/013254248X

作者联系方式

 

www.sobell.com
twitter.com/marksobell

© 版权所有 Mark G. Sobell。保留所有权利。

加载 Disqus 评论