偏执的企鹅 - 基于 OpenVPN 的 Linux VPN,第四部分
在过去的几个月中,我一直在描述如何使用 OpenVPN(一个免费的、多平台的、基于 TLS/SSL 的 VPN 守护程序)构建虚拟专用网络 (VPN) 服务器。我的示例使用场景涉及常见的“移动战士”设置,其中远程用户通过在互联网上建立加密的 VPN 隧道连接回其“家庭网络”上的 VPN 服务器。
上个月,在第三部分中,我逐行完成了示例 OpenVPN 服务器配置文件 (server.ovpn) 的详细介绍,此处显示供您参考(清单 1)。
清单 1. 服务器的 server.ovpn 文件
port 1194 proto udp dev tun ca 2.0/keys/ca.crt cert 2.0/keys/server.crt key 2.0/keys/server.key # Keep this file secret dh 2.0/keys/dh1024.pem tls-auth 2.0/keys/ta.key 0 server 10.31.33.0 255.255.255.0 ifconfig-pool-persist ipp.txt push "redirect-gateway def1 bypass-dhcp" keepalive 10 120 cipher BF-CBC # Blowfish (default) comp-lzo max-clients 2 user nobody group nogroup persist-key persist-tun status openvpn-status.log verb 3 mute 20
然后我讨论了将 OpenVPN 作为服务器进程运行(同一个可执行文件既可以作为守护程序/监听器运行,也可以作为客户端进程运行),可以前台运行,并将所有日志消息打印到控制台
bash-$ sudo openvpn --config ./server.ovpn
或者在后台运行,并将所有日志消息写入 /var/log/daemon.log
bash-$ sudo openvpn --daemon --config ./server.ovpn
在 OpenVPN 在服务器和客户端上工作的早期阶段,您肯定希望在前台运行服务器守护程序,因为您可能无论如何都必须通过配置调整来停止并重新启动它。一旦一切正常,您可以将一个 init 脚本放入服务器的 /etc/init.d 目录中,该脚本会在启动时自动以守护程序模式启动 OpenVPN。
清单 2. 客户端的 client.ovpn 文件
client proto udp dev tun remote 1.2.3.4 1194 nobind ca ca.crt cert minion.crt key minion.key ns-cert-type server tls-auth ta.key 1 cipher BF-CBC comp-lzo user nobody group nogroup persist-key persist-tun mute-replay-warnings verb 3 mute 20
首先是client指令。像server,我们上次讨论过,client 实际上是一个辅助指令,当 openvpn 命令读取它时,会扩展为两个其他指令:pull,它指示 OpenVPN 接受它连接到的 OpenVPN 服务器推送给它的选项;以及 tls-client,它启用 TLS (SSL) 加密并告诉 OpenVPN 在任何时候启动 TLS 事务时都扮演客户端的角色。
接下来是proto udp,它告诉 OpenVPN 使用 UDP 数据包来构建其 VPN 隧道。此设置需要与您希望连接的服务器上指定的设置相同。
接下来是dev tun,它告诉 OpenVPN 通过 /dev/tun 接口封装 IP 数据包,而不是通过 /dev/tap 设备封装以太网帧。在我的示例中,我坚持使用 IP 封装,而且,此设置必须与您希望连接的服务器上的设置相同。
那么,您希望连接到哪个服务器?是在remote指令中指定的服务器,该指令有两个参数:IP 地址(或主机名)和端口。在清单 2 中,这些设置为 1.2.3.4 1194,特别是 UDP 端口 1194。(如果之前我设置了proto为tcp-client,OpenVPN 会假定您在这里指的是 TCP 端口 1194。)
我的示例服务器的 IP 地址是 1.2.3.4,这可能会让您觉得不太可能,但至少此地址是可通过互联网路由的。如果您要从互联网连接到您的 OpenVPN 服务器,则需要以可通过互联网路由的 IP 地址为目标。在我的家庭设置中,这实际上是我的 DSL 路由器的地址,我已将其配置为将 UDP 1194 连接重定向到我的 OpenVPN 服务器上的同一端口,后者的真实 IP 地址是不可通过互联网路由的 192.168.0.0 地址。
之后remote是nobind,它告诉 OpenVPN 允许本地 IP 堆栈(您的 Linux 内核的 TCP/IP 模块)动态分配一个本地端口,用于发送和接收 OpenVPN 数据包,而不是像服务器进程那样让 OpenVPN 守护程序“绑定”到(监听)特定端口。因此,此设置仅适用于 VPN 客户端系统。
接下来,有五个与辅助文件相关的指令,OpenVPN 需要读取这些文件才能构建隧道。ca指定一个文件,其中包含至少一个证书颁发机构证书,特别是将签署 OpenVPN 服务器证书的任何 CA 的证书。如果您尝试连接到服务器证书未由此处指定的 CA 密钥/证书签名的服务器,则您的 OpenVPN 客户端进程将终止连接。
cert指定一个文件,其中包含您的 OpenVPN 客户端进程要呈现给 OpenVPN 服务器的客户端证书。此证书需要由 CA 签名,该 CA 的证书位于服务器的 ca 文件中,否则服务器将拒绝来自您的连接。
在许多情况下(如果不是大多数情况),处理这些证书的最简单方法是使用相同的 CA 来创建和签署服务器和客户端证书。事实上,如果您还记得本系列的第二部分(LJ,2010 年 3 月),我在创建服务器凭据后已经创建了客户端证书和密钥。
因为这个过程既重要又简单,所以让我们花一点时间回顾一下。我跳过了设置和创建证书颁发机构本身的过程,因为这仅适用于服务器设置(您应该只在服务器上执行一次)。因此,假设您已按照本文第二部分的描述在 OpenVPN 服务器上设置了工作的 CA,请按照以下步骤使用 OpenVPN 的 pkitool 脚本创建新的客户端证书
1) su 到 root
bash-$ su
2) 将您的工作目录更改为 /etc/openvpn/2.0
bash-# cd /etc/openvpn/2.0
3) 声明存储在文件 vars 中的一些 PKI 相关的环境变量
bash-# source ./vars
4) 创建新证书
bash-# ./pkitool --pass minion
在步骤 4 中,字符串minion是您正在创建证书的主机或用户的名称(在 x.509 术语中是“通用名称”)。发出此命令后,系统将提示您两次键入证书的密码。
此命令的输出采用三个文件的形式:./keys/minion.csr、./keys/minion.crt 和 ./keys/minion.key。对于本文的目的而言,只有最后两个文件是重要的:分别是新的客户端证书和客户端密钥。
在您设置 OpenVPN 服务器时创建的与加密相关的辅助文件中,您的客户端证书和密钥是客户端独有的两个文件; ca.crt 和 ta.key 用于服务器以及连接到它的所有客户端。另请注意,虽然客户端证书 (minion.crt) 不包含任何私有数据,但客户端密钥 minion.key 和 TLS 身份验证密钥 ta.key 都应通过本地文件权限和小心处理来保密。
例如,您绝不应以明文形式通过电子邮件发送任何客户端密钥或 TA 密钥(请注意,使用 https:// URL 进行 Webmail 访问不算是消息加密)。如果您需要通过邮件将密钥发送给用户,则应使用 S/MIME 或 PGP 电子邮件加密。
如果您使用 USB 驱动器或其他物理介质分发密钥,则应亲自交付或使用可信的快递员交付,并且应指示用户在安装密钥后销毁或擦除介质,或将介质锁起来。虽然拥有受密码保护的客户端密钥应该使攻击者难以使用拦截的密钥文件,但这并不能保证!在任何情况下,您都不应为任何 VPN 场景颁发空白密码客户端证书。
说到客户端密钥的密码,您还应该注意如何将该密码传输给密钥的用户。因为在任何情况下系统管理员都知道用户的密码都不是一个好的策略,所以用户之后可能想要更改密钥的密码。用户执行此操作的最简单方法是通过 openssl 命令,如下所示
bash-$ openssl rsa -in minion.key -out minion.key -aes192
用户不需要成为 root 才能执行此操作,前提是在 minion.key 上设置了正确的文件权限(用户应该对其自己的密钥具有读/写访问权限)。输入此命令后,系统将提示用户输入密钥文件的旧密码,然后两次输入新密码。
一旦用户将文件 ca.crt、client.crt、client.key 和 ta.key 复制到其客户端系统的 /etc/openvpn/ 目录中,他们应确保已设置正确的文件权限。两个 .crt 文件应该是全局可读的,但仅所有者可写(即,-rw-r--r--)。但是,两个 .key 文件应仅所有者可读/可写(即,-rw-------).
假设您的用户在其自己的 Linux 系统上拥有 root 权限,则所有四个文件都应归 root 所有。(为非 root 用户设置 OpenVPN 以及这样做的安全挑战超出了本文的范围。)
现在您已经清楚了如何生成和管理客户端证书/密钥对,让我们继续向下浏览清单 2。ca, cert和key指令分别指定您的 CA 密钥文件、客户端证书文件和客户端密钥文件的路径。在清单 2 中,这些参数的值都只是文件名,没有指定其完整路径。这意味着这三个文件与客户端配置文件本身位于同一目录中。
因此,与服务器上不同,在服务器上,我将所有证书和密钥都放在 /etc/openvpn/2.0/keys 中,因此,指定了 CA 证书路径为 2.0/keys/ca.crt,在客户端系统上,您可以简单地使用ca.crt如果文件 ca.crt(如配置文件 client.ovpn)保存在 /etc/openvpn/ 目录中。
Thens-cert-type server指令说明您的客户端应接受哪种类型的证书。因为在本示例中,我正在处理多个客户端连接回服务器,所以在清单 2 中,它设置为serverserver。这将防止其他客户端在中间人攻击中冒充服务器;服务器的证书虽然与客户端由同一 CA 签名,但具有将其标识为服务器证书而不是另一个客户端证书的属性。
清单 2 的证书/密钥文件部分的最后一个指令是tls-auth ta.key 1,它告诉 OpenVPN 使用文件 ta.key 在您的 VPN 隧道上添加额外的身份验证层,方法是要求每个隧道会话开始时 TLS 握手阶段中的所有数据包都使用指定的 TLS 身份验证密钥签名。在 TLS 身份验证密钥的名称 (ta.key) 之后,指定一个数字,指示使用此文件的“方向”:“0”表示服务器,“1”表示客户端。
清单 2 中几乎所有其余指令都是我在本系列的第二部分和第三部分中在剖析清单 1(服务器配置文件)时已经介绍过的指令。这些指令的客户端设置甚至与服务器上指定的设置相同。
对于user nobody和group nogroup,它们指示 openvpn 命令在初始化后以非特权用户和组身份运行,请确保用户帐户 nobody 和组 nogroup 存在于您的客户端系统上。因为两者通常都存在于大多数 Linux 系统上,所以它们可能已经存在。如果不存在,您可以创建它们,也可以更改指令以指向其他现有的守护程序帐户或组名。在任何情况下,您都不应将任何一个更改为用于实际人工使用登录帐户的任何用户或组名!
这里唯一值得提及的另一个指令是mute-replay-warnings,我没有将其包含在 server.ovpn 文件中。声明此指令(没有任何参数)会告诉 OpenVPN 不要记录与其反数据包重放机制相关的事件,如果您连接到无线网络,该机制往往会触发误报。它不会关闭实际的反重放保护;它只是抑制相关的本地日志条目。
您已经创建了一个客户端配置文件并将其放入客户端系统的 /etc/openvpn 目录中。您已经复制了您的 CA 证书文件、客户端证书和密钥文件以及您的 TA 密钥文件。现在是连接到服务器的时候了。
假设您的客户端系统正在运行 Linux(特别是 Ubuntu),并假设,如清单 2 所示,您的客户端配置文件名为 client.ovpn,首次连接到服务器时,请按照以下步骤操作
1) 将您的工作目录更改为 /etc/openvpn
bash-$ cd /etc/openvpn
2) 像这样运行 OpenVPN
bash-$ sudo openvpn --config ./client.ovpn
3) 出现提示时,输入您的客户端密钥的密码。
Enter Private Key Password: Your passphrase here
请注意,在步骤 2 中,您没有使用--daemon指令启动 OpenVPN,使其在前台运行。如果一切顺利,下次启动 OpenVPN 时,您可以使用sudo openvpn --daemon --config ./client.ovpn,在这种情况下,OpenVPN 在询问您客户端密钥密码后将静默运行。在我的 Ubuntu 客户端系统上,OpenVPN 在守护程序模式下运行时会将日志记录到文件 /var/log/syslog 中。
希望到目前为止,您已经有了一个可以正常工作的 VPN 隧道连接回您的服务器。如果您没有,那么两个不同的错误导致了我在使用 OpenVPN 时的大部分问题。
首先,如果您未能将所有必要的文件复制到 /etc/openvpn 中,您的隧道将失败:客户端配置文件、CA 证书、客户端证书、客户端密钥和 TLS 身份验证密钥。这些文件还必须设置适当的权限,并且您必须以适当的权限级别(即 root 权限)运行 openvpn 命令。
其次,服务器和客户端上的防火墙规则必须禁用(保持打开状态)或重新配置以允许 OpenVPN 流量通过。告诉您如何轻松编写此类规则可能会占用整篇文章的一半或更多,特别是如果我要介绍强制某些类型的流量使用隧道的技巧。现在只需说,在您甚至费心以服务器或客户端模式运行 OpenVPN 之前,请检查您的 iptables 规则。
在撰写本文时,我测试了 Ubuntu 客户端和 Windows XP 客户端。让 Windows XP 客户端正确连接并不比在 Ubuntu 上更难。这只是将正确的文件放在正确的目录中并稍微调整我的 Windows 防火墙配置的问题。
在 Windows 客户端上,user和group指令不起作用,因为 Windows 不支持 OpenVPN 的自降级功能。除此之外,我发现 Windows 版本的 OpenVPN 与 Linux 版本的工作方式非常相似。
亲爱的读者们,这就是您配置 OpenVPN 以允许您从不受信任的远程站点连接回您的家庭网络的方式。实际上,这个过程比我花了四个月时间来描述它所暗示的要容易得多。希望我对两个配置文件的逐行剖析使您对 OpenVPN 的工作原理有了足够的了解,以便您探索其他使用场景。
我可能会再用一栏来讨论这个主题,因为虚拟专用网络是一个非常强大的工具,而这四期专栏只涵盖了其潜力的很小一部分。无论如何,我希望您觉得这个系列有用,并且您在自己的虚拟专用 Linux 事业中取得成功。下次再见,请注意安全!
Mick Bauer (darth.elmo@wiremonkeys.org) 是美国最大的银行之一的网络安全架构师。他是 O'Reilly 图书《Linux 服务器安全》第二版(原名《使用 Linux 构建安全服务器》)的作者,偶尔在信息安全会议上发表演讲,也是“网络工程波尔卡”的作曲家。