Paranoid Penguin - 使用 Stunnel 修复明文网络应用程序
世界上充斥着各种网络应用程序,它们功能强大、经受住了时间的考验,但安全性却很糟糕。Telnet?简单而通用,堪称杰作,但它以明文传输您的登录凭据。rcp?无处不在且易于编写脚本,但基于 rhosts 的身份验证时代已经过去,这要归功于 IP 欺骗。
当然,您可以将这些老旧的工具替换为加密的替代品——使用 SSH 代替 telnet,使用 scp 或 rsync-over-SSH 代替 rcp。或者,您可以为每个与您通信的远程主机构建一个通用的 IPSec 隧道。但后者通常矫枉过正,而如果特定场景中应用程序的选择并非完全由您控制,则前者说起来容易做起来难。肯定有一种方法可以为旧的网络应用程序添加强大的加密功能。
考虑一下强大的 SSL 包装器 Stunnel。本月,我将解释如何结合使用 Stunnel 4.0 和 OpenSSL,在安全方面将您的旧应用程序带入现代。无线网络呢?如果您不得不通过安全性较弱的无线介质(如 802.11b)使用明文网络应用程序,Stunnel 能否提供帮助?请继续阅读。
为了使用 Stunnel,您需要了解两件事。首先,了解您的网络应用程序如何使用网络。如果它是一个简单的单 TCP 端口应用程序,例如 telnet,它在其所有监听都在 TCP 23 端口上进行,那么 Stunnel 可以工作。如果它使用 UDP、端口映射器服务或任何其他动态端口分配方案,Stunnel 就无法帮助您。例如,RPC 应用程序不起作用,因为它们使用端口映射器。FTP 使用 TCP 21 进行控制流量,但动态分配任意高端口用于数据连接,因此它也不符合条件。
其次,您需要了解公钥密码学的基本知识,但不一定需要了解 X.509 或公钥基础设施的来龙去脉。我在之前的几篇文章中描述了它的工作原理,例如“OpenSSH 的 101 种用途,第二部分”[LJ,2001 年 1 月]。目前,只需知道在公钥密码学中,每个参与者都有两个密钥:一个与其他人共享的公钥,以及一个只有您自己拥有的私钥。其他人使用您的公钥来加密他们只想让您看到的东西;您使用您的私钥来解密这些东西。
数字签名的工作原理与加密相反。如果您使用您的私钥对某物进行签名,任何人都可以使用您的公钥来验证该签名是否是用您的私钥生成的,因此是由您生成的。同样,这取决于只有您拥有您的私钥,无论有多少人拥有您的公钥副本。
在 X.509 世界中,我们将公钥称为证书,从技术上讲,它是一个与数字签名信息捆绑在一起的公钥,其中包含公钥所有者的姓名和电子邮件地址。我们将私钥简称为密钥。有些令人困惑的是,我们有时将它们两者一起称为证书。上下文有所帮助:当我谈论无密码短语证书时,您知道我谈论的是组合密钥/证书,因为证书本身是公钥,不可能有密码短语。
这绝对是我对公钥密码学和 X.509 最简洁的解释。如果这不足以让您理解本文的其余部分,请阅读 Stunnel FAQ 或强大的 RSA Crypto FAQ(请参阅在线资源部分)以获取更多信息。现在是深入了解 Stunnel 本身的时候了。
您的 Linux 发行版很可能包含 Stunnel 的二进制软件包。SuSE、Fedora 和 Red Hat Enterprise 的最新版本都包含 Stunnel 4 版本。Debian 3.0 (Woody) 包含 Stunnel 3.22 版本。
一方面,3.22 是一个稳定的版本,文档齐全且易于理解。另一方面,Stunnel 4 版本是一个主要的重写版本,除其他外,它允许更轻松地管理多个隧道。这是我在这里介绍的版本。如果您运行 Debian,我认为值得您下载最新的 Stunnel 源代码并自行编译。
在任何 Linux 发行版上编译 Stunnel 都快速而简单。首先,确保您已经安装了发行版的 OpenSSL 软件包,可能称为 openssl;OpenSSL 开发库,openssl-devel 或 libssl096-dev;以及 TCPwrapper 开发库,Debian 上的 libwrap0-dev,包含在 SuSE 和 Fedora 的基本安装中。然后,解压缩 Stunnel 的源代码 tarball 并快速执行
./configure && make && make install
如果由于某种原因这不起作用,请输入./configure --help列出您可以传递给 configure 脚本的高级预编译配置选项。安装 Stunnel 后,就可以创建一些证书并开始隧道传输了。
以下大部分内容仅适用于 Stunnel v.4.0.0 及更高版本。如果您选择使用,例如 Debian 的 Stunnel 3.22 软件包,则需要参考该软件包随附的文档或 Stunnel 网站上的示例(请参阅资源);该网站的大部分内容仍然涵盖旧版本。
Stunnel 服务器(即接收发送到本地明文服务的加密数据包的主机)需要服务器证书。但是,Stunnel 客户端不需要,除非您打算将 Stunnel 服务器配置为使用客户端证书身份验证。遗憾的是,客户端证书身份验证超出了本文的范围,因此我们的示例都不需要客户端拥有自己的证书——只有服务器需要。
如果您从源代码编译后安装了 Stunnel 并发出make install命令,您已经拥有一个服务器证书 (/usr/local/etc/stunnel/stunnel.pem)。如果您从二进制软件包安装了 Stunnel,则该软件包的安装后脚本可能已为您创建了服务器证书,也可能没有。
Fedora Core 1 的 Stunnel RPM 出于某种原因创建了一个空证书。不用说,您需要一个合适的证书,在 Fedora 上执行此操作的方法是将您的工作目录更改为 /usr/share/ssl/certs,键入make stunnel.pem并将 stunnel.pem 复制到目录 /etc/stunnel。此证书与 Stunnel 源代码发布 Make 脚本创建的证书一样,没有密码短语。
无密码短语证书的风险
使用脚本自动或半自动生成的服务器证书既快速又方便,但有一个问题:此类证书几乎总是创建时没有密码短语。一方面,您可以并且应该设置证书的权限和所有权,使其仅root 可读,这样至少系统上非特权用户无法读取并因此使用它。另一方面,如果您的系统 root 权限被泄露,您的无密码短语证书则可能被攻击者使用和滥用。
对于您来说,这可能是可以接受的风险,事实上,使用无密码短语服务器证书是一种常见的做法。毕竟,每次 Stunnel 启动时都要求人为输入证书的密码短语是不方便的。但是,作为一项原则,密码学专家普遍认为使用无密码短语证书是鲁莽的。有人认为,如果一个进程足够敏感,值得首先进行加密,那么它也足够敏感,需要人为启动该进程。
如果您的 Stunnel 服务器系统需要服务器证书,或者如果您的自动生成的证书不符合您的需求(例如,它缺少密码短语),您需要使用 OpenSSL 命令生成自己的证书。篇幅有限,我无法对这个强大的工具进行完整、适当的解释。幸运的是,Stunnel FAQ(请参阅资源)回答了关于 OpenSSL 和 Stunnel 如何交互的一些最常见问题,并且它包括指向关于 OpenSSL 的更多信息来源的指针。
可以肯定地说,对于某些用户来说,可能有一个令人信服的理由来创建他们自己的证书颁发机构 (CA),在运行 Stunnel 的每个系统上安装他们的 CA 证书(但不是密钥),并使用他们的 CA 证书来签署所有服务器证书。例如,如果您希望您的 Stunnel 服务器系统仅接受来自具有特定证书的 Stunnel 客户端主机的客户端连接,则这是必要的。
但是,对于许多(如果不是大多数)Stunnel 用户来说,使用自签名证书并且不担心成为或不成为 CA 就足够了。以下是如何使用 OpenSSL 生成自签名服务器证书。
列表 1. 使用 OpenSSL 创建服务器证书
nearclient:/etc/stunnel# openssl req -x509 \ -newkey rsa:1024 -days 365 \ -keyout stunnel.pem -out stunnel.pem Using configuration from /usr/lib/ssl/openssl.cnf Generating a 1024 bit RSA private key ...++++++ .............................................++++++ writing new private key to 'key2.pem' Enter PEM pass phrase: ************ Verifying password - Enter PEM pass phrase: ****** ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:US State or Province Name (full name) []:Minnesota Locality Name (eg, city) []:St. Paul Organization Name (eg, company) []:Wiremonkeys Organizational Unit Name (eg, section) []: Common Name (eg, YOUR name) []:nearclient.wiremonkeys.org Email Address []:X.509master@wiremonkeys.org nearclient:/etc/stunnel# chmod 600 stunnel.pem
在列表 1 中,真正的核心在第一行。从左到右解析,此命令告诉 OpenSSL 创建一个 X.509 数字证书格式的证书请求,使用 RSA 密码和 1,024 位密钥,有效期为 365 天,并将其密钥和(公共)证书写入同一个文件 stunnel.pem。如果您想创建一个无密码证书,您可以在末尾包含 -nodes 选项。但请先阅读侧栏“无密码短语证书的风险”。
列表 1 的其余大部分内容显示了一个对话框,OpenSSL 在其中提示我输入要包含在证书中的 X.509 类型信息,包括国家/地区。这些内容应该是不言自明的,但通用名称非常重要。如果您要创建服务器证书,则证书的通用名称必须设置为将要使用该证书的主机的完全限定域名(即完整主机名)。
大多数启用 SSL/TLS 的应用程序都希望此通用名称通过 DNS 解析为您的服务器的 IP 地址。除非您配置 Stunnel 这样做,否则 Stunnel 不会这样做,但将通用名称设置为您的 FQDN 仍然是一个值得养成的好习惯。
在列表 1 的最后一行中,我将新服务器证书的权限设置为 0600 (-rw-------)。因为我以 root 身份运行了 OpenSSL 命令,所以此文件已经归 root 所有。对于任何服务器证书,无论它是否受密码保护,都必须仅root 可读。我从 /etc/stunnel 中运行了我的 OpenSSL 命令,所以我的证书是在本地创建的,我无需手动将我的新证书移动到那里。
获得合适的服务器证书后,就可以配置隧道了。这比之前的任务要简单得多。这部分也更具体于版本。在 4.0 之前的 Stunnel 版本中,Stunnel 接受所有配置参数作为命令选项。但是,在当前版本中,它期望的唯一可操作的命令行参数是其配置文件的非默认路径。
如果您使用默认编译时选项从源代码安装了 Stunnel,则 Stunnel 期望其配置文件位于 /usr/local/etc/stunnel 中。如果您从二进制软件包安装,则此路径更可能是 /etc/stunnel。列表 2 显示了缩写示例 stunnel.conf 文件中的全局设置(缩写主要是因为我省略了注释行)。
列表 2. stunnel.conf 中的全局设置
cert = /etc/stunnel/stunnel.pem chroot = /var/run/stunnel/ pid = /stunnel.pid setuid = nobody setgid = nogroup debug = 7 output = /var/log/stunnel.log client = yes
cert 参数告诉 Stunnel 在哪里查找其服务器证书;因此,只有在您的 Stunnel 服务器主机上才需要此参数,而不是在客户端主机上。chroot 参数告诉 Stunnel 在启动后将其自身 chroot(将 / 重置为)到哪个目录;这发生在 Stunnel 读取其配置和服务器证书文件之后。您可能需要手动创建此 chroot 监狱,并使用一些东西填充它,例如,它自己的 etc/hosts.allow 和 etc/hosts.deny 文件,如果您想使用 TCPwrappers 样式的访问控制。
pid 告诉 Stunnel 在哪里写入其进程 ID。此路径相对于 chroot 设置的路径;也就是说,Stunnel 在 chroot 自身后写入其 PID。
setuid 和 setgid 告诉 Stunnel 在启动后将其降级为哪个用户和组。如果 Stunnel 要侦听任何低于 1025 的 TCP 端口,则必须以 root 身份启动,但它会在读取其配置文件、读取其服务器证书并绑定到特权端口后降级自身。
默认情况下,Stunnel 将严重性为 notice 或更高的日志消息发送到本地 dæmon syslog 工具。Fedora 版本将其发送到 authpriv,authpriv 又记录到 /var/log/secure。您可以使用 debug 选项设置不同的日志级别。七是最高级别,如果您在让 Stunnel 工作时遇到问题,最好使用七级。您可以使用 output 选项告诉 Stunnel 将其消息发送到特定文件,而不是将其消息交给 syslog。
列表 2 中的最后一行将 client 参数设置为 yes,这意味着在此特定系统上,我打算发起 SSL 事务,而不是接收它们。在我要与之通信的服务器上,我需要将此参数设置为其默认值 no。
现在,最后,我们来到了回报——一个实际的隧道。对于此示例,我们将对从主机 nearclient 到服务器 farserver 的 telnet 进行隧道传输。farserver 的 stunnel.conf 文件中的全局部分几乎可以与列表 2 中的全局部分相同,除了 client 需要设置为 no。两个主机配置之间的主要区别在于其服务定义。
但是,在我深入探讨之前,让我们更详细地说明示例场景。假设 farserver 已经配置为 telnet 服务器;它已经在 TCP 端口 23 上接受 telnet 会话。但是,我们不希望 nearclient 连接到明文端口;我们需要使用其他东西进行 SSL 连接。碰巧的是,IANA 已经为启用 SSL 的 telnet(又名 telnets)指定了一个端口:TCP 992。
因此,我们想要一个从 nearserver 到 farserver 上 TCP 992 端口的隧道。但是,我们的非 SSL 启用的 telnet 命令和我们同样不精通 SSL 的 telnet 服务器进程将如何知道如何使用此隧道?这是一个技巧性问题;隧道对于发送和接收 telnet 进程完全透明。
nearserver 的 Stunnel 进程在常用端口(TCP 23,尽管这是用户定义的)上接受连接,然后在将数据包转发到 farserver 上的 TCP 端口 992 之前使用 SSL 对数据包进行加密。farserver 解密数据包,然后将它们转发到其本地 telnet 进程上的 TCP 23。实际上,xinetd 或 inetd 在 in.telnetd 之前接收数据包,但您明白了。
这样,当 nearserver 上的用户想要连接到 farserver 时,他们输入命令telnet 127.0.0.1,连接被加密,转发到 farserver 并解密。farserver 的回复数据包遵循相同的路径,但方向相反。每个 telnet 进程(telnet 和 in.telnetd)都认为它正在与本地用户通信,但数据包实际上正在遍历 SSL 加密的 Stunnel 会话。所有这些都是冗长地解释构成列表 3 和 4 的总共六行的原因。
列表 4. farserver 上的服务定义
[telnets] accept = 992 connect = 23
如您所见,服务定义可以像一对行一样简单,一个 accept 行指定侦听端口,一个 connect 行指定目标端口。这究竟意味着什么取决于给定系统是 Stunnel 客户端还是 Stunnel 服务器。在客户端系统上,client = yes,Stunnel 期望在其接受端口上接收明文数据包,并将加密数据包发送到连接端口。在服务器系统上,client = no,Stunnel 在其接受端口上侦听 SSL 连接(加密数据包),并将解密的数据包发送到连接端口。
另请注意,在客户端上,在您的 connect 语句中,您可能不仅需要指定端口,还需要指定远程主机名或 IP 地址,如列表 3 所示,connect = farserver.wiremonkeys.org:992.
根据我们在列表 3 和 4 中配置的方式,任何主机都可以连接到 nearserver 上的 TCP 端口 23 并遍历 nearserver 到 farserver 的隧道。我们可以通过多种方式限制这一点,包括使用 iptables。但是,我们可以通过使用accept = 127.0.0.1:23在 nearclient 上告诉 Stunnel 仅接受与 Stunnel 客户端进程的本地连接。
此技术也适用于 farserver。如果 farserver 有多个接口,eth0、wlan0、ppp0 等等,您可以以相同的方式编写您的 accept 语句以指定您希望它在其上接收隧道数据包的接口的 IP 地址。在 Stunnel 客户端和服务器上,accept 语句的默认 IP 是 any(所有本地接口),connect 语句的默认 IP 是 127.0.0.1(localhost)。
farserver 上的 telnet 服务器呢?毕竟,明文 telnet 会话很少是一个好主意。因此,在实践中,您需要确保将行bind = 127.0.0.1添加到您的 /etc/xinetd/telnet 脚本中,以便只有本地进程可以连接到 TCP 23。
在客户端和服务器上完成 Stunnel 配置后,只需键入命令stunnel即可启动 Stunnel。您无需担心先在服务器上启动它,然后再在客户端上启动它;在您尝试使用隧道之前,客户端不会启动隧道,例如,尝试启动 telnet 会话。如果任一主机或两个主机都使用受密码保护的服务器证书,系统会立即提示您输入密码。如果您设置了 Stunnel 启动脚本,请牢记这一点。输入证书后,您应该就可以启动并运行了。
您现在可以通过快速发出ps auxw并查找 Stunnel 进程来检查是否成功启动——无论 Stunnel 是否干净启动,Stunnel 都不会向控制台返回任何输出。但是,它会将消息发送到您系统的 syslog 工具,包括启动消息。
一旦 stunnel 在客户端和服务器上都运行起来,nearclient 上的用户就可以通过连接到 nearclient 的 TCP 端口 23 来触发隧道,例如,使用命令telnet 127.0.0.1(23 是 telnet 的默认端口)。列表 5 显示了示例加密 telnet 会话。
列表 5. 示例 Telnet 会话
nearserver:/usr/local/etc/stunnel# telnet 127.0.0.1 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. Fedora Core release 1 (Yarrow) Kernel 2.4.22-1.2115.nptl on an i686 login: myfaraccount Password: ********** Last login: Sun Jun 13 21:39:17 from localhost.localdomain [myfaraccount@farserver myfaraccount]$
如您所见,从最终用户的角度来看,以这种方式连接到 farserver 与任何其他 telnet 会话几乎没有区别。但是,列表末尾的 bash 提示符证实我们实际上已连接到 farserver。
Stunnel 和 inetd/xinetd
我有一个秘密要坦白。本文中的示例并未显示使用 Stunnel 加密 telnet 的最有效方法,尽管我向您保证我已测试过这些示例,并且它们确实有效。
我的理由是我想快速说明隧道传输通用 TCP 服务的概念,使用一种适用于几乎任何单 TCP 端口服务的技术。但是 telnet、pop3 和其他通常由超级服务器(如 inetd 或 xinetd)启动的服务通过本文范围之外的几种不同的 Stunnel 功能得到支持。
例如,Stunnel 服务器可以不使用 connect 语句转发数据包,而是使用 stunnel.conf 中的 exec 参数将数据包传递给 Stunnel 自己调用的进程。或者,您可以配置 Stunnel 本身由 inetd 或 xinetd 动态调用。
有关将 Stunnel 与动态启动的 dæmons 或与 inetd 或 xinetd 结合使用以获取更多信息,请参阅 stunnel(8) 手册页和 Stunnel FAQ。
我希望,这足以让您开始 Stunnel 的疯狂之旅。与往常一样,我只触及了表面。我将由您来探索 Stunnel 使用客户端证书检查来验证隧道的能力、它对 TCPwrappers 样式访问控制的支持以及 stunnel.conf 中支持的无数全局和服务特定选项。让 stunnel(8) 手册页成为您的指南,并祝愿您使用单 TCP 端口的未加密 TCP 应用程序不再容易被窃听。
本文资源: /article/7646。
Mick Bauer,CISSP,是 Linux Journal 的安全编辑,也是明尼苏达州明尼阿波利斯市的 IS 安全顾问。他是 Building Secure Servers With Linux(O'Reilly & Associates,2002 年)的作者。