OpenVPN 秘籍第二版(二)
原文:
annas-archive.org/md5/fd72a09eaec4467049fbfe9a8c996015译者:飞龙
第三章:客户端-服务器以太网风格的网络
本章将涵盖以下主题:
-
简单配置 - 非桥接模式
-
启用客户端到客户端的流量
-
桥接 - Linux
-
桥接 - Windows
-
检查广播和非 IP 流量
-
外部 DHCP
-
使用状态文件
-
管理界面
-
将 IPv6 集成到 TAP 风格的网络中
介绍
本章中的教程将涵盖单服务器和多个远程客户端的部署模型,这些客户端能够转发以太网流量。
我们将查看几种常见的配置,包括桥接、使用外部 DHCP 服务器,以及使用OpenVPN状态文件。请注意,桥接应仅在最后的选择中使用。桥接提供的大部分功能可以通过其他方法实现。此外,使用桥接有许多缺点,尤其是在性能和安全性方面。
简单配置 - 非桥接模式
本教程将演示如何使用证书在客户端或服务器模式下设置基于 TAP 的连接。它还使用伪装技术,使 OpenVPN 客户端能够访问 OpenVPN 服务器背后的所有计算机。伪装的优点是使用它后,服务器局域网无需特殊路由。OpenVPN 服务器的伪装功能仅在 Linux 和 UNIX 变种上可用。本教程类似于上一章中的服务器端路由教程。
准备工作
使用第二章中的第一个教程设置客户端和服务器证书,客户端-服务器仅 IP 网络。对于本教程,服务器计算机和客户端计算机都运行 CentOS 6 Linux 和 OpenVPN 2.3.10。
我们使用以下网络布局:
如何操作...
-
创建服务器配置文件:
tls-server proto udp port 1194 dev tap server 192.168.99.0 255.255.255.0 tls-auth /etc/openvpn/cookbook/ta.key 0 ca /etc/openvpn/cookbook/ca.crt cert /etc/openvpn/cookbook/server.crt key /etc/openvpn/cookbook/server.key dh /etc/openvpn/cookbook/dh2048.pem persist-key persist-tun keepalive 10 60 push "route 10.198.0.0 255.255.0.0" user nobody group nobody # use "group nogroup" on some distros daemon log-append /var/log/openvpn.log将其保存为
example-3-1-server.conf。请注意,在某些 Linux 发行版中,使用nogroup而不是nobody。 -
启动服务器:
[root@server]# openvpn --config example3-1-server.conf -
设置 IP 转发和
iptables伪装规则:[root@server]# sysctl -w net.ipv4.ip_forward=1 [root@server]# iptables -t nat -I POSTROUTING -i tap+ -o eth0 \ -s 192.168.99.0/24 -j MASQUERADE -
接下来,创建客户端配置文件:
client proto udp remote openvpnserver.example.com port 1194 dev tap nobind remote-cert-tls server tls-auth /etc/openvpn/cookbook/ta.key 1 ca /etc/openvpn/cookbook/ca.crt cert /etc/openvpn/cookbook/client1.crt key /etc/openvpn/cookbook/client1.key将其保存为
example-3-1-client.conf。 -
启动客户端:
[root@client]# openvpn --config example3-1-client.conf生成的输出如下所示:
-
连接建立后,我们可以验证其是否正常工作。首先,我们 ping 一下服务器:
[client]$ ping -c 2 192.168.99.1 PING 192.168.99.1 (192.168.99.1) 56(84) bytes of data. 64 bytes from 192.168.99.1: icmp_seq=1 ttl=64 time=25.3 ms 64 bytes from 192.168.99.1: icmp_seq=2 ttl=64 time=25.2 ms第二步,我们对服务器端局域网中的一台主机进行 ping 测试:
[client]$ ping -c 2 10.198.0.1 PING 10.198.0.1 (10.198.0.1) 56(84) bytes of data. 64 bytes from 10.198.0.1: icmp_seq=1 ttl=63 time=29.2 ms 64 bytes from 10.198.0.1: icmp_seq=2 ttl=63 time=25.3 ms
它是如何工作的...
当服务器启动时,它会配置第一个可用的 TAP 接口,并分配 IP 地址192.168.99.1。之后,服务器会在 UDP 端口 1194 上监听传入的连接,这是 OpenVPN 的默认端口。
客户端通过此端口连接到服务器。在初始 TLS 握手过程中,使用客户端和服务器证书后,客户端会被分配 IP 地址192.168.99.2。客户端使用此信息配置其第一个可用的 TAP 接口;之后,VPN 建立连接。
除了 OpenVPN 配置,本食谱还使用 iptables 命令来使客户端能够访问站点 B 的局域网,而无需在站点 B 的局域网网关上设置额外的路由。以下命令指示 Linux 内核重写所有来自子网 192.168.99.0/24(即我们的 OpenVPN 子网)并离开以太网接口 eth0 的流量:
[root@server]# iptables -t nat -I POSTROUTING -i tap+ -o eth0 \
-s 192.168.99.0/24 -j MASQUERADE
每一个数据包的源地址都会被重写,以便看起来像是来自 OpenVPN 服务器本身,而不是来自 OpenVPN 客户端。iptables 模块会跟踪这些重写的数据包,当收到返回数据包时,会反向处理并将数据包转发回 OpenVPN 客户端。这是一种使路由正常工作的简单方法,但当使用多个客户端时,会有一个缺点:无法区分流量是否来自 OpenVPN 服务器本身,还是来自通过 VPN 隧道的 client1 或 clientN。
还有更多...
在设置 TAP 风格网络时,需要注意一些事项。
TUN 和 TAP 之间的区别
这个设置与前一章节的 服务器端路由 食谱的区别很小。然而,也有一些细微的差别,如果不注意,可能会导致一些意想不到的效果:
-
使用 TAP 适配器时,整个以太网帧会被封装。这会导致稍微更大的开销。
-
所有连接到 TAP 风格网络的机器都形成一个单一的广播域。这个效果将在下一个食谱中变得更加清晰。
-
如果需要桥接,则需要 TAP 风格的隧道。
使用 TCP 协议
在此示例中,我们选择了 UDP 协议。本食谱中的配置文件可以通过更改以下行轻松转换为使用 TCP 协议:
proto udp
将其更改为:
proto tcp
在客户端和服务器的配置文件中都执行此操作。
UDP 协议通常提供最佳性能,但一些路由器和防火墙在转发 UDP 流量时存在问题。在这种情况下,TCP 协议通常能正常工作。
使 IP 转发永久生效
在大多数 Linux 系统上,设置 IP 转发的正确方法如下:
-
将以下行添加到
/etc/sysctl.conf文件中:net.ipv4.ip_forward=1 -
使用以下命令重新加载
sysctl.conf文件:[root@server]# sysctl -p
另见
- 来自 第二章 的 服务器端路由 食谱,解释了一个基本的 TUN 风格的设置。
启用客户端到客户端的流量
本食谱是前一个食谱的延续。它将演示如何使用证书在客户端或服务器模式下设置基于 TAP 的连接。通过使用 client-to-client 指令,它还将使不同的 OpenVPN 客户端能够相互联系。对于基于 TAP 的网络,这会产生一些重要的副作用。
准备就绪
我们使用以下网络布局:
使用第二章中的第一个方案设置客户端和服务器证书,客户端-服务器 IP-only 网络。
对于这个方案,服务器运行 CentOS 6 Linux 和 OpenVPN 2.3.10;两个客户端都运行 Windows 7 64 位和 OpenVPN 2.3.10。对于服务器,请保留上一方案中的配置文件 example3-1-server.conf。
如何操作...
-
通过向
example3-1-server.conf文件添加一行来创建服务器配置文件:client-to-client将其保存为
example-3-2-server.conf。 -
启动服务器:
[root@server]# openvpn --config example3-2-server.conf -
设置 IP 转发和
iptables伪装规则:[root@server]# sysctl -w net.ipv4.ip_forward=1 [root@server]# iptables -t nat -I POSTROUTING -i tap+ -o eth0 \ -s 192.168.99.0/24 -j MASQUERADE -
接下来,创建第一个客户端的配置文件:
client proto udp remote openvpnserver.example.com port 1194 dev tap nobind remote-cert-tls server tls-auth "c:/program files/openvpn/config/ta.key" 1 ca "c:/program files/openvpn/config/ca.crt" cert "c:/program files/openvpn/config/client1.crt" key "c:/program files/openvpn/config/client1.key" verb 5将其保存为
example-3-2-client1.ovpn。 -
同样地,为第二个客户端创建配置文件:
client proto udp remote openvpnserver.example.com port 1194 dev tap nobind remote-cert-tls server tls-auth "c:/program files/openvpn/config/ta.key" 1 ca "c:/program files/openvpn/config/ca.crt" cert "c:/program files/openvpn/config/client2.crt" key "c:/program files/openvpn/config/client2.key" verb 5将其保存为
example-3-2-client2.ovpn。 -
启动 Windows 客户端,一个通过命令行:
[WinClient1]C:> cd \program files\openvpn\config [WinClient1]C:> ..\bin\openvpn --config example3-2- client1.ovpn使用 OpenVPN GUI 启动 Client2:
由于私钥文件
client2.key使用密码短语保护,因此会提示输入密码: -
连接建立后,GUI 窗口会消失并弹出气泡:
现在,我们可以通过以下方法验证 VPN 连接是否正常工作。首先,ping 通服务器:
[WinClient1]C:> ping 192.168.99.1 Pinging 192.168.99.1 with 32 bytes of data: Reply from 192.168.99.1: bytes=32 time=24ms TTL=64 Reply from 192.168.99.1: bytes=32 time=25ms TTL=64然后,ping 通第二个客户端:
[WinClient1]C:> ping 192.168.99.3 Pinging 192.168.99.3 with 32 bytes of data: Reply from 192.168.99.3: bytes=32 time=49ms TTL=128 Reply from 192.168.99.3: bytes=32 time=50ms TTL=128请注意较高的往返时间。
-
最后,验证我们是否仍然能够 ping 通服务器端局域网的主机:
[WinClient1]C:\> ping -c 2 10.198.0.9 Pinging 10.198.0.9 with 32 bytes of data: Reply from 10.198.0.9: bytes=32 time=25ms TTL=63 Reply from 10.198.0.9: bytes=32 time=25ms TTL=63
如何运作...
两个客户端以常规方式连接到 OpenVPN 服务器。为了让客户端彼此看到,只需要以下指令:
client-to-client
客户端之间的通信仍然会通过 OpenVPN 服务器,这解释了 ICMP 数据包的较高往返时间。ICMP(ping)回显和回复的流向如下:
-
OpenVPN 客户端加密数据包并通过安全链接将其转发到服务器。
-
服务器解密数据包,并确定该数据包需要转发给另一个 OpenVPN 客户端。因此,数据包不会转发到内核路由模块,而是再次加密并转发给第二个客户端。
-
第二个客户端接收到数据包,解密它,并通过安全链接发送回复回服务器。
-
服务器解密回复数据包,并确定该数据包需要转发给第一个客户端。因此,数据包不会转发到内核路由模块,而是再次加密并转发给原始客户端。
还有更多...
一如既往,需要注意一些警告。
广播流量可能会影响可扩展性
所有连接到 TAP 类型网络的机器都形成一个单一的广播域。当启用 client-to-client 时,这意味着所有客户端的广播流量都会转发到所有其他客户端。Wireshark 在 client2 上运行时,确实显示了大量来自 client1 的广播数据包,所有这些数据包都通过 OpenVPN 服务器。这可能导致当连接大量客户端时,出现可扩展性问题。
过滤流量
在当前版本的 OpenVPN 中,当使用 client-to-client 指令时,无法过滤 VPN 客户端之间的流量。OpenVPN 确实支持过滤插件,但该插件未维护,并且需要进行大量配置。
另一种过滤客户端之间流量的方法是使用系统的路由表,并结合 Linux 内核标志 proxy_arp_pvlan。这个标志在现代 Linux 内核中可用(2.6.34+ 或者带有回溯选项的内核)。这个标志指示 Linux 内核将 ARP 请求从原接口重新发送回来。正是这个标志使得客户端之间的流量能够在不使用 client-to-client 指令的情况下正常工作。因此,为了过滤流量,我们首先通过设置这个标志来启用 TAP 模式下的客户端间流量:
# echo 1 > /proc/sys/net/ipv4/conf/tap0/proxy_arp_pvlan
然后,我们可以使用 iptables 命令过滤客户端之间的流量。
TUN 类型网络
client-to-client 指令也可以在 TUN 类型网络中使用。它的工作方式与本示例完全相同,不同之处在于 OpenVPN 客户端并不形成一个单一的广播域。
桥接 - Linux
本示例将演示如何设置一个桥接的 OpenVPN 服务器。在这种模式下,本地网络和 VPN 网络被桥接,这意味着一个网络的所有流量都会转发到另一个网络,反之亦然。
这种设置通常用于安全地将远程客户端连接到基于 Windows 的局域网,但要正确配置它相当困难。在几乎所有情况下,使用带有本地 WINS 服务器的 TUN 类型网络即可满足需求,而无需使用桥接的 VPN。桥接的 VPN 确实有其优势,这将在接下来的几个示例中显现出来。
然而,使用桥接也有一些缺点,特别是在性能方面:桥接的 100 Mbps 以太网适配器性能大约是非桥接适配器的一半。
准备就绪
我们使用以下网络布局:
使用 第二章 中的第一个示例,设置客户端和服务器证书,客户端-服务器 IP-only 网络。对于这个示例,服务器运行 CentOS 6 Linux 和 OpenVPN 2.3.10,客户端计算机运行 Windows 7 64 位和 OpenVPN 2.3.10。对于客户端,请将客户端配置文件 example3-2-client2.ovpn 保存在手边。
如何操作...
-
创建服务器配置文件:
proto udp port 1194 dev tap0 ## the '0' is extremely important server-bridge 192.168.4.65 255.255.255.0 192.168.4.128 192.168.4.200 push "route 192.168.4.0 255.255.255.0" tls-auth /etc/openvpn/cookbook/ta.key 0 ca /etc/openvpn/cookbook/ca.crt cert /etc/openvpn/cookbook/server.crt key /etc/openvpn/cookbook/server.key dh /etc/openvpn/cookbook/dh2048.pem persist-key persist-tun keepalive 10 60 user nobody group nobody # use "group nogroup" on some distros daemon log-append /var/log/openvpn.log将其保存为
example-3-3-server.conf。 -
接下来,创建一个脚本来启动网络桥接:
#!/bin/bash br="br0" tap="tap0" eth="eth0" eth_ip="192.168.4.65" eth_netmask="255.255.255.0" eth_broadcast="192.168.4.255" openvpn --mktun --dev $tap brctl addbr $br brctl addif $br $eth brctl addif $br $tap ifconfig $tap 0.0.0.0 promisc up ifconfig $eth 0.0.0.0 promisc up ifconfig $br $eth_ip netmask $eth_netmask \ broadcast $eth_broadcast将此脚本保存为
example3-3-bridge-start文件。 -
同样,使用脚本停止以太网桥接:
#!/bin/bash br="br0" tap="tap0" ifconfig $br down brctl delbr $br openvpn --rmtun --dev $tap将此脚本保存为
example3-3-bridge-stop文件。这些脚本基于bridge-start和bridge-stop示例,后者是 OpenVPN 配发的一部分。 -
创建网络桥接并验证它是否正常工作:
[root@server]# bash example3-3-bridge-start TUN/TAP device tap0 opened Persist state set to: ON [root@server]# brctl show bridge name bridge id STP enabled interfaces br0 8000.00219bd2d422 no eth0 tap0 -
启动 OpenVPN 服务器:
[root@server]# openvpn --config example3-3-server.conf -
启动客户端:
-
检查分配的 VPN 地址:
[WinClient]C:> ipconfig /all [...] Ethernet adapter tun0: Connection-specific DNS Suffix . : Description . . . . . . . . . . . : TAP-Win32 Adapter V9 Physical Address. . . . . . . . . : 00-FF-17-82-55-DB Dhcp Enabled. . . . . . . . . . . : Yes Autoconfiguration Enabled . . . . : Yes IP Address. . . . . . . . . . . . : 192.168.4.128 Subnet Mask . . . . . . . . . . . : 255.255.255.0 Default Gateway . . . . . . . . . : DHCP Server . . . . . . . . . . . : 192.168.4.0 -
现在,验证我们是否能 ping 通远程服务器局域网中的一台机器:
[WinClient]C:> ping 192.168.4.164 Pinging 192.168.4.164 with 32 bytes of data: Reply from 192.168.4.164: bytes=32 time=3ms TTL=64 Reply from 192.168.4.164: bytes=32 time=1ms TTL=64 Reply from 192.168.4.164: bytes=32 time=1ms TTL=64 Reply from 192.168.4.164: bytes=32 time<1ms TTL=64 -
停止 OpenVPN 服务器后,请记得拆除网络桥接:
[root@server]# bash example3-3-bridge-stop TUN/TAP device tap0 opened Persist state set to: OFF
它是如何工作的...
bridge-start脚本为两个网络适配器建立了连接:一侧是局域网适配器eth0,另一侧是 VPN 适配器tap0。网络桥接的主要特性是所有流量都会从一侧复制到另一侧,反之亦然。这使我们能够设置一个几乎让客户端真正成为服务器端局域网一部分的 VPN。
桥接网络的缺点是增加了开销,并且对 OpenVPN 服务器本身造成性能损失:如果来自任一端的客户端有大量广播流量,桥接可能会变得过载。
还有更多...
固定地址和默认网关
在这个配方中,OpenVPN 服务器被分配了一个固定的服务器局域网地址,这通常是为桥接接口设置的做法。给网络桥接分配动态地址的问题是,无法清楚地确定动态地址应该从哪个网络中选择。这也使我们能够在服务器配置文件中指定一个固定的服务器桥接地址。
使用桥接时,检查桥接启动后默认路由是否可用也很重要。在大多数设置中,eth0会被分配一个动态地址,包括默认网关。当执行bridge-start脚本时,br0会被分配一个固定地址,但作为副作用,默认网关通常会丢失。
名称解析
设置桥接网络时的一个难点是与名称解析有关。OpenVPN 只支持以太网(Layer2)或基于 IP 的路由。在桥接环境中,设置一个适当的名称解析系统(例如,Windows 网络中的域控制器和/或 WINS 服务器)可能也会非常棘手。
另见
- 本章的下一个配方,解释了如何在 Windows 服务器上设置桥接。
桥接 - Windows
这个配方将演示如何在 Windows 上设置桥接 OpenVPN 服务器。Windows 上的桥接与 Linux 或 UNIX 略有不同,但概念是一样的。
这个配方与前一个配方非常相似,唯一不同的是设置桥接的方法。
准备工作
使用第二章中的第一个配方设置客户端和服务器证书,客户端-服务器 IP-only 网络。
对于这个配方,服务器计算机运行的是 Windows 7 64 位系统和 OpenVPN 2.3.10 版本。客户端计算机运行的是 Fedora 20 Linux 和 OpenVPN 2.3.10 版本。对于 Linux 客户端,保持客户端配置文件example3-1-client.conf在手。
我们使用以下网络布局:
如何操作...
-
创建服务器配置文件:
proto udp port 1194 dev tap dev-node tapbridge server-bridge 192.168.3.15 255.255.255.0 192.168.3.128 192.168.3.250 dh "c:/program files/openvpn/config/dh2048.pem" tls-auth "c:/program files/openvpn/config/ta.key" 0 ca "c:/program files/openvpn/config/ca.crt" cert "c:/program files/openvpn/config/server.crt" key "c:/program files/openvpn/config/server.key" push "route 192.168.3.0 255.255.255.0" persist-key persist-tun keepalive 10 60将其保存为
example-3-4-server.conf。 -
接下来,创建网络桥接:
-
进入网络和共享中心,然后选择更改适配器设置。
-
通过右键点击
TAP-Win适配器并选择重命名,将其重命名为tapbridge。在所使用的测试计算机上,连接到局域网的以太网适配器被重命名为eth0。 -
通过按住Ctrl键并点击每个适配器来选择需要桥接的两个适配器,然后右键点击并选择桥接连接:
这将会在控制面板中创建一个新的桥接适配器图标,通常命名为网络桥接(...)。
-
-
网络桥接现在已准备好进行配置:
-
在命令窗口中,验证桥接是否正确配置:
[winserver]C:> netsh interface ip show address "Network Bridge" Configuration for interface "Network Bridge" DHCP enabled: No IP Address: 192.168.3.15 SubnetMask: 255.255.255.0 Default Gateway: 192.168.3.1 GatewayMetric: 5 InterfaceMetric: 0 -
启动 OpenVPN 服务器:
[winserver]C:> cd \program files\openvpn\config [winserver]C:> ..\bin\openvpn --config example3-4-server.ovpn -
Windows 防火墙会弹出一个安全警告。允许 OpenVPN 访问 VPN:
-
启动客户端:
[root@client]# openvpn --config example3-1-client.conf -
现在,检查分配的 VPN 地址,并验证我们是否能 ping 通远程服务器局域网中的一台计算机:
[client]$ /sbin/ifconfig tap1 tap1 Link encap:Ethernet HWaddr A2:F4:E2:41:05:BF inet addr:192.168.3.128 Bcast:192.168.3.255 Mask:255.255.255.0 [...] [client]$ ping -c 2 192.168.3.1 PING 192.168.3.1 (192.168.3.1) 56(84) bytes of data. 64 bytes from 192.168.3.1: icmp_seq=1 ttl=128 time=24.0 ms 64 bytes from 192.168.3.1: icmp_seq=2 ttl=128 time=26.0 ms
它是如何工作的...
除了创建和配置桥接的方式外,这个配方与之前的配方非常相似。唯一需要记住的是如何在服务器配置文件中选择适配器:
dev tap
dev-node tapbridge
在 Linux 和其他 UNIX 变种上,可以通过一行命令来实现:
dev tap0
但 Windows 上 TAP 适配器的命名规则不同。为了解决这个问题,需要添加dev-node指令。
参见:
- 之前的配方中,已经解释了如何在 Linux 上进行桥接。
检查广播和非 IP 流量
桥接设置的主要目的是为所有连接的客户端创建一个单一的广播域,无论是通过 VPN 还是通过常规网络连接。
另一个原因是能够路由或转发基于非 IP 的流量,例如较旧的 Novell IPX 和 Appletalk 协议。
这个配方重点介绍了如何使用tcpdump和wireshark等工具来检测广播域是否正常工作,以及非 IP 流量是否以正确的方式流动。
准备就绪
对于这个配方,我们使用的是本章桥接 - Linux配方中的设置。我们使用以下网络布局:
对于这个配方,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.9。对于服务器,保持服务器配置文件example3-3-server.conf来自桥接 - Linux配方准备好。第一台客户端计算机运行的是 Windows 7 64 位和 OpenVPN 2.3.10,并且与 OpenVPN 服务器处于同一局域网段。第二台客户端运行的是 Windows XP 和 OpenVPN 2.1.1。对于这个客户端,保持客户端配置文件example3-2-client2.ovpn来自启用客户端到客户端流量配方。
确保 Windows 机器上已安装 AppleTalk 和 IPX 协议。将这些协议绑定到局域网适配器(这是默认设置)。
如何操作...
-
创建网络桥接并验证它是否工作:
[root@server]# bash example3-3-bridge-start TUN/TAP device tap0 opened Persist state set to: ON [root@server]# brctl show bridge name bridge id STP enabled interfaces br0 8000.00219bd2d422 no eth0 tap0 -
启动 OpenVPN 服务器:
[root@server]# openvpn --config example3-3-server.conf -
启动 OpenVPN 客户端:
[WinClient1]C:> cd \program files\openvpn\config [WinClient1]C:> ..\bin\openvpn --config example3-2- client2.ovpn使用 OpenVPN GUI 启动客户端 2:
在这个配方中,Windows 7 客户端被分配了
192.168.4.64。Windows XP 客户端被分配了192.168.4.128。 -
客户端成功连接后,我们首先检查 ARP 消息。在服务器上运行
tcpdump命令并监听桥接接口br0上的流量:在这个输出中,
192.168.4.254是服务器端网关的地址。因此,网关正在请求 ARP 信息,而 ARP 回复来自 OpenVPN 服务器和 OpenVPN 客户端本身。只有在 ARP 请求通过桥接转发到 OpenVPN 客户端时,才会发生这种情况。 -
接下来,在 Windows 7 客户端上,检查来自 Windows XP 客户端的广播流量。为此,我们使用 Wireshark。Wireshark 适用于 Linux 和 Windows。将其配置为捕获来自以太网适配器的所有流量。以下是一个示例:
在这个输出中,我们看到很多 Netbios 广播流量,当 OpenVPN 客户端首次连接到网络时。
-
作为最终示例,我们查看 IPX 流量:
这表明非 IP 流量也会通过桥接转发。
它是如何工作的...
所有通过桥接转发的流量都会被 Wireshark 等程序拦截。通过过滤特定类型的流量,很容易证明,在桥接设置中,来自 OpenVPN 客户端的流量确实是通过服务器端的局域网传输的。这在排查几乎完成的设置时非常重要。
一个外部 DHCP 服务器
在这个配方中,我们将配置一个桥接的 OpenVPN 服务器,使其使用外部 DHCP 服务器为 OpenVPN 客户端分配地址,从而进一步增强远程客户端与服务器端局域网中现有客户端的集成。
准备就绪
我们使用以下网络布局:
使用第二章中的第一个配方,客户端-服务器仅 IP 网络,设置客户端和服务器证书。对于这个配方,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.10。客户端运行的是 Windows 7 64 位和 OpenVPN 2.3.10。对于这个客户端,保留客户端配置文件example3-2-client2.ovpn,并参考启用客户端到客户端流量配方。
如何操作...
-
创建服务器配置文件:
proto udp port 1194 dev tap0 server-bridge push "route 0.0.0.0 255.255.255.255 net_gateway" tls-auth /etc/openvpn/cookbook/ta.key 0 ca /etc/openvpn/cookbook/ca.crt cert /etc/openvpn/cookbook/server.crt key /etc/openvpn/cookbook/server.key dh /etc/openvpn/cookbook/dh2048.pem persist-key persist-tun keepalive 10 60 user nobody group nobody # use "group nogroup" on some distros daemon log-append /var/log/openvpn.log -
保存为
example3-6-server.conf。 -
启动服务器:
[root@server]# openvpn --config example3-6-server.conf -
启动 Windows 客户端:
-
VPN 连接建立后,验证 IP 地址和路由表:
[WinClient]C:> ipconfig /all [...] Ethernet adapter tapwin32-0 Connection-specific DNS Suffix . : lan Description . . . . . . . . . . . : TAP-Win32 Adapter V9 Physical Address. . . . . . . . . : 00-FF-17-82-55-DB Dhcp Enabled. . . . . . . . . . . : Yes Autoconfiguration Enabled . . . . : Yes IP Address. . . . . . . . . . . . : 192.168.4.66 Subnet Mask . . . . . . . . . . . : 255.255.255.0 Default Gateway . . . . . . . . . : 192.168.4.254 DHCP Server . . . . . . . . . . . : 192.168.4.254 DNS Servers . . . . . . . . . . . : 192.168.4.254 [...] [WinClient]C:> netstat -rn [...] 0.0.0.0 0.0.0.0 192.168.3.1 192.168.3.22 10 0.0.0.0 255.255.255.255 192.168.3.1 192.168.3.22 1 0.0.0.0 0.0.0.0 192.168.4.254 192.168.4.66 1 Default Gateway: 192.168.3.1 [...] -
最后,检查我们是否能访问服务器端 LAN 中的其他主机:
[WinClient]C:> ping 192.168.4.64 Pinging 192.168.4.64 with 32 bytes of data: Reply from 192.168.4.64: bytes=32 time=3ms TTL=64 Reply from 192.168.4.64: bytes=32 time=1ms TTL=64 Reply from 192.168.4.64: bytes=32 time=1ms TTL=64 Reply from 192.168.4.64: bytes=32 time<1ms TTL=64
它是如何工作的...
这是服务器指令:
server-bridge
如果没有任何参数,该指令会指示 OpenVPN 不为客户端分配一个 IP 地址池。所以,所有来自客户端的 DHCP 请求都会通过桥接转发。然后,服务器端局域网中的 DHCP 服务器会返回一个 IP 地址。
这里的难点是,DHCP 服务器几乎总是会返回默认网关,而默认网关通常是 LAN 网关。如果远程客户端将其默认网关设置为 LAN 的网关,就会发生一些奇怪的事情,因为在大多数情况下,直接到 OpenVPN 服务器的路由会丢失。
以下指令指示 OpenVPN 客户端通过 net_gateway 添加一个显式默认路由,该路由始终是客户端侧的 LAN 网关:
push "route 0.0.0.0 255.255.255.255 net_gateway"
对于 Windows 客户端,这个技巧是有效的,默认网关保持不变。
对于 Linux 客户端,调整 dhclient 和 network-scripts 设置更加容易。但这取决于具体的发行版。
在保持默认网关不变的情况下,OpenVPN 客户端将从服务器端的 DHCP 服务器正确地分配到一个地址。
还有更多内容...
使用外部 DHCP 设置时,请注意以下事项。
DHCP 服务器配置
正确的解决方案是配置 DHCP 服务器,使 VPN 客户端的 DHCP 请求不会分配默认网关。这将增加服务器端 DHCP 服务器的管理负担。
在这种情况下,使用以下示例,通过客户端配置文件明确设置唯一的 MAC 地址是有意义的:
lladdr CA:C6:F8:FB:EB:3B
在 Linux 上,当 TAP 接口启动时,MAC 地址会随机计算,因此每次停止并重新启动 OpenVPN 客户端时,都会分配一个新的 IP 地址。也可以通过在启动 OpenVPN 之前使用系统配置脚本启动 TAP 设备,从而创建一个永久固定的静态 MAC 地址。
DHCP 转发
也可以在不使用桥接的情况下使用外部 DHCP 服务器。如果在启动 OpenVPN 之前配置了 TAP 适配器,并使用此配方中的服务器配置文件,则可以使用 Linux dhrelay 命令来使用外部 DHCP 服务器:
[root@server]# dhrelay -i tap0 -i eth0
确保列出 TAP 适配器和外部 DHCP 服务器连接的以太网适配器。结合proxy-arp脚本(参见第二章,客户端-服务器仅 IP 网络),可以在大多数情况下避免使用桥接。
调整/etc/sysconfig/network-scripts
在基于 RedHat、Fedora 和 OpenSuSE 的系统中,TAP 适配器通过脚本/etc/sysconfig/network-scripts/ifup-tap0和以下命令启动:
[root@client]# /sbin/ifup tap0
通过向/etc/sysconfig/network-scripts/ifup-tap0文件中添加这一行,dhclient脚本会忽略从 DHCP 服务器分配的网关:
GATEWAYDEV=eth0
一个类似的黑客可以为基于 Debian/Ubuntu 的系统开发。
使用状态文件
OpenVPN 提供了几种监控连接到服务器的客户端的方法。最常用的方法是使用状态文件。本配方将展示如何使用和读取 OpenVPN 状态文件。我们还将重点介绍在 TAP 样式设置中的一些状态文件的细节。
准备工作
使用第二章的第一个配方设置客户端和服务器证书,客户端-服务器仅 IP 网络。在这个配方中,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.10。第一个客户端运行的是 Fedora 20 Linux 和 OpenVPN 2.3.10。第二个客户端运行的是 Windows 7 64 位和 OpenVPN 2.3.10。对于 Linux 客户端,请随时准备好客户端配置文件example3-1-client.conf。对于 Windows 客户端,请随时准备好客户端配置文件example3-2-client2.ovpn。
如何操作...
-
通过在
example3-1-server.conf文件中添加一行,创建服务器配置文件:status /var/log/openvpn.status将其保存为
example3-7-server.conf。 -
启动服务器:
[root@server]# openvpn --config example3-7-server.conf -
首先,使用早前配方中的配置文件启动 Linux 客户端,并 ping 一个远程网络中的主机:
[root@client1]# openvpn --config example3-1-client.conf [root@client1]# ping 10.198.0.1 -
在 VPN 建立后,列出
openvpn.status文件的内容(以 root 用户身份):[root@server]# cat /var/log/openvpn.status OpenVPN CLIENT LIST Updated,Wed Mar 2 17:34:39 2016 Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since client1,192.168.4.65:50183,10024,10159,Wed Mar 2 17:26:48 2016 ROUTING TABLE Virtual Address,Common Name,Real Address,Last Ref 5e:52:73:5c:6a:ce,client1,192.168.4.65:50183,Wed Mar 2 17:27:06 2016 GLOBAL STATS Max bcast/mcast queue length,1 END -
启动 Windows 客户端:
-
Ping 远程网络中的主机:
[WinClient2]C:> ping 10.198.0.1 -
再次列出服务器上状态文件的内容:
[root@server]# cat /var/log/openvpn.status OpenVPN CLIENT LIST Updated,Wed Mar 2 17:40:22 2016 Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since client1,192.168.4.65:50183,10024,10159,Wed Mar 2 17:27:08 2016 client2,192.168.4.64:50186,18055,9726,Wed Mar 2 17:26:48 2016 ROUTING TABLE Virtual Address,Common Name,Real Address,Last Ref 5e:52:73:5c:6a:ce,client1,192.168.4.65:50183,Wed Mar 2 17:27:06 2016 00:ff:17:82:55:db,client2,192.168.4.64:50186,Wed Mar 2 17:27:16 2016 GLOBAL STATS Max bcast/mcast queue length,1 END
它是如何工作的...
每次客户端连接到 OpenVPN 服务器时,状态文件会更新连接信息。OPENVPN CLIENT LIST和ROUTING TABLE表格是最为重要的表格,它们显示了以下内容:
-
哪些客户端已连接
-
客户端连接的 IP 地址
-
每个客户端接收和传输的字节数
-
客户端连接的时间
路由表还显示了每个客户端路由到的网络。只有当客户端开始发送需要路由的流量时,这个路由表才会被填充。配方中的ping命令被用来触发路由表项。
还有更多...
当将此示例与 TUN 风格的设置进行比较时,存在许多相似之处,但也有一些区别:
与 TUN 风格网络的区别
使用 TAP 风格网络与 TUN 风格网络相比,状态文件的主要区别(请参见来自 第二章 的 使用状态文件 配方,客户端-服务器仅 IP 网络)在于 路由 表。上一章的配方展示了这一点:
10.200.0.2,client1,192.168.4.65:56764,<Date>
在本教程中,我们看到以下内容:
5e:52:73:5c:6a:ce,client1,192.168.4.65:50183,<Date>
地址 5e:52:73:5c:6a:ce 是 client1 机器上 tap 适配器的随机选择的 MAC 地址。
断开客户端连接
注意,当客户端断开连接时,状态文件不会立即更新。OpenVPN 首先尝试根据服务器配置文件中的 keepalive 参数重新连接客户端。本教程中的服务器配置文件使用了以下设置:
keepalive 10 60
这告诉服务器它将在每第 10 秒时 ping 客户端。如果 OpenVPN 服务器在 2 * 60 秒后没有收到响应,它将重启连接。服务器还会告诉客户端每 10 秒 ping 服务器一次,如果没有响应则在 60 秒后重启连接。
如果客户端通过 explicit-exit-notify 指令显式关闭连接,或者在使用基于 TCP 的设置时,服务器将不会等待客户端的 ping 响应。
另见
- 来自 第二章 的 使用状态文件 配方,解释了如何配置并使用状态文件来处理仅 IP 风格的网络。
管理接口
本教程展示了如何通过服务器上的管理接口来管理 OpenVPN。
准备工作
使用 第二章 中的第一个配方来设置客户端和服务器证书,客户端-服务器仅 IP 网络。
对于本教程,服务器计算机运行 CentOS 6 Linux 和 OpenVPN 2.3.10。第一个客户端运行 Fedora 20 Linux 和 OpenVPN 2.3.10。第二个客户端运行 Windows 7 64 位和 OpenVPN 2.3.10。
对于服务器,随时准备使用本章第一个教程中的 example3-1-server.conf 配置文件。对于 Linux 客户端,随时准备使用本章第一个教程中的 example3-1-client.conf 配置文件。对于 Windows 客户端,随时准备使用 启用客户端间流量 配方中的 example3-2-client2.ovpn 配置文件。
我们使用以下网络布局:
如何操作...
-
通过向
example3-1-server.conf文件添加一行来创建服务器配置文件:management tunnel 23000 stdin -
将其保存为
example3-8-server.conf。 -
启动服务器:
[root@server]# openvpn --config example3-8-server.confOpenVPN 服务器现在首先会要求输入管理接口的密码。
-
使用之前菜谱中的配置文件启动客户端:
[root@client1]# openvpn --config example3-1-client.conf -
同样启动 Windows 客户端:
-
VPN 建立后,我们可以使用
telnet程序从服务器连接到 OpenVPN 客户端的管理接口:[server]$ telnet 127.0.0.1 23000 Trying 127.0.0.1... Connected to localhost.localdomain (127.0.0.1). Escape character is '^]'. ENTER PASSWORD:cookbook SUCCESS: password is correct >INFO:OpenVPN Management Interface Version 1 -- type 'help' for more info status OpenVPN CLIENT LIST Updated,Wed Mar 2 17:57:07 2016 Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since client1,192.168.4.64:50209,7851,8095,Wed Mar 2 17:56:08 2016 client2,192.168.4.5:50212,11696,7447,Wed Mar 2 17:56:45 2016 ROUTING TABLE Virtual Address,Common Name,Real Address,Last Ref 00:ff:17:82:55:db,client2,192.168.4.5:50212,Wed Mar 2 17:56:49 2016 1e:b8:95:e5:60:21,client1,192.168.4.64:50209,Wed Mar 2 17:56:53 2016 GLOBAL STATS Max bcast/mcast queue length,1 END请注意,它与之前菜谱中的状态文件完全相同。
-
也可以断开客户端连接:
kill client2 SUCCESS: common name 'client2' found, 1 client(s) killed status OpenVPN CLIENT LIST Updated,Wed Mar 2 17:58:51 2016 Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since client1,192.168.4.64:50209,8381,8625,Wed Mar 2 17:56:08 2016 ROUTING TABLE Virtual Address,Common Name,Real Address,Last Ref 1e:b8:95:e5:60:21,client1,192.168.4.64:50209,Wed Mar 2 17:56:53 2016 GLOBAL STATS Max bcast/mcast queue length,1 END -
使用Ctrl + ] 或退出命令来退出 telnet 程序。
它是如何工作的…
当 OpenVPN 服务器启动时,使用以下指令设置一个特殊的管理接口:
management 127.0.0.1 23000 stdin
接口通过以下参数进行设置:
-
将 IP
127.0.0.1绑定到管理接口,仅允许 localhost 连接。 -
23000端口,管理接口将在此端口监听。 -
最后一个参数是密码文件或特殊关键字
stdin,表示在 OpenVPN 启动时会指定管理接口的密码。请注意,这个密码与私钥密码或 OpenVPN 使用的其他用户管理密码完全无关。
在管理接口启动后,服务器操作员可以使用telnet连接到该接口,并查询服务器。通过输入以下命令,操作员可以断开客户端:
kill <clientcommonname>
请注意,如果 OpenVPN 客户端配置为自动重连,它将在几分钟后自动重连。
当将管理接口的status命令的输出与使用状态文件这一菜谱中展示的状态文件输出进行比较时,第二章,客户端-服务器仅 IP 网络,主要区别在于此处列出了客户端的 MAC 地址,而不是 VPN 的 IP 地址。OpenVPN 甚至不需要知道客户端的 IP 地址,因为这些地址可以由外部的 DHCP 服务器分配。
还有更多…
管理接口也可以在 OpenVPN 客户端上运行。请参见第二章,客户端-服务器仅 IP 网络中的管理接口菜谱。
预计管理接口将在未来版本的 OpenVPN 中变得更加重要,无论是客户端还是服务器端,都将成为与 OpenVPN 软件进行程序化交互的首选方法。
另见
-
管理接口菜谱出自第二章,客户端-服务器仅 IP 网络,其中解释了客户端侧的管理接口。
-
使用状态文件这一菜谱出自第二章,客户端-服务器仅 IP 网络,其中解释了 TUN 风格网络的状态文件详情。
将 IPv6 集成到 TAP 风格网络中
本章的最后一个食谱将展示如何将 IPv6 设置集成到 TAP 风格的网络中。TAP 风格的网络对 IPv6 流量的支持比 TUN 风格的网络更早,因为 TAP 风格网络提供类似以太网的层,这一层能够传输几乎任何类型的网络协议,包括 IPv6。在 OpenVPN 2.3 中,增加了更好的 IPv6 支持,使得 OpenVPN 服务器能够提供带有 IPv6 地址的 DHCP 池。在此食谱中,我们将展示如何实现这一点。
准备中
使用第二章中的第一个食谱设置客户端和服务器证书,客户端-服务器仅 IP 网络。在此食谱中,服务器计算机和客户端计算机都运行 CentOS 6 Linux 和 OpenVPN 2.3.10。对于服务器,请随时使用本章第一个食谱中的配置文件example3-1-server.conf。对于客户端,请随时使用本章第一个食谱中的客户端配置文件example3-1-client.conf。
我们使用以下网络布局:
如何实现...
-
通过添加一行来修改服务器配置文件
example3-1-server.conf:server-ipv6 2001:db8:99::0/112 -
将其保存为
example3-9-server.conf。 -
启动服务器:
[root@server]# openvpn --config example3-9-server.conf -
启动客户端:
[root@client1]# openvpn --config example3-1-client.conf \ --suppress-timestamps OpenVPN 2.3.10 x86_64-redhat-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [MH] [IPv6] built on Jan 4 2016 library versions: OpenSSL 1.0.1e-fips 11 Feb 2013, LZO 2.03 Control Channel Authentication: using '/etc/openvpn/cookbook/ta.key' as a OpenVPN static key file UDPv4 link local: [undef] UDPv4 link remote: [AF_INET]openvpnserver:1194 [openvpnserver] Peer Connection Initiated with [AF_INET]openvpnserver:1194 TUN/TAP device tap0 opened do_ifconfig, tt->ipv6=1, tt->did_ifconfig_ipv6_setup=1 /sbin/ip link set dev tap0 up mtu 1500 /sbin/ip addr add dev tap0 192.168.99.2/24 broadcast 192.168.99.255 /sbin/ip -6 addr add 2001:db8:99::1000/112 dev tap0 Initialization Sequence Completed请注意,我们通过命令行指令
--suppress-timestamps抑制了日志文件中的时间戳。 -
在 VPN 建立后,使用
ping6命令验证我们是否能够连接到服务器:[client]$ ping6 -c 4 2001:db8:99::1 ping6 -c 4 2001:db8:99::1 PING 2001:db8:99::1(2001:db8:99::1) 56 data bytes 64 bytes from 2001:db8:99::1: icmp_seq=1 ttl=64 time=0.620 ms 64 bytes from 2001:db8:99::1: icmp_seq=2 ttl=64 time=0.630 ms 64 bytes from 2001:db8:99::1: icmp_seq=3 ttl=64 time=0.631 ms 64 bytes from 2001:db8:99::1: icmp_seq=4 ttl=64 time=0.627 ms --- 2001:db8:99::1 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3000ms rtt min/avg/max/mdev = 0.620/0.627/0.631/0.004 ms
工作原理...
TAP 风格网络的 IPv6 支持几乎与 TUN 风格网络的 IPv6 支持相同。通过向服务器配置文件添加一行,我们可以为连接的 VPN 客户端提供 IPv6 地址:
server-ipv6 2001:db8:99::0/112
适用于 TUN 基于设置的相同指令(以-ip6结尾),也适用于 TAP 风格网络。
还有更多...
IPv6 流量的防火墙规则与 IPv4 流量的防火墙规则略有不同。此外,对于 TAP 风格的网络,通常需要允许 tap+ 适配器范围内的所有传入和传出流量。这在调试无法工作的设置时尤其有用:
# iptables -I INPUT -i tap+ -j ACCEPT
# iptables -I OUTPUT -o tap+ -j ACCEPT
# ip6tables -I INPUT -i tap+ -j ACCEPT
# ip6tables -I OUTPUT -o tap+ -j ACCEPT
# iptables -I FORWARD -i tap+ -j ACCEPT
# iptables -I FORWARD -o tap+ -j ACCEPT
# ip6tables -I FORWARD -i tap+ -j ACCEPT
# ip6tables -I FORWARD -o tap+ -j ACCEPT
请注意,这类规则仅应用于调试目的。
另请参见
- 来自第二章的添加 IPv6 支持食谱,客户端-服务器仅 IP 网络,其中为非常相似的 TUN 风格设置添加了 IPv6 支持
第四章:PKI、证书与 OpenSSL
本章内容包括:
-
证书生成
-
OpenSSL 技巧:x509、pkcs12、验证输出
-
撤销证书
-
使用 CRL(证书撤销列表)
-
检查过期/吊销的证书
-
中介 CA
-
多个 CA:堆叠,使用
capath指令 -
确定使用的加密库
-
OpenSSL 和 PolarSSL 的加密功能
-
推送密码
-
椭圆曲线支持
介绍
本章内容是对公钥基础设施(PKI)、证书和openssl命令的简单介绍。本章的主要目的是展示如何生成、管理、查看 OpenVPN 中使用的证书,以及 OpenSSL 与 OpenVPN 之间的交互。
证书生成
本示例将演示如何使用普通的openssl命令创建并签署证书请求。这与使用easy-rsa脚本略有不同,但非常有指导意义。
准备工作
使用第二章中的第一个配方设置easy-rsa证书环境,方法是加载vars文件。这个示例在运行 Fedora 22 Linux 的计算机上执行,但也可以轻松在 Windows 或 MacOS 上运行。请注意,easy-rsa包可以独立于 OpenVPN 下载。
如何实现...
在我们使用普通的openssl命令生成和签署请求之前,需要设置一些环境变量。默认情况下,这些变量并未在vars文件中设置。
-
添加缺失的环境变量:
$ cd /etc/openvpn/cookbook $ . ./vars $ export KEY_CN= $ export KEY_OU= $ export KEY_NAME= $ export OPENSSL_CONF=/etc/openvpn/cookbook/openssl- 1.0.0.cnf请注意,
openssl-1.0.0.cnf文件是 easy-rsa 分发包的一部分,应该已经存在于/etc/openvpn/cookbook目录中。 -
接下来,我们在不使用密码的情况下生成证书请求。通过将选项
-nodes添加到openssl req命令来实现:$ openssl req -nodes -newkey rsa:2048 -new -out client.req \ -subj "/C=NL/O=Cookbook/CN=MyClient" Generating a 2048 bit RSA private key .......................................++++++ ............++++++ writing new private key to 'privkey.pem' ----- -
最后,我们使用证书颁发机构的私钥对证书请求进行签名:
$ openssl ca -in client.req -out client.crt Using configuration from /etc/openvpn/cookbook/openssl.cnf Enter pass phrase for /etc/openvpn/cookbook/keys/ca.key: [enter CA key password] Check that the request matches the signature Signature ok The Subject's Distinguished Name is as follows countryName :PRINTABLE:'NL' organizationName :PRINTABLE:'Cookbook' commonName :PRINTABLE:'MyClient' Certificate is to be certified until Apr 20 15:08:25 2026 GMT (3650 days) Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated
它是如何工作的...
第一步始终是生成一个私钥。在这个示例中,我们生成一个没有密码的私钥,这样并不安全。证书请求使用私钥签名,以证明证书请求和私钥属于同一对。openssl req命令一次性生成私钥和证书请求。
第二步是使用证书颁发机构(CA)的私钥签署证书请求。这样就会生成一个 X.509 证书文件,可用于 OpenVPN。
一个(公有)X.509 证书的副本也存储在/etc/openvpn/cookbook/keys目录中。如果证书之后需要被撤销,这个副本非常重要,因此不要将其从该目录中删除。
还有更多...
也可以生成一个由密码保护的私钥(在 OpenSSL 中称为“密码短语”)。要生成这样的私钥,只需去掉-nodes命令行参数:
$ openssl req -newkey rsa:1024 -new -out client.req \
-subj "/C=NL/O=Cookbook/CN=MyClient"
OpenSSL 命令现在将要求输入密码短语:
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
另见
- 来自第二章的设置公钥和私钥配方,客户端-服务器 IP 仅网络,该配方解释了如何使用
easy-rsa脚本进行 PKI 的初步设置
OpenSSL 技巧 - x509,pkcs12,验证输出
OpenSSL 命令一开始可能看起来令人生畏,但 OpenSSL 工具箱中有很多有用的命令可以用来查看和管理 X.509 证书及私钥。这个配方将展示如何使用其中一些命令。
准备工作
使用来自第二章的第一个配方,通过源vars文件设置easy-rsa证书环境。此配方是在运行 Fedora 22 Linux 的计算机上执行的,但也可以在 Windows 或 MacOS 上轻松运行。
如何做...
对于这个配方,我们需要执行以下步骤:
-
要查看给定证书的主题和过期日期,请输入:
$ cd /etc/openvpn/cookbook/keys $ openssl x509 -subject -enddate -noout -in client1.crt subject= /C=US/O=Cookbook 2.4/CN=client1 notAfter=Oct 13 17:54:30 2018 GMT -
导出证书和私钥为
PKCS12格式:$ openssl pkcs12 -export -in client1.crt \ -inkey client1.key -out client1.p12 Enter Export Password:[Choose a strong password] Verifying - Enter Export Password:[Type the password again] $ chmod 600 client1.p12请注意,
chmod 600确保 PKCS12 文件仅对用户可读。 -
验证给定证书的用途:
$ openssl verify -purpose sslclient -CAfile ca.crt client1.crt client1.crt: OK -
如果选择了错误的用途(
sslclient与sslserver),请注意错误:$ openssl verify -purpose sslclient -CAfile ca.crt server.crt server.crt: C = US, O = Cookbook 2.4, CN = openvpnserver error 26 at 0 depth lookup:unsupported certificate purpose OK -
更改证书的密码(密码短语):
$ openssl rsa -in client2.key -aes256 -out newclient.key Enter pass phrase for client2.key:[old password] writing RSA key Enter PEM pass phrase:[new password] Verifying - Enter PEM pass phrase:[new password]
它是如何工作的...
OpenSSL 工具包包含广泛的命令,用于生成、操作和查看 X.509 证书及其相应的私钥。本章中的命令只是可用命令的一个小子集。在 Linux 和 UNIX 系统上,您可以使用openssl -h以及x509、pkcs12和req的手册页面来获取更多详细信息。手册页面也可以在线访问:www.openssl.org/docs/apps/openssl.html。
点击列表中所有命令下方的 OpenSSL 命令以直接指引。
撤销证书
在管理 PKI 时,一个常见的任务是撤销不再需要的或已被泄露的证书。这个配方演示了如何使用easy-rsa脚本撤销证书,以及如何配置 OpenVPN 以使用证书撤销列表(CRL)。
准备工作
使用来自第二章的第一个配方设置客户端和服务器证书。此配方是在运行 CentOS 6 Linux 的计算机上执行的,但也可以在 Windows 或 Mac OS 上轻松运行。
如何做...
-
首先,我们生成一个证书:
$ cd /etc/openvpn/cookbook $ . ./vars $ ./build-key client4 [...] -
然后,我们立即撤销它:
$ ./revoke-full client4 Using configuration from /etc/openvpn/cookbook/openssl- 1.0.0.cnf Enter pass phrase for /etc/openvpn/cookbook/keys/ca.key: Revoking Certificate 06. Data Base Updated Using configuration from /etc/openvpn/cookbook/openssl- 1.0.0.cnf Enter pass phrase for /etc/openvpn/cookbook/keys/ca.key: client4.crt: C = US, O = Cookbook 2.4, CN = client4 error 23 at 0 depth lookup:certificate revoked -
这也将更新 CRL 列表。可以使用以下命令查看 CRL:
$ openssl crl -text -noout -in keys/crl.pem Certificate Revocation List (CRL): Version 1 (0x0) Signature Algorithm: sha256WithRSAEncryption Issuer: /C=US/O=Cookbook 2.4/CN=Cookbook 2.4 CA/emailAddress=openvpn@example.com Last Update: Apr 22 15:54:10 2016 GMT Next Update: May 22 15:54:10 2016 GMT Revoked Certificates: Serial Number: 06 Revocation Date: Apr 22 15:54:08 2016 GMT Signature Algorithm: sha256WithRSAEncryption 12:8a:f0:b4:3e:aa:5b:a1:13:64:41:c7:0b:46:ef:00:99:50: 6b:72:b8:2e:ff:93:eb:9b:7e:63:9e:8d:78:63:e8:96:44:30: 5b:eb:3d:4a:a4:2a:36:1e:8c:c6:cd:11:63:b1:d5:88:31:46:
它是如何工作的...
CRL 包含已被撤销的证书序列号列表。每个序列号只能由一个 CA 发放一次,因此该序列号对于这个特定的 CA 是唯一的。CRL 使用 CA 的私钥签名,确保 CRL 确实是由适当的方发布的。
还有更多...
“撤销证书到底需要什么?”这个问题经常被提到,因此接下来的部分会更深入地探讨这个问题。
撤销证书所需的内容
要撤销证书,需要提供证书主题("DN")和证书序列号。如果证书丢失,则无法撤销它。这表明进行适当的 PKI 管理非常重要,包括备份已发放给用户的证书。
另见
-
下一个配方,CRL 的使用
-
本章稍后的配方,多个 CA:堆叠,使用-capath 指令
CRL 的使用
本配方展示了如何配置 OpenVPN 使用 CRL。它使用前一个配方中创建的 CRL。该配方是第二章中的路由:伪装配方的扩展,意思是服务器和客户端的配置文件几乎相同。
准备工作
使用第二章中的第一个配方,客户端-服务器仅 IP 网络,设置客户端和服务器证书。使用前一个配方生成 CRL。在此配方中,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.10,客户端运行的是 Fedora 22 Linux 和 OpenVPN 2.3.10。保留第二章中的服务器端路由配方中的basic-udp-server.conf配置文件,客户端-服务器仅 IP 网络。
如何操作...
-
将生成的 CRL 复制到更公开的目录:
[root@server]# cd /etc/openvpn/cookbook [root@server]# cp keys/crl.pem . -
通过添加以下行修改服务器配置文件
basic-udp-server.conf:crl-verify /etc/openvpn/cookbook/crl.pem将其保存为
example4-6-server.conf。 -
启动服务器:
[root@server]# openvpn --config example4-6-server.conf -
接下来,创建客户端配置文件:
client proto udp remote openvpnserver.example.com port 1194 dev tun nobind remote-cert-tls server tls-auth /etc/openvpn/cookbook/ta.key 1 ca /etc/openvpn/cookbook/ca.crt cert /etc/openvpn/cookbook/client4.crt key /etc/openvpn/cookbook/client4.key将其保存为
example4-6-client.conf。 -
最后,启动客户端:
[root@client]# openvpn --config example4-6-client.conf
客户端无法连接,而是服务器日志文件中显示:
[...] TLS_ERROR: BIO read tls_read_plaintext error: error:140890B2:SSL
routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned
[...] TLS Error: TLS object -> incoming plaintext read error
[...] TLS Error: TLS handshake failed
这个相当晦涩的信息证明了客户端由于证书无效而无法连接。
它是如何工作的...
每次客户端连接到 OpenVPN 服务器时,都会检查 CRL 以查看客户端证书是否列出。如果列出,OpenVPN 服务器将拒绝接受客户端证书,连接将无法建立。
还有更多...
生成 CRL 是一回事,保持其最新是另一回事。确保 CRL 保持最新是非常重要的。为此,最好设置一个 cron 任务,在夜间更新服务器的 CRL 文件。OpenVPN 中有一个与 CRL 更新相关的已知 bug:每当客户端连接时,OpenVPN 服务器都会尝试访问 CRL 文件。如果文件不存在或不可访问,OpenVPN 服务器进程将因错误而中止。正确的行为应该是暂时拒绝客户端的访问,但不幸的是,情况并非如此。
另见
- 本章后续的示例,多个 CA:堆叠,使用-capath 指令,解释了 CA 和 CRL 的更高级用法。
检查过期/撤销的证书
本示例的目标是深入了解一些 OpenSSL CA 命令的内部实现。我们将展示如何将证书的状态从“有效”更改为“撤销”或“过期”。
准备工作
使用第二章中的第一个示例设置客户端和服务器证书,客户端-服务器 IP-only 网络。这个示例是在运行 CentOS 6 Linux 的计算机上执行的,但它也可以在 Windows 或 Mac OS 上轻松执行。
如何操作...
-
在我们可以使用普通的
openssl命令之前,需要设置几个环境变量。这些变量在vars文件中默认并未设置:$ cd /etc/openvpn/cookbook $ . ./vars $ export KEY_NAME= $ export OPENSSL_CONF=/etc/openvpn/cookbook/openssl-1.0.0.cnf -
现在,我们可以使用证书的序列号查询其状态:
$ cd keys $ openssl x509 -serial -noout -in server.crt serial=01 $ openssl ca -status 01 Using configuration from /etc/openvpn/cookbook/openssl- 1.0.0.cnf 01=Valid (V)这表明我们的 OpenVPN 服务器证书仍然有效。
-
我们在撤销证书示例中撤销的证书显示如下:
$ openssl x509 -serial -noout -in client4.crt serial=06 $ openssl ca -status 06 Using configuration from /etc/openvpn/cookbook/openssl- 1.0.0.cnf 08=Revoked (R) -
如果我们查看
index.txt文件,位于/etc/openvpn/cookbook/keys目录下,我们可以看到:V 181013174924Z 01 unknown .../CN=openvpnserver R 190117155337Z 160422155408Z 06 unknown .../CN=client4 -
接下来,我们使用普通的文本编辑器修改此文件,将
R替换为E,并将第三个字段160422155408Z用空格清空。该字段是证书撤销的时间戳。现在,第二行变成:E 190117155337Z 08 unknown .../CN=client4 -
现在,如果我们再次检查状态,我们会得到:
$ openssl ca -status 06 Using configuration from /etc/openvpn/cookbook/openssl- 1.0.0.cnf 08=Expired (E)如果我们再次生成 CRL,我们可以看到该证书已经被“撤销”:
$ openssl ca -gencrl -out crl.pem $ openssl crl -text -noout -in crl.pem | head -8 Certificate Revocation List (CRL): Version 1 (0x0) Signature Algorithm: sha256WithRSAEncryption Issuer: /C=US/O=Cookbook 2.4/CN=Cookbook 2.4 CA/emailAddress=openvpn@example.com Last Update: Apr 26 15:02:01 2016 GMT Next Update: May 26 15:02:01 2016 GMT No Revoked Certificates. Signature Algorithm: sha256WithRSAEncryption
它是如何工作的...
OpenSSL 的ca命令通过查看index.txt文件来生成 CRL。每一行以R开头的条目都会被添加到 CRL 中,之后,CRL 会使用 CA 私钥进行加密签名。
通过将撤销证书的状态更改为E甚至V,我们可以撤销撤销的证书。
还有更多内容...
在这个示例中,我们将一个证书的状态从撤销更改为过期。这将允许之前示例中的客户端再次连接到服务器,因为该证书仍然有效。从index.txt文件中将证书状态从有效更改为过期的主要原因,是为了允许我们使用完全相同的名称生成并发放新的证书。
中介 CA
本配方展示了如何设置中介 CA,并如何配置 OpenVPN 使用中介 CA。OpenVPN 的 easy-rsa 脚本也包括设置中介 CA 的功能。中介 CA(或子 CA)的优势在于,顶级 CA(也称为根 CA)可以更紧密地保护。中介 CA 可以分发给负责生成服务器和客户端证书的人员。
准备就绪
使用第二章中的第一个配方,设置客户端和服务器证书,客户端-服务器仅 IP 网络。该配方是在运行 CentOS 6 Linux 的计算机上执行的,但也可以在 Windows 或 Mac OS 上轻松执行。
如何操作...
-
首先,我们创建中介 CA 证书:
$ cd /etc/openvpn/cookbook/ $ . ./vars $ ./build-inter IntermediateCA -
验证该证书是否可以确实作为证书颁发机构:
$ openssl x509 -text -noout -in keys/IntermediateCA.crt \ | grep -C 1 CA X509v3 Basic Constraints: CA:TRUE Signature Algorithm: sha1WithRSAEncryption -
接下来,我们为中介 CA 创建一个新的
keys目录(当前目录仍然是/etc/openvpn/cookbook):$ mkdir -m 700 -p IntermediateCA/keys $ cp [a-z]* IntermediateCA $ cd IntermediateCA -
编辑新目录中的
vars文件,并将EASY_RSA行更改为:export EASY_RSA=/etc/openvpn/cookbook/IntermediateCA -
来源这个新的
vars文件,并设置keys目录:$ . ./vars $ ./clean-all $ cp ../keys/IntermediateCA.crt keys/ca.crt $ cp ../keys/IntermediateCA.key keys/ca.key -
现在我们准备创建我们的第一个中介证书:
$ ./build-key IntermediateClient -
验证证书是否以新的中介 CA 作为颁发者:
$ openssl x509 -subject -issuer -noout -in keys/IntermediateClient.crt subject= /C=US/O=Cookbook 2.4/CN=IntermediateClient issuer= /C=US/O=Cookbook 2.4/CN=subCA/emailAddress=... -
最后,我们验证证书是否确实是有效的证书。为了做到这一点,我们需要将根 CA(公钥)证书和中介 CA 证书堆叠成一个文件:
$ cd /etc/openvpn/cookbook $ cat keys/ca.crt IntermediateCA/keys/ca.crt > ca+subca.pem $ cp IntermediateCA/keys/IntermediateClient.{crt,key} . $ openssl verify -CAfile ca+subca.pem IntermediateClient.crt IntermediateClient.crt: OK
它是如何工作的...
中介 CA 证书具有作为证书颁发机构(CA)的“权利”,这意味着它可以自行签发新证书。中介 CA 需要一个目录结构,这个结构与根 CA 的目录结构非常相似。首先,我们设置这个目录结构,然后将所有必要的文件复制过来。之后,我们创建一个客户端证书,并验证它是有效的证书。为了进行此验证,从根级 CA 到中介 CA 到客户端证书的整个证书链都需要存在。这就是为什么根 CA 公钥证书和中介 CA 公钥证书会堆叠到一个文件中的原因。这个单一文件随后用于执行整个证书链验证。
还有更多内容...
已由中介 CA 签发的证书也需要由同一 CA 撤销。这意味着,使用多个 CA 时,您还需要使用多个 CRL。幸运的是,CRL 可以像 CA 证书一样堆叠:将文件连接在一起,使用 cat 命令,如下一个配方中所述。
多个 CA - 堆叠,使用 capath 指令
这个方案的目标是创建一个 OpenVPN 设置,其中客户端证书由“仅客户端”CA 签发,服务器证书由不同的“仅服务器”CA 签发。这提供了额外的操作安全性,其中一个人只被允许创建客户端证书,而另一个人只被允许生成服务器证书。这确保了客户端和服务器证书永远不能混合,从而避免中间人攻击。
准备工作
使用第二章中的第一个方案设置服务器证书,客户端-服务器 IP-only 网络。使用之前方案中的客户端证书和中介 CA 证书。对于此方案,服务器计算机运行 CentOS 6 Linux 和 OpenVPN 2.3.10,而客户端运行 Fedora 22 Linux 和 OpenVPN 2.3.10。
如何操作...
-
创建服务器配置文件:
tls-server proto udp port 1194 dev tun server 192.168.200.0 255.255.255.0 ca /etc/openvpn/cookbook/ca+subca.pem cert /etc/openvpn/cookbook/server.crt key /etc/openvpn/cookbook/server.key dh /etc/openvpn/cookbook/dh1024.pem tls-auth /etc/openvpn/cookbook/ta.key 0 persist-key persist-tun keepalive 10 60 user nobody group nobody daemon log-append /var/log/openvpn.log将其保存为
example4-9-server.conf。 -
启动服务器:
[root@server]# openvpn --config example4-9-server.conf -
接下来,创建客户端配置文件:
client proto udp remote openvpnserver.example.com port 1194 dev tun nobind tls-auth /etc/openvpn/cookbook/ta.key 1 ca /etc/openvpn/cookbook/ca.crt cert /etc/openvpn/cookbook/IntermediateClient.crt key /etc/openvpn/cookbook/IntermediateClient.key将其保存为
example4-9-client.conf。注意,我们没有在客户端配置中指定ca+subca.pem文件。 -
启动客户端:
[root@client]# openvpn --config example4-9-client.conf -
在服务器日志文件中,你现在可以看到客户端使用由中介 CA 创建的证书连接:
... openvpnclient:49283 [IntermediateClient] Peer Connection Initiated with openvpnclient:49283
它是如何工作的...
当客户端连接到服务器时,客户端(公钥)证书会发送给服务器进行验证。服务器需要访问完整的证书链才能进行验证;因此,我们将根 CA 证书和中介 CA(或子 CA)证书堆叠在一起。这使得客户端能够连接到服务器。
相反,当客户端连接时,服务器(公钥)证书也会发送给客户端。由于服务器证书最初是由根 CA 签名的,我们在此不需要指定完整的证书堆栈。
请注意,如果我们忘记在 OpenVPN 服务器配置文件中指定ca+subca.pem文件,我们将收到错误信息:
openvpnclient:49286 VERIFY ERROR: depth=0, error=unable to get local issuer certificate: C=US, O=Cookbook 2.4, CN=IntermediateClient
还有更多...
除了堆叠 CA 证书外,还可以堆叠 CRL 或使用完全不同的机制来支持多个 CA 证书及其对应的 CRL。
使用-capath 指令
另一种在 OpenVPN 服务器配置中包含多个 CA 和 CRL 的方法是使用以下指令:
capath /etc/openvpn/cookbook/ca-dir
这个目录需要包含所有 CA 证书和 CRL,采用特殊的命名规则:
-
所有 CA 证书的名称必须等于 CA 证书的哈希值,并且必须以
.0结尾。 -
所有 CRL 的名称必须等于 CA 证书的哈希值,并且必须以
.r0结尾。
对于我们的根 CA 和中介 CA,我们可以使用以下命令来实现:
$ cd /etc/openvpn/cookbook
$ mkdir ca-dir
$ openssl x509 -hash -noout -in keys/ca.crt
bcd54da9
这个十六进制数字bcd54da9是根 CA 证书的哈希值:
$ cp keys/ca.crt ca-dir/bcd54da9.0
$ cp keys/crl.pem ca-dir/bcd54da9.r0
类似地,对于中介 CA 证书:
$ openssl x509 -hash -noout -in IntermediateCA/keys/ca.crt
1f5e4734
$ cp IntermediateCA/keys/ca.crt ca-dir/1f5e4734.0
$ cp IntermediateCA/keys/crl.pem ca-dir/1f5e4734.r0
使用多个不同的 CA 证书和相应的 CRL,这种方法比“堆叠”文件管理起来要简单得多。
确定将使用的加密库
从 OpenVPN 2.3 开始,可以使用 OpenSSL 加密库或 PolarSSL 库来构建 OpenVPN。PolarSSL 库现已更名为 "mbedTLS"。PolarSSL 库在 OpenVPN Connect 应用程序中用于 Android 和 iOS 平台,但该库也可以在所有其他支持的平台上使用。
本配方的目标是展示如何确定使用的加密库,包括运行时的版本号。
正在准备中
使用来自第二章的第一个配方设置服务器证书,客户端-服务器仅 IP 网络。使用前一个配方中的客户端证书和中介 CA 证书。对于本配方,计算机运行的是 Fedora 22 Linux 和 OpenVPN 2.3.10,分别为 OpenSSL 和 PolarSSL 构建。保持来自第二章的 服务器端路由 配方中的 basic-udp-server.conf 配置文件。
如何操作...
-
使用标准配置文件启动常规版本的 OpenVPN:
[root@server]# openvpn --config basic-udp-server.conf -
检查服务器日志文件的前几行:
OpenVPN 2.3.10 x86_64-redhat-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [MH] [IPv6] built on Jan 4 2016 library versions: OpenSSL 1.0.1e-fips 11 Feb 2013, LZO 2.08 -
通过终止
openvpn进程停止服务器。 -
接下来,修改系统的
LD_LIBRARY_PATH,指向更新版的 OpenSSL:[root@server]# export LD_LIBRARY_PATH=..../openssl-1.0.1s [root@server]# openvpn --config basic-udp-server.conf -
检查服务器日志文件的前几行:
OpenVPN 2.3.10 x86_64-redhat-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [MH] [IPv6] built on Jan 4 2016 library versions: OpenSSL 1.0.1s 1 Mar 2016, LZO 2.08 -
再次通过终止
openvpn进程停止服务器。 -
切换到使用 PolarSSL 构建的 OpenVPN 版本并重新启动服务器:
[root@server]# .../openvpn-2.3.10polarssl/openvpn --config basic-udp-server.conf -
检查服务器日志文件的前几行:
OpenVPN 2.3.10 x86_64-unknown-linux-gnu [SSL (PolarSSL)] [LZO] [EPOLL] [MH] [IPv6] built on Apr 27 2016 library versions: PolarSSL 1.3.16, LZO 2.08
它是如何工作的...
当 OpenVPN 启动时,加密库会被加载和初始化。此时,库的版本信息会被检索并打印出来。通过使用不同构建版本的加密库,我们可以看到只有服务器日志文件的前几行会发生变化。
还有更多...
使用的加密库的类型和构建版本决定了 OpenVPN 一些更高级的功能,正如我们在接下来的几个配方中将看到的那样。库的版本信息对于调试无法正常工作的设置提供了至关重要的信息,正如我们在第六章中将看到的,OpenVPN 故障排除 - 配置。
另见
-
下一个配方将解释加密库之间的差异
-
来自第六章的 如何阅读 OpenVPN 日志文件 配方,详细介绍了如何阅读 OpenVPN 日志文件
OpenSSL 和 PolarSSL 的加密功能
如前一方案所述,从 OpenVPN 2.3 版本开始,可以使用 OpenSSL 加密库或 PolarSSL 库来构建 OpenVPN。在此方案中,我们将展示这两种加密库的一些关键区别。
准备就绪
使用第二章中的第一个方案设置服务器证书,仅 IP 网络的客户端-服务器。使用来自上一方案的客户端证书和中介 CA 证书。在此方案中,计算机运行的是 Fedora 22 Linux 和 OpenVPN 2.3.10,支持 OpenSSL 和 PolarSSL 两种构建方式。
如何操作...
-
启动常规版本的 OpenVPN 并使用
--show-ciphers选项:[root@server]# openvpn --show-ciphers -
OpenVPN 现在将列出所有可用的密码,OpenSSL 1.0+的密码列表可能会超过 50 个。最常用的密码包括:
BF-CBC 128 bit default key (variable) BF-CFB 128 bit default key (variable) (TLS client/server...) BF-OFB 128 bit default key (variable) (TLS client/server...) AES-128-CBC 128 bit default key (fixed) AES-128-OFB 128 bit default key (fixed) (TLS client...) AES-128-CFB 128 bit default key (fixed) (TLS client...) AES-256-CBC 256 bit default key (fixed) AES-256-OFB 256 bit default key (fixed) (TLS client...) AES-256-CFB 256 bit default key (fixed) (TLS client...) AES-128-CFB1 128 bit default key (fixed) (TLS client...) AES-192-CFB1 192 bit default key (fixed) (TLS client...) AES-256-CFB1 256 bit default key (fixed) (TLS client...) AES-128-CFB8 128 bit default key (fixed) (TLS client...) AES-192-CFB8 192 bit default key (fixed) (TLS client...) AES-256-CFB8 256 bit default key (fixed) (TLS client...) -
接下来,切换到使用 PolarSSL 构建的 OpenVPN 版本,并重新运行相同的命令:
[root@server]# .../openvpn-2.3.10polarssl/openvpn --show- ciphers -
当前的密码列表如下:
AES-128-CBC 128 bit default key AES-192-CBC 192 bit default key AES-256-CBC 256 bit default key BF-CBC 128 bit default key CAMELLIA-128-CBC 128 bit default key CAMELLIA-192-CBC 192 bit default key CAMELLIA-256-CBC 256 bit default key DES-CBC 64 bit default key DES-EDE-CBC 128 bit default key DES-EDE3-CBC 192 bit default key -
启动常规版本的 OpenVPN 并使用
--show-digests选项:[root@server]# openvpn --show-digests -
OpenVPN 现在将列出所有可用的 HMAC 算法,这些算法可以使用
--auth选项进行指定。该列表可能会超过 25 个条目,因此只会打印出最常用的:MD5 128 bit digest size SHA 160 bit digest size RIPEMD160 160 bit digest size ecdsa-with-SHA1 160 bit digest size SHA224 224 bit digest size SHA256 256 bit digest size SHA384 384 bit digest size SHA512 512 bit digest size -
接下来,切换到使用 PolarSSL 构建的 OpenVPN 版本,并重新运行相同的命令:
[root@server]# .../openvpn-2.3.10polarssl/openvpn --show- digests -
当前的 HMAC 算法列表如下:
SHA512 512 bit default key SHA384 384 bit default key SHA256 256 bit default key SHA224 224 bit default key SHA1 160 bit default key RIPEMD160 160 bit default key MD5 128 bit default key
它是如何工作的...
当 OpenVPN 启动时,加密库会被加载并初始化。仅在此时,才知道可用的加密算法和 HMAC 算法。OpenSSL 和 PolarSSL 都提供了一种机制来获取可用算法的列表,OpenVPN 使用该列表来处理--show-ciphers和--show-digests选项。
本方案展示了 PolarSSL/mbed-TLS 库不支持 OpenSSL 支持的所有算法。当你需要支持 PolarSSL 构建版本的 OpenVPN(例如 Android 和 iOS 的 OpenVPN Connect 客户端)时,你只能使用两种加密库都支持的密码或摘要(--auth参数)。
还有更多...
除了数据通道密码和 HMAC 算法外,还有一组可用的算法可以列出。这是用于加密和认证控制通道的 TLS 算法集。要列出 TLS 参数集,请使用以下命令:
openvpn --show-tls
AEAD 密码
从 OpenVPN 2.4 开始,支持一组新的密码。这些密码被称为AEAD密码,代表带相关数据的认证加密。这些密码将加密与认证结合,从而不再需要单独的 HMAC 算法,提升了性能。OpenSSL 1.0+和 mbed-TLS 1.3+都支持这些密码。在 OpenVPN 2.4+中,密码列表将包括:
-
AES-128-GCM
-
AES-192-GCM
-
AES-256-GCM
加密速度
OpenSSL 和 PolarSSL 之间的另一个主要区别是算法的加密/解密速度。OpenSSL 包含了为最大加密速度而手工调优的汇编例程,特别是对于新款 Intel CPU 上的 AES 算法。然而,加密速度并不是决定 OpenVPN 网络吞吐量时最重要的因素,正如我们将在第八章中看到的,性能调优章节所述。
推送密码算法
OpenVPN 2.4+的另一个新特性是能够从服务器“推送”密码算法或 HMAC 算法到客户端。这使得切换加密算法或 HMAC 认证算法变得更加容易,前提是所有客户端都使用 OpenVPN 2.4。这个教程提供了一个明确推送密码算法的设置,并解释了新的密码协商协议。
准备工作
本教程使用了第二章中客户端-服务器 IP 专用网络教程中创建的 PKI 文件。对于这个教程,服务器计算机运行 CentOS 6 Linux 和 OpenVPN 2.4.0。客户端运行 Fedora 22 Linux 和 OpenVPN 2.4.0。对于服务器,请保留第二章中服务器端路由教程中的服务器配置文件basic-udp-server.conf。对于 Windows 客户端,请保留第二章中使用 ifconfig-pool 块教程中的相应客户端配置文件basic-udp-client.ovpn。
如何操作...
-
通过添加以下行来修改服务器配置文件
basic-udp-server.conf:cipher aes-256-gcm push "cipher aes-256-gcm"然后将其保存为
example4-10-server.conf。 -
启动服务器:
[root@server]# openvpn --config example4-10-server.conf -
使用“标准”配置文件启动客户端,但启用详细日志:
[root@client]# openvpn --config basic-udp-client.conf -- verb 4 Data Channel Encrypt: Cipher 'BF-CBC' initialized with 128 bit key Data Channel Encrypt: Using 160 bit message hash 'SHA1' for HMAC authentication Data Channel Decrypt: Cipher 'BF-CBC' initialized with 128 bit key Data Channel Decrypt: Using 160 bit message hash 'SHA1' for HMAC authentication Control Channel: TLSv1.2, cipher TLSv1/SSLv3 ECDHE-RSA-AES256- GCM-SHA384, 2048 bit RSA [...] OPTIONS IMPORT: data channel crypto options modified [...] Data Channel Encrypt: Cipher '**AES-256-GCM**' initialized with 256 bit key Data Channel Decrypt: Cipher '**AES-256-GCM**' initialized with 256 bit key显示 OpenVPN 当前正在使用 AES-256 密码算法的输出将以粗体显示。
-
使用
ping命令验证我们是否能连接到服务器:[client]$ ping -c 4 10.200.0.1 PING 10.200.0.1 (10.200.0.1) 56(84) bytes of data. 64 bytes from 10.200.0.1: icmp_seq=1 ttl=64 time=9.23 ms 64 bytes from 10.200.0.1: icmp_seq=2 ttl=64 time=8.78 ms 64 bytes from 10.200.0.1: icmp_seq=3 ttl=64 time=10.0 ms 64 bytes from 10.200.0.1: icmp_seq=4 ttl=64 time=9.00 ms --- 10.200.0.1 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3004ms rtt min/avg/max/mdev = 8.780/9.259/10.022/0.468 ms
它是如何工作的...
推送密码算法现在和推送其他 OpenVPN 选项一样简单。2.4 之前的版本不支持这一功能。这使得 VPN 管理员可以更改所用的加密参数,而无需修改所有(远程)客户端配置文件。
还有更多…
从 OpenVPN 2.4 开始,引入了新的密码协商协议。在启动时,客户端和服务器将检查双方是否都支持新的 GCM 加密协议。然后,从这个列表中选择最强的密码算法作为加密算法。如果没有找到匹配项,OpenVPN 会回退到默认的 BlowFish(BF-CBC)密码算法,以确保向后兼容性。
这个功能可以通过新的指令ncp-ciphers和disable-ncp进行调节。第一个指令指定协商的密码算法列表,而第二个指令则完全关闭密码算法协商。
当从服务器显式推送密码算法到客户端时,你只能从 NCP 密码列表中指定一个密码。默认的 NCP 密码列表是 AES-256-GCM:AES-128-CGM:BF-CBC。
ccp-ciphers
push "auth SHA512"
未来的增强功能
预计未来对这一新功能的增强将包括:
-
一个单独的控制通道 HMAC 算法,使你能够独立切换数据通道算法
-
可以设置“每个客户端”加密密码算法,允许为不同的平台和客户端支持不同的密码算法
椭圆曲线支持
在 OpenVPN 的 2.4 版本中,增加了使用椭圆曲线(EC)证书代替更常见的 RSA 类型证书的支持。椭圆曲线加密(ECC)提供了一种快速的加密和认证安全连接的方法,但尚未广泛使用。部分原因是一些专利问题。然而,由于大多数现代 OpenSSL 库提供 ECC 支持,OpenVPN 也可以使用 EC 证书。ECC 的主要优势在于,你可以提供更小的密钥来达到与更常见的 RSA 和 DSA 类型加密相同的安全级别。这将提高 VPN 性能,同时不牺牲安全性。正如我们在本示例中看到的,OpenVPN 的控制通道可以使用 EC 算法进行认证。数据通道仍然使用非 EC HMAC 算法,如 SHA1,进行认证。
准备工作
对于这个示例,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.4.0,客户端运行的是 Fedora 22 Linux 和 OpenVPN 2.4.0。
如何操作...
-
我们首先需要生成一个新的基于 EC 的证书颁发机构:
$ export KEY_CN= $ export KEY_OU= $ export KEY_NAME= $ export OPENSSL_CONF=/etc/openvpn/cookbook/openssl- 1.0.0.cnf $ openssl ecparam -out cakey_temp.pem \ -name sect571k1 -text -genkey $ openssl ec -in cakey_temp.pem -out ec-ca.key -aes256 $ openssl req -new -x509 -out ec-ca.crt -key ec-ca.key -days 3650 -sha512 -extensions v3_ca -subj "/C=US/O=Cookbook 2.4/CN=Elliptic Curve CA"这将生成
ec-ca.crt和ec-ca.key文件,使用sect571k1椭圆曲线,我们将用它来签署基于 EC 的客户端和服务器证书。 -
接下来,生成新的 EC 服务器证书:
$ openssl req -nodes -sha512 -newkey ec:ec-ca.crt -new -days 400 -out ec-server.req -keyout ec-server.key -subj "/C=US/O=Cookbook 2.4/CN=ecserver" $ chmod 600 ec-server.key $ openssl x509 -req -extfile $OPENSSL_CONF -extensions server -out ec-server.crt -sha512 -CA ec-ca.crt -CAkey ec-ca.key -in ec-server.req -set_serial $RANDOM这将生成
ec-server.crt和ec-server.key文件。 -
类似地,生成新的 EC 客户端证书:
$ openssl req -nodes -sha512 -newkey ec:ec-ca.crt -new -days 400 -out ec-client.req -keyout ec-client.key -subj "/C=US/O=Cookbook 2.4/CN=ecclient" $ chmod 600 ec-client.key $ openssl x509 -req -extfile $OPENSSL_CONF -extensions usr_cert -out ec-client.crt -sha512 -CA ec-ca.crt -CAkey ec-ca.key -in ec-client.req -set_serial $RANDOM这将生成
ec-client.crt和ec-client.key文件。 -
创建服务器配置文件:
proto udp port 1194 dev tun server 10.200.0.0 255.255.255.0 ca /etc/openvpn/cookbook/ec-ca.crt cert /etc/openvpn/cookbook/ec-server.crt key /etc/openvpn/cookbook/ec-server.key dh /etc/openvpn/cookbook/dh2048.pem将其保存为
example4-11-server.conf。 -
启动服务器:
[root@server]# openvpn --config example4-11-server.conf -
接下来,创建客户端配置文件:
client proto udp remote openvpnserver.example.com port 1194 dev tun nobind ca /etc/openvpn/cookbook/ec-ca.crt cert /etc/openvpn/cookbook/ec-client.crt key /etc/openvpn/cookbook/ec-client.key verb 4然后将其保存为
example4-11-client.conf。 -
使用安全通道将文件如
ec-ca.crt、ec-client.crt和ec-client.key传输到客户端计算机。 -
最后,启动客户端:
[root@client]# openvpn --config example4-11-client.conf观察所选的控制通道密码算法:
Control Channel: TLSv1.2, cipher TLSv1/SSLv3 ECDHE-ECDSA- AES256-GCM-SHA384这表明控制通道使用基于 ECDSA 的密码算法进行保护。
它是如何工作的...
通过生成基于 EC 的证书颁发机构并使用基于 EC 的证书,OpenVPN 现在可以在控制通道上支持椭圆曲线加密。数据通道仍然使用默认的 BF-CBC(Blowfish)密码算法和默认的 HMAC 算法 SHA1 进行保护。
应该注意的是,使用基于 RSA 的证书时,控制通道的密码算法看起来非常相似:
Control Channel: TLSv1.2, cipher TLSv1/SSLv3 ECDHE-RSA-AES256-GCM-SHA384, 2048 bit RSA
并不是“ECDHE”部分证明了使用了 ECC,而是“ECDSA”。
还有更多...
还可以选择不同的 ECDH“曲线”。这是通过首先列出 OpenVPN 服务器上可用的 ECDH 曲线来完成的:
[root@server]# openvpn --show-curves
Available Elliptic curves:
[...]
secp112r1
secp112r2
secp521r1
prime192v1
prime192v2
[...]
然后通过将选项添加到服务器配置文件中:
ecdh-curve secp521r1
椭圆曲线支持
并非所有 Linux 发行版都提供开箱即用支持椭圆曲线加密的 OpenSSL 库。特别是基于 RedHat 的和源自 RedHat 的发行版,如 RedHat Enterprise Linux、CentOS 和 Fedora,明确禁用了 ECC 支持。RedHat 引用专利问题作为原因,但“默认”OpenSSL 库提供了完整的 ECC 支持。
由于本书中使用的 Linux 发行版是 CentOS 和 Fedora,因此特别为本食谱制作了 OpenSSL 1.0.2 库的自定义版本。
第五章:脚本和插件
本章将涵盖以下实例:
-
使用客户端的 up/down 脚本
-
使用
client-connect脚本 -
使用
learn-address脚本 -
使用
tls-verify脚本 -
使用
auth-user-pass-verify脚本 -
脚本顺序
-
脚本安全性和日志记录
-
脚本编写与 IPv6
-
使用
down-root插件 -
使用 PAM 认证插件
简介
OpenVPN 的一个强大功能是其脚本能力,并通过插件扩展 OpenVPN 本身。通过客户端脚本,可以根据站点的具体需求定制连接过程,比如设置高级路由选项、添加防火墙规则或映射网络驱动器。通过服务器端脚本,可以为不同客户端分配自定义 IP 地址,或者通过增加额外的用户名和密码检查来扩展认证过程。插件在将 OpenVPN 认证集成到现有的认证框架(如 PAM、LDAP 或 Active Directory)中时非常有用。
本章将重点介绍脚本编写,包括客户端和服务器端的脚本,以及一些常用的插件。
使用客户端的 up/down 脚本
在这个实例中,我们将在客户端使用非常简单的up和down脚本来展示 OpenVPN 如何调用这些脚本。通过将消息记录到文件以及环境变量中,我们可以轻松地看到 OpenVPN 提供给up和down脚本的信息。
准备工作
使用第二章中的设置公钥和私钥实例来设置客户端和服务器证书,客户端-服务器仅 IP 网络。在这个实例中,服务器计算机运行的是 Fedora 22 Linux 和 OpenVPN 2.3.10,客户端运行的是 Windows 7 64 位和 OpenVPN 2.3.10。保留服务器配置文件basic-udp-server.conf,该文件来自第二章中的服务器端路由实例。
如何实现...
-
启动服务器:
[root@server]# openvpn --config basic-udp-server.conf -
创建客户端配置文件:
client proto udp remote openvpnserver.example.com port 1194 dev tun nobind ca "c:/program files/openvpn/config/ca.crt" cert "c:/program files/openvpn/config/client2.crt" key "c:/program files/openvpn/config/client2.key" tls-auth "c:/program files/openvpn/config/ta.key"" 1 remote-cert-tls server script-security 2 up "c:\\program\ files\\openvpn\\scripts\\updown.bat" down "c:\\program\ files\\openvpn\\scripts\\updown.bat"将文件保存为
example5-1.ovpn。请注意反斜杠:在指定ca、cert、key和tls-auth指令时可以使用正斜杠,但up和down脚本不能使用! -
接下来,在 Windows 客户端上,在
C:\Program Files\OpenVPN\scripts目录中创建批处理文件updown.bat:@echo off echo === BEGIN '%script_type%' script === >> c:\temp\openvpn.log echo Script name: [%0] >> c:\temp\openvpn.log echo Command line argument 1: [%1] >> c:\temp\openvpn.log echo Command line argument 2: [%2] >> c:\temp\openvpn.log echo Command line argument 3: [%3] >> c:\temp\openvpn.log echo Command line argument 4: [%4] >> c:\temp\openvpn.log echo Command line argument 5: [%5] >> c:\temp\openvpn.log echo Command line argument 6: [%6] >> c:\temp\openvpn.log echo Command line argument 7: [%7] >> c:\temp\openvpn.log echo Command line argument 8: [%8] >> c:\temp\openvpn.log echo Command line argument 9: [%9] >> c:\temp\openvpn.log set >> c:\temp\openvpn.log echo === END '%script_type%' script === >> c:\temp\openvpn.log -
最后,启动 OpenVPN 客户端:
客户端成功连接到 OpenVPN 服务器后,c:\temp\openvpn.log日志文件将包含类似以下的输出:
=== BEGIN 'up' script ===
Script name: ["c:\program files\openvpn\scripts\updown.bat"]
Command line argument 1: [Local Area Connection 2]
Command line argument 2: [1500]
Command line argument 3: [1541]
Command line argument 4: [10.200.0.2]
Command line argument 5: [255.255.255.0]
Command line argument 6: [init]
Command line argument 7: []
Command line argument 8: []
Command line argument 9: []
...
script_type=up
[dump of environment variables]
...
=== END 'up' script ===
当客户端与服务器断开连接时,脚本会再次被调用,且命令行参数完全相同,但此时script_type设置为down。
请注意,第一个命令行参数包含 TUN 设备的名称。在 Linux 和 Mac OS 系统上,这通常是 tun0 或 tun1,但在 Windows 平台上,它是实际的 TAP-Win32 适配器名称。
工作原理...
在与 OpenVPN 服务器建立初始连接后,但在 VPN 完全建立之前,OpenVPN 客户端调用 up 脚本。如果 up 脚本返回的退出代码不等于零,则连接过程会被中止。
同样,当连接关闭时,down 脚本会在 VPN 连接停止后执行。
注意在 up 和 down 指令中使用双反斜杠 (\\):OpenVPN 在内部翻译反斜杠字符,因此需要指定两次。在 c:\\program 和 files 之间的反斜杠是必需的,否则 OpenVPN 无法找到 up 和 down 脚本。
还有更多...
在本节中,我们将看到使用 up 和 down 脚本的一些更高级技巧,包括一个示例脚本,用于验证 VPN 服务器的远程主机名。
环境变量
本配方中使用的脚本仅将所有环境变量写入文件。这些环境变量包含有关远程服务器的有用信息,如 common_name 证书。此脚本的扩展可以检查 common_name 证书是否与远程主机名匹配。远程主机名的 IP 地址作为 trusted_ip 提供。
在连接终止之前调用 down 脚本
down 脚本在与 OpenVPN 服务器的实际连接停止后执行。也可以在断开连接阶段执行该脚本,即在与服务器的连接断开之前。要实现此功能,请将以下指令添加到客户端配置文件中:
down-pre
高级 - 验证远程主机名
up 脚本的更高级用法是验证远程主机名是否与远程 IP 地址匹配,类似于 Web 浏览器验证安全网站地址的方式。在 Linux 系统上,这可以通过使用 shell 脚本作为 up 脚本轻松实现:
#!/bin/bash
# reverse DNS lookup
server_name=`host $untrusted_ip | \
sed -n 's/.*name pointer \(.*\)\./\1/p'
if [ "$server_name" != "$common_name" ]
then
echo "Server certificate does not match hostname."
echo "Aborting"
exit 1
fi
但在 Windows 上,除非使用 PowerShell 或 Cygwin 等工具,否则这会更加复杂。
使用客户端连接脚本
本配方将演示如何设置一个客户端连接脚本,该脚本在新客户端连接时在服务器端执行。同样,我们可以指定一个 client-disconnect 脚本,该脚本在客户端与服务器断开连接时执行。客户端连接和客户端断开脚本可以用于多种目的:
-
额外认证
-
开放和关闭防火墙端口
-
为特定客户端分配特定 IP 地址
-
为客户端编写特定的连接配置行
在这个教程中,我们将使用client-connect脚本,在晚上 10 点(或 22:00)到早上 6 点之间禁用使用client2证书的客户端访问。其他时间段内,将为该客户端分配一个静态 IP 地址。
正在准备中
在两台计算机上安装 OpenVPN 2.3 或更高版本。确保计算机通过网络连接。使用第二章的第一个教程设置客户端和服务器证书,客户端-服务器 IP 网络。在这个示例中,服务器计算机运行的是 Fedora 22 Linux 和 OpenVPN 2.3.10,而客户端运行的是 Windows 7 64 位和 OpenVPN 2.3.10。保持basic-udp-server.conf服务器配置文件(来自服务器端路由教程,出自第二章,客户端-服务器 IP 网络)在手。对于客户端,保持basic-udp-client.ovpn配置文件(来自第二章的使用 ifconfig-pool 块教程,客户端-服务器 IP 网络)在手。
如何操作...
-
将以下内容添加到
basic-udp-server.conf服务器配置文件中:script-security 2 client-connect /etc/openvpn/cookbook/example5-2-connect.sh -
将其保存为
example5-2-server.conf。 -
接下来,创建连接脚本:
#!/bin/bash if [ "x$common_name" = "xclient2" ] then hour= /bin/date +"%H" if [ $hour -lt 6 -o $hour -gt 22 ] then echo "disable" > $1 else echo "ifconfig-push 10.200.0.200 255.255.255.0" fi fi -
将此文件保存为
example5-2-connect.sh。 -
确保脚本是可执行的:
[root@server]# chmod 755 example5-2-connect.sh -
启动服务器:
[root@server]# openvpn --config example5-2-server.conf -
启动 OpenVPN 客户端:
-
如果客户端在早上 6 点到晚上 10 点之间启动,连接将会成功建立。否则,客户端日志文件将显示类似以下的内容:
us=70083 SENT CONTROL [openvpnserver]: 'PUSH_REQUEST' (status=1)同时,服务器日志会更明确地说明连接拒绝的原因:
client2/192.168.3.22:57870 MULTI: client has been rejected due to 'disable' directive
它是如何工作的...
当客户端连接时,OpenVPN 服务器会执行client-connect脚本,并设置一些与客户端连接相关的环境变量。该脚本会向特定于连接的配置文件写入两行,这个配置文件作为第一个也是唯一的参数传递给client-connect脚本。然后,OpenVPN 服务器会像处理普通配置文件一样处理这个配置文件。我们使用的两个可能的选项是disable和ifconfig-push 10.200.0.200 255.255.255.0。
第一个选项禁用客户端连接。第二个选项向客户端推送预定义的 IP 地址。
还有更多内容...
在本节中,我们重点讲解client-disconnect和所有 OpenVPN 脚本可用的许多环境变量。
使用 ifconfig-push 时的陷阱
这里使用的client-connect脚本没有检查通过ifconfig-push 10.200.0.200 255.255.255.0命令分配的 IP 地址是否实际上可用。如果有多个客户端连接到服务器,那么这个 IP 地址将从由server 10.200.0.0 255.255.255.0语句生成的 IP 地址池中分配。
在为客户端分配静态 IP 地址时,最好从一个特殊的子网分配它们。
客户端断开连接脚本
可以使用以下方式指定 client-disconnect 脚本:
client-disconnect /etc/openvpn/cookbook/disconnect.sh
当客户端从服务器断开连接时执行此脚本。请注意,当客户端首次断开连接且客户端端未指定 explicit-exit-notify 时,OpenVPN 服务器将首先尝试多次重新连接客户端。如果客户端在多次尝试后仍未响应,则将执行 client-disconnect 脚本。根据服务器配置,这可能是客户端实际断开连接后数分钟。使用 TCP 连接时,无需指定 explicit-exit-notify,因为当 TCP 连接停止时,客户端会立即断开连接。
环境变量
在 client-connect 和 client-disconnect 脚本中可用多种环境变量。编写一个比以下脚本更详细的 client-connect 脚本非常有教育意义:
#!/bin.bash
env >> /tmp/log
与 up 和 down 脚本类似的还有 script_type 环境变量,该变量包含在服务器配置文件中配置的脚本类型。这使服务器管理员可以为 client-connect 和 client-disconnect 编写单个脚本。
绝对路径
请注意脚本使用了绝对路径。允许使用相对路径,但特别是对于 OpenVPN 服务器,使用绝对路径更安全。假设 OpenVPN 服务器始终在相同目录中启动是不良的安全实践。一个替代方法是使用以下内容:
cd /etc/openvpn/cookcook
client-connect example5-2-connect.sh
使用learn-address脚本
本文档将演示如何设置一个 learn-address 脚本,该脚本在连接客户端地址变化时在服务器端执行。learn-address 脚本可用于为特定客户端动态设置防火墙规则或调整路由表。
在本文档中,我们将使用一个 learn-address 脚本来打开防火墙并为客户端设置伪装。当客户端断开连接时,关闭防火墙,并移除 iptables 伪装规则。
准备工作
在两台计算机上安装 OpenVPN 2.3 或更高版本。确保计算机通过网络连接。使用第一章节的客户端和服务器证书配置第二章中的第一个配方,仅 IP 客户端服务器网络。对于这个配方,服务器计算机运行 Fedora 22 Linux 和 OpenVPN 2.3.10,客户端运行 Windows 7 64 位和 OpenVPN 2.3.10。对于客户端,保留来自使用 ifconfig-pool block配方的客户端配置文件basic-udp-client.ovpn,从第二章,仅 IP 客户端服务器网络。
如何做…
-
创建服务器配置文件:
proto udp port 1194 dev tun server 10.200.0.0 255.255.255.0 ca /etc/openvpn/cookbook/ca.crt cert /etc/openvpn/cookbook/server.crt key /etc/openvpn/cookbook/server.key dh /etc/openvpn/cookbook/dh2048.pem tls-auth /etc/openvpn/cookbook/ta.key 0 persist-key persist-tun keepalive 10 60 topology subnet daemon log-append /var/log/openvpn.log script-security 2 learn-address /etc/openvpn/cookbook/example5-3-learn-address.sh push "redirect-gateway def1" -
将其保存为
example5-3-server.conf。注意,此服务器配置文件中没有user nobody和group nobody(也没有group nogroup)行。 -
接下来,创建
learn-address脚本:#!/bin/bash # $1 = action (add, update, delete) # $2 = IP or MAC # $3 = client_common name if [ "$1" = "add" ] then /sbin/iptables -I FORWARD -i tun0 -s $2 -j ACCEPT /sbin/iptables -I FORWARD -o tun0 -d $2 -j ACCEPT /sbin/iptables -t nat -I POSTROUTING -s $2 -o wlan0 -j MASQUERADE elif [ "$1" = "delete" ] then /sbin/iptables -D FORWARD -i tun0 -s $2 -j ACCEPT /sbin/iptables -D FORWARD -o tun0 -d $2 -j ACCEPT /sbin/iptables -t nat -D POSTROUTING -s $2 -o wlan0 -j MASQUERADE fi -
将此文件保存为
example5-3-learn-address.sh。 -
确保脚本是可执行的,并启动 OpenVPN 服务器:
[root@server]# chmod 755 example5-3-learn-address.sh [root@server]# openvpn --config example5-3-server.conf -
使用基本配置文件通过 Windows GUI 启动客户端:
-
客户端连接到服务器后,检查服务器上的
iptables防火墙规则:[root@server]# iptables -L FORWARD -n -v Chain FORWARD (policy ACCEPT 4612K packets, 1761M bytes) pkts bytes target prot opt in out source destination 0 0 ACCEPT all -- * tun0 0.0.0.0/0 10.200.0.2 0 0 ACCEPT all -- tun0 * 10.200.0.2 0.0.0.0/0 [root@server]# iptables -t nat -L POSTROUTING -n -v Chain POSTROUTING (policy ACCEPT 336K packets, 20M bytes) pkts bytes target prot opt in out source destination 0 0 MASQUERADE all -- * wlan0 10.200.0.2 0.0.0.0/0 -
断开客户端连接,等待几分钟,然后验证
iptables规则是否已被移除。
它是如何工作的...
当客户端连接或断开连接 OpenVPN 服务器时,OpenVPN 服务器会使用几个命令行参数执行 learn-address 脚本:
-
$1:操作(添加、更新、删除)。 -
$2:IP 或 MAC。对于基于 tun 的网络,这是客户端的 IP 地址。对于基于 tap 的网络,这是客户端(虚拟)MAC 地址。 -
$3:client_common名称。
在本例中,learn-address 脚本用于为连接的客户端打开防火墙,并为客户端设置伪装规则,以便客户端可以访问服务器端局域网上的其他计算机。
还有更多...
在接下来的部分中,将介绍 user nobody 指令和 learn-address 脚本的 update 操作的一些细节。
用户 nobody
如前所述,此服务器配置不包括以下行:
user nobody
group nobody
或者,在某些 Linux 发行版中,它可以如下所示:
group
nogroup
如果我们添加了这些行,那么 OpenVPN 服务器进程将作为 nobody 用户运行。此用户没有足够的权限通过 iptables 打开和关闭防火墙端口,因此在本例中将其删除。
更新操作
当 OpenVPN 服务器检测到客户端侧的地址变化时,也会调用 learn-address 脚本。这个情况在使用外部 DHCP 服务器的 TAP 网络中最为常见。此时,learn-address 脚本可以基于新的客户端 IP 地址调整路由表或防火墙规则,使用 update 操作。
生成 learn-address update 操作的另一种方法是通过触发服务器的软重启;例如,通过向服务器进程发送 USR1 信号。这将导致所有客户端重新连接,这时会触发 update 操作。
使用 tls-verify 脚本
OpenVPN 在多个层次上验证连接客户端的凭证。通过指定 tls-verify 脚本,甚至可以向验证过程添加自定义层。在本例中,我们将演示如何使用该脚本,只允许某个特定证书进行访问。
准备工作
在两台计算机上安装 OpenVPN 2.3 或更高版本。确保计算机之间通过网络连接。使用第二章中的 设置公钥和私钥 例子来设置客户端和服务器证书,客户端-服务器 IP-only 网络。在这个例子中,服务器计算机运行 Fedora 22 Linux 和 OpenVPN 2.3.10,而客户端运行 Windows 7 64 位和 OpenVPN 2.3.10。对于客户端,保留第二章 使用 ifconfig-pool 块 例子中的 basic-udp-client.ovpn 配置文件,客户端-服务器 IP-only 网络。
如何操作...
-
创建服务器配置文件:
proto udp port 1194 dev tun server 10.200.0.0 255.255.255.0 ca /etc/openvpn/cookbook/ca.crt cert /etc/openvpn/cookbook/server.crt key /etc/openvpn/cookbook/server.key dh /etc/openvpn/cookbook/dh2048.pem tls-auth /etc/openvpn/cookbook/ta.key 0 persist-key persist-tun keepalive 10 60 topology subnet user nobody group nobody # nogroup on some distros daemon log-append /var/log/openvpn.log script-security 2 tls-verify /etc/openvpn/cookbook/example5-4-tls-verify.sh -
将其保存为
example5-4-server.conf。 -
接下来,创建
tls-verify脚本:#!/bin/bash [ $# -lt 2 ] && exit 1 # if the depth is non-zero , continue processing [ "$1" -ne 0 ] && exit 0 allowed_cns=`sed 's/ /_/g' $0.allowed` for i in $allowed_cns do [ "$2" = "$i" ] && exit 0 done # catch-all exit 1 -
将此文件保存为
example5-4-tls-verify.sh。 -
确保脚本是可执行的:
[root@server]# chmod 755 example5-4-tls-verify.sh -
最后,创建允许的证书列表:
[root@server]# echo "/C=US/O=Cookbook 2.4/CN=client1" > \ /etc/openvpn/cookbook/example5-4-tls-verify.sh.allowed请注意,这是一个单行命令。
-
启动 OpenVPN 服务器:
[root@server]# openvpn --config example5-4-server.conf -
使用 Windows GUI 启动客户端,使用基本配置文件:
客户端应该能够正常连接。
-
现在,在 OpenVPN 服务器上,删除
/etc/openvpn/cookbook/example5-4-tls-verify.sh.allowed文件并重新连接。这时,服务器日志将显示以下内容:CN not found in /etc/openvpn/cookbook/example5-4-tls- verify.sh.allowed, denying access ... openvpnclient1:9007 TLS_ERROR: BIO read tls_read_plaintext error: error:140890B2:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned ... openvpnclient1:9007 TLS Error: TLS object -> incoming plaintext read error ... openvpnclient1:9007 TLS Error: TLS handshake failed这意味着客户端被 OpenVPN 服务器拒绝访问。
它是如何工作的...
当客户端连接到 OpenVPN 服务器时,tls-verify 脚本会多次执行,以验证连接客户端的整个证书链。在这个例子中,我们查找终端用户证书,它相当于 client1.crt 文件。当在 example5-4-tls-verify.sh.allowed 文件中找到该终端用户证书时,脚本返回 0,表示验证成功。如果没有找到,OpenVPN 日志中会打印一条消息,脚本返回 1。然后,OpenVPN 服务器会拒绝该客户端的访问。
还有更多...
在这个例子中,我们只关注终端用户证书,使用一个简单的查找表。当然,也可以通过其他多种方式来实现(例如,使用 client-config-dir 文件)。通过 tls-verify 脚本,还可以禁止来自特定证书颁发机构(CA)的所有证书。
在更复杂的设置中,如果客户端证书可能由多个不同的 CA 签发,临时拒绝来自特定 CA 的所有证书的访问是非常有用的。例如,为了拒绝所有由主题名称为 "Cookbook 2.4 CA" 的 CA 证书签发的证书访问,可以使用以下脚本:第二章中的 客户端-服务器 IP-only 网络:
#!/bin/bash
[ $# -lt 2 ] && exit 1
CA=`echo $2 | sed -n 's/.*\/CN=\(.*\)\/.*/\1/p'`
[ "$CA" = "Cookbook 2.4 CA" ] && exit 1
使用 auth-user-pass-verify 脚本
除了证书和私钥外,OpenVPN 还提供了使用用户名和密码机制来验证客户端访问的选项。在这个示例中,我们将演示如何设置一个auth-user-pass-verify脚本,该脚本在客户端连接时由服务器端执行。这个脚本可以用来在数据库或文件中查找用户,也可以用来验证输入的密码是否正确。
准备就绪
在两台计算机上安装 OpenVPN 2.3 或更高版本。确保这两台计算机已通过网络连接。使用第二章中的第一个示例设置客户端和服务器证书,客户端-服务器 IP-only 网络。对于本示例,服务器计算机运行 CentOS 6 Linux 和 OpenVPN 2.3.10,客户端运行 Fedora 22 和 OpenVPN 2.3.10。对于服务器,保持使用服务器端路由示例中的服务器配置文件basic-udp-server.conf,该示例来自第二章,客户端-服务器 IP-only 网络。
如何操作...
-
向服务器配置文件
basic-udp-server.conf添加一行:script-security 2 auth-user-pass-verify /etc/openvpn/cookbook/example5-5-aupv.sh via-file请注意,最后一行是单独的一行。将其保存为
example5-5-server.conf。 -
创建
auth-user-pass-verify脚本:#!/bin/bash # the username+password is stored in a temporary file # pointed to by $1 username=`head -1 $1` password=`tail -1 $1` if grep "$username:$password" $0.passwd > /dev/null 2>&1 then exit 0 else if grep "$username" $0.passwd > /dev/null 2>&1 then echo "auth-user-pass-verify: Wrong password entered for user '$username'" else echo "auth-user-pass-verify: Unknown user '$username'" fi exit 1 fi -
将其保存为
example5-5-aupv.sh。 -
设置一个(非常不安全的!)密码文件:
[server]$ cd /etc/openvpn/cookbook [server]$ echo "cookbook:koobcook" > example5-5- aupv.sh.passwd -
确保
auth-user-pass-verify脚本是可执行的,然后启动服务器:[root@server]#$ chmod 755 example5-5-aupv.sh [root@server]# openvpn --config example5-5-server.conf -
接下来,创建客户端配置文件:
client proto udp remote openvpnserver.example.com port 1194 dev tun nobind ca /etc/openvpn/cookbook/ca.crt cert /etc/openvpn/cookbook/client1.crt key /etc/openvpn/cookbook/client1.key tls-auth /etc/openvpn/cookbook/ta.key 1 remote-cert-tls server auth-user-pass -
将其保存为
example5-5-client.conf。 -
启动客户端:
[root@client]# openvpn --config example5-5-client.conf -
首先,OpenVPN 客户端将询问用户名和密码:
Enter Auth Username: cookbook Enter Auth Password: koobcook -
然后,如果输入正确的密码,连接将正常建立。
-
接下来,尝试使用不同的用户名重新连接:
Enter Auth Username: janjust Enter Auth Password: whatever -
服务器日志现在将显示以下内容:
auth-user-pass-verify: Unknown user 'janjust' ... openvpnclient:50834 TLS Auth Error: Auth Username/Password verification failed for peer
客户端现在被拒绝访问。
它是如何工作的...
OpenVPN 客户端首先提示用户输入身份验证的用户名和密码。请注意,密码通过安全通道发送到服务器,但密码本身没有被哈希或加密。服务器端的auth-user-pass-verify脚本会将用户名和密码以文件形式传递,其中密码位于两行中。脚本随后在密码文件中查找用户名,并验证是否指定了正确的密码。如果密码正确,脚本将以退出代码 0 退出,表示成功。否则,返回退出代码 1,导致服务器中止客户端连接。
还有更多...
在接下来的部分中,我们将看到有关如何指定密码以及如何将密码从服务器传递到auth-user-pass-verify脚本的一些细节。
在客户端文件中指定用户名和密码
OpenVPN 提供了在客户端的文件中指定用户名和密码的选项。为了实现这一点,OpenVPN 需要使用一个特殊的标志进行编译,从 OpenVPN 2.3 开始默认启用该选项。
请注意,允许密码以明文格式存储在客户端是不安全的!
通过环境变量传递密码
在本节中,我们使用了以下内容:
auth-user-pass-verify example5-5-aupv.sh via-file
这将配置 OpenVPN 服务器通过临时文件传递客户端提供的用户名和密码。该临时文件仅对服务器进程可访问,因此这是将加密密码安全地传递给auth-user-pass-verify脚本的机制。
也可以通过环境变量将用户名和密码传递给auth-user-pass-verify脚本:
auth-user-pass-verify example5-5-aupv.sh via-env
这样做的优点是无需创建额外的文件。缺点是通过明文和环境变量传递密码的安全性稍差:比起读取其他用户拥有的安全文件,嗅探其他进程的环境要容易(但并非易事!)。
脚本顺序
在 OpenVPN 服务器上可以配置多种脚本,因此确定这些脚本的执行顺序变得非常重要。在本节中,我们将了解执行顺序是什么,以及每个脚本的命令行参数。
准备工作
在两台计算机上安装 OpenVPN 2.3 或更高版本,确保计算机通过网络连接。使用第二章中的第一个配方设置客户端和服务器证书,客户端-服务器仅 IP 网络。在此配方中,服务器计算机运行 CentSO 6 Linux 和 OpenVPN 2.3.10,客户端运行 Fedora 22 和 OpenVPN 2.3.10。对于服务器,保留来自服务器端路由配方的服务器配置文件basic-udp-server.conf,该配方来自第二章,客户端-服务器仅 IP 网络。对于客户端,保留上一节中的客户端配置文件。
如何操作...
-
将以下行追加到服务器配置文件
basic-udp-server.conf中:script-security 2 cd /etc/openvpn/cookbook up example5-6-script.sh route-up example5-6-script.sh down example5-6-script.sh client-connect example5-7-script.sh client-disconnect example5-6-script.sh learn-address example5-6-script.sh tls-verify example5-6-script.sh auth-user-pass-verify example5-6-script.sh via-env -
将其保存为
example5-6-server.conf。 -
创建以下脚本:
#!/bin/bash exec >> /tmp/example5-6.log 2>&1 date +"%H:%M:%S: START $script_type script ===" echo "argv = $0 $@" echo "user = `id -un`/`id -gn`" date +"%H:%M:%S: END $script_type script ===" -
将其保存为
example5-6-script.sh。 -
确保脚本可执行,然后启动服务器:
[root@server]# chmod 755 example5-6-script.sh [root@server]# openvpn --config example5-6-server.conf -
接下来,启动客户端:
[root@client]# openvpn --config example5-5-client.confAuth 用户名和密码可以任意选择,因为它们并不会被使用。
-
成功连接到服务器后,断开客户端连接并等待几分钟,直到服务器识别出客户端已断开连接。现在,也停止 OpenVPN 服务器。
日志文件将被创建在
/tmp/example5-6.log中,以下为部分内容:13:34:45: START up script === 13:34:45: START route-up script === 13:36:26: START tls-verify script === 18:36:26: START tls-verify script === 18:36:27: START user-pass-verify script === 18:36:27: START client-connect script === 18:36:27: START learn-address script === argv = example5-6-script.sh add 10.200.0.2 client1 18:37:14: START client-disconnect script === 18:37:20: START learn-address script === argv = example5-6-script.sh delete 10.200.0.2 18:37:20: START down script ===
工作原理...
OpenVPN 内置了许多脚本钩子。当 OpenVPN 服务器启动、客户端连接并断开时,这些脚本会依次执行。顺序(针对 OpenVPN 2.3)如下:
-
以用户
root执行up脚本。 -
以用户
root执行route-up脚本;之后,root 权限会被丢弃,OpenVPN 会切换到配置文件中指定的nobody用户。 -
tls-verify脚本。用于签署客户端证书的 CA 证书会传递进行验证。 -
tls-verify脚本。客户端证书本身会传递。 -
user-pass-verify脚本。 -
client-connect脚本。 -
带有动作
add的learn-address脚本。
此时,客户端已经成功建立 VPN 连接。现在,当客户端断开连接时:
-
client-disconnect脚本 -
带有动作
delete的learn-address脚本
当服务器关闭时:
down命令;请注意,这是以用户nobody身份运行的!
还有更多...
在编写脚本时,牢记脚本执行时间非常重要。OpenVPN 2 的设计非常单体化:所有内容(插件除外,我们将在本章后面讨论插件)都在单个线程中运行。这意味着,在脚本执行时,整个 OpenVPN 服务器会暂时对所有其他客户端不可用:数据包路由停止,其他客户端无法连接或断开连接,甚至管理界面也不会响应。因此,确保所有服务器端脚本快速执行非常重要。
这个设计缺陷已经被识别,但预计在 OpenVPN 3 到来之前不会有重大变化。
脚本安全性和日志记录
OpenVPN 2.0 及以后版本之间的一个主要区别与运行脚本时的安全性有关。在 OpenVPN 2.0 中,所有脚本都是通过 system 调用执行的,并且将整个服务器环境变量集传递给每个脚本。从 OpenVPN 2.1 开始,引入了 script-security 配置指令,默认执行脚本的方式变为 execv 调用,这更加安全。此外,出于安全原因,建议记录脚本输出。通过日志记录脚本输出(包括时间戳),可以更容易地追踪问题和可能的安全事件。从 OpenVPN 2.3 开始,不能再将 system 选项添加到 script-security 配置指令中。
在这个方案中,我们将重点介绍 script-security 配置指令的不同选项,以及简化脚本输出日志记录的方法。
准备工作
在两台计算机上安装 OpenVPN 2.3 或更高版本。确保计算机通过网络连接。使用 第二章 中的第一个方案设置客户端和服务器证书,客户端-服务器 IP-only 网络。对于本方案,服务器计算机运行的是 CentSO 6 Linux 和 OpenVPN 2.3.10,客户端运行的是 Fedora 22 和 OpenVPN 2.3.10。对于服务器,保留来自 服务器端路由 方案的服务器配置文件 basic-udp-server.conf,来自 第二章,客户端-服务器 IP-only 网络。
如何执行...
-
使用来自 使用客户端端 up/down 脚本 配方的配置文件启动 OpenVPN 服务器:
[root@server]# openvpn --config basic-udp-server.conf -
创建客户端配置文件:
client proto udp remote openvpnserver.example.com port 1194 dev tun nobind ca /etc/openvpn/cookbook/ca.crt cert /etc/openvpn/cookbook/client1.crt key /etc/openvpn/cookbook/client1.key tls-auth /etc/openvpn/cookbook/ta.key 1 remote-cert-tls server up "/etc/openvpn/cookbook/example5-7-up.sh arg1 arg2" -
将其保存为
example5-7-client.conf。注意缺少script-security指令。 -
创建
up脚本:#!/bin/bash exec >> /etc/openvpn/cookbook/example5-7.log 2>&1 date +"%H:%M:%S: START $script_type script ===" echo "argv = [$0] [$1] [$2] [$3] [$4]" pstree $PPID date +"%H:%M:%S: END $script_type script ===" -
将其保存为
example5-7-up.sh并确保它是可执行的。 -
启动 OpenVPN 客户端:
[client]$ openvpn --config example5-7-client.conf -
客户端似乎连接成功,直到需要执行
up脚本时:... /etc/openvpn/cookbook/example5-7-up.sh [arguments] ... WARNING: External program may not be called unless '-- script-security 2' or higher is enabled. See --help text or man page for detailed info. ... WARNING: Failed running command (--up/--down): external program fork failed ... Exiting due to fatal error -
当我们重复上述操作,并添加额外的命令行参数
--script-security 2时,客户端可以成功连接:[client]$ openvpn --config example5-7-client.conf \ --script-security 2
/etc/openvpn/cookbook/example5-7.log 日志文件现在显示以下内容:
05:25:33: START up script ===
argv = [/etc/openvpn/cookbook/example5-7-up.sh] [argument1] [argument2] [tun0] [1500]
openvpn---example5-7-up.s---pstree
05:25:33: END up script ===
如果我们使用 --script-security 3 重复此操作,我们将得到类似的输出。
它是如何工作的...
为了在客户端或服务器上执行脚本,必须指定指令 script-security 2(或 3);否则,OpenVPN 2.1 或更高版本将拒绝启动。可以为 script-security 指令指定以下参数:
-
0:此参数指定不允许调用外部程序。这意味着除非在某些特定情况下在 Microsoft Windows 上,否则 OpenVPN 无法成功启动。 -
1:此参数指定只能调用内置的外部程序(例如在 Linux 上的/sbin/ifconfig和/sbin/ip,以及在 Windows 上的netsh.exe和route.exe)。 -
2:此参数指定可以调用内置程序和脚本。 -
3:这与2相同,但现在可以通过环境变量将密码传递给脚本。
还有更多内容...
在 Linux/NetBSD/Mac OS 和 Windows 上运行脚本之间存在细微差异。在 Windows 上,默认使用系统调用 CreateProcess。这使得无法将额外的参数传递给某些脚本,例如 up 脚本,因为 up 指令后的整个文本(用引号括起来的部分)被视为可执行文件或脚本的名称。
脚本编写与 IPv6
现在 IPv6 地址更为普及,展示如何将 IPv6 地址从服务器传递到客户端脚本是很有帮助的。基本上,所有适用于 IPv4 地址的环境变量,对于 IPv6 也同样适用,只需在环境变量后附加或插入 _ipv6 即可。在本例中,我们将向您展示如何处理这些环境变量。
准备就绪
在两台计算机上安装 OpenVPN 2.3 或更高版本。确保计算机已通过网络连接。使用第二章中的第一个配方设置客户端和服务器证书,客户端-服务器 IP-only 网络。在这个配方中,服务器计算机运行 CentOS 6 Linux 和 OpenVPN 2.3.10,而客户端运行 Fedora 22 和 OpenVPN 2.3.10。对于服务器,保留服务器端路由配方中的服务器配置文件basic-udp-server.conf,该配方来自第二章,客户端-服务器 IP-only 网络。
如何操作…
-
将两行添加到服务器配置文件
basic-udp-server.conf中:push "route-ipv6 2001:610:120::111:0:1/96" push "route-ipv6 2001:610:120::222:0:1/96" -
将其保存为
example5-8-server.conf。 -
启动服务器:
[root@server]# openvpn --config example5-8-server.conf -
接下来,创建客户端配置文件:
client proto udp remote openvpnserver.example.com port 1194 dev tun nobind ca /etc/openvpn/cookbook/ca.crt cert /etc/openvpn/cookbook/client1.crt key /etc/openvpn/cookbook/client1.key tls-auth /etc/openvpn/cookbook/ta.key 1 remote-cert-tls server script-security 2 up "/etc/openvpn/cookbook/example5-8.sh" route-up "/etc/openvpn/cookbook/example5-8.sh" -
将其保存为
example5-8-client.conf。 -
创建以下脚本:
#!/bin/bash exec >> /tmp/example5-10.log 2>&1 date +"%H:%M:%S: START $script_type script ===" export | grep ipv6 date +"%H:%M:%S: END $script_type script ===" -
将其保存为
example5-8-script.sh。 -
确保
example5-8.sh脚本是可执行的,然后启动客户端:[root@client]# chmod 755 example5-8.sh [root@client]# openvpn --config example5-8-client.conf -
客户端连接后,检查客户端日志文件
/tmp/example5-8.log:16:19:58: START up script === declare -x ifconfig_ipv6_local="2001:610:120::200:0:1001" declare -x ifconfig_ipv6_netbits="112" declare -x ifconfig_ipv6_remote="2001:610:120::200:0:2" declare -x route_ipv6_gateway_1="2001:610:120::200:0:2" declare -x route_ipv6_gateway_2="2001:610:120::200:0:2" declare -x route_ipv6_network_1="2001:610:120::111:0:1/96" declare -x route_ipv6_network_2="2001:610:120::222:0:1/96" 16:19:58: END up script === 16:19:58: START route-up script === declare -x ifconfig_ipv6_local="2001:610:120::200:0:1001" declare -x ifconfig_ipv6_netbits="112" declare -x ifconfig_ipv6_remote="2001:610:120::200:0:2" declare -x route_ipv6_gateway_1="2001:610:120::200:0:2" declare -x route_ipv6_gateway_2="2001:610:120::200:0:2" declare -x route_ipv6_network_1="2001:610:120::111:0:1/96" declare -x route_ipv6_network_2="2001:610:120::222:0:1/96" 16:19:58: END route-up script ===
它是如何工作的…
OpenVPN 服务器为客户端分配一个 IPv6 地址,并通过push "route-ipv6 ..." 指令将两个 IPv6 路由推送到客户端。客户端接收这些指令并将其传递给up和route-up脚本。这些脚本只显示包含ipv6的环境变量,这可以很好地概述可用于脚本和插件的 IPv6 设置。
还有更多…
当传递包含服务器自身 IPv6 地址的 IPv6 路由给客户端时要小心——这些路由可能会优先于现有的到服务器的路由,导致 VPN 连接停滞。
使用 down-root 插件
OpenVPN 支持插件架构,外部插件可以用来扩展 OpenVPN 的功能。插件是遵循 OpenVPN 插件 API 的特殊模块或库。其中一个插件是down-root插件,它仅在 Linux 上可用。这个插件允许用户在 OpenVPN 关闭时以root用户身份运行指定的命令。通常,OpenVPN 进程会出于安全原因丢弃 root 权限(如果使用了--user指令)。虽然这是一个很好的安全措施,但它使得撤销某些up脚本作为root用户插件执行的操作变得困难。为此,开发了down-root插件。本配方将演示如何使用down-root插件删除由up脚本创建的文件。
准备工作
使用第二章中的设置公钥和私钥配方设置客户端和服务器证书,客户端-服务器 IP-only 网络。在这个配方中,服务器计算机运行 CentOS 6 Linux 和 OpenVPN 2.3.10。不需要客户端计算机。
如何操作…
-
创建服务器配置文件:
proto udp port 1194 dev tun server 10.200.0.0 255.255.255.0 ca /etc/openvpn/cookbook/ca.crt cert /etc/openvpn/cookbook/server.crt key /etc/openvpn/cookbook/server.key dh /etc/openvpn/cookbook/dh2048.pem tls-auth /etc/openvpn/cookbook/ta.key 0 persist-key persist-tun keepalive 10 60 topology subnet user nobody group nobody # nogroup on some distros daemon log-append /var/log/openvpn.log script-security 2 cd /etc/openvpn/cookbook up "example5-9.sh" plugin /usr/lib64/openvpn/plugin/lib/openvpn-down-root.so "./example5-9.sh --down" suppress-timestamps verb 5 -
将其保存为
example5-9-server.conf。 -
接下来,创建
up脚本,我们也将使用它来实现down-root插件:#!/bin/sh if [ "$script_type" = "up" ] then touch /tmp/example5-9.tempfile fi if [ "$1" = "--down" ] then rm /tmp/example5-9.tempfile fi -
将其保存为
example5-9.sh并确保它是可执行的。 -
启动 OpenVPN 服务器:
[root@server]# openvpn --config example5-9-server.conf服务器日志文件现在会显示以下内容:
PLUGIN_CALL: POST /usr/lib64/openvpn/plugin/lib/openvpn-down- root.so/PLUGIN_UP status=0 example5-9.sh tun0 1500 1541 10.200.0.1 10.200.0.2 init这表示插件已启动。
up脚本执行后没有出现错误代码,说明其成功运行。 -
验证服务器上是否创建了
/tmp/example5-9.tempfile文件。 -
接下来,停止服务器。服务器日志现在会显示以下内容:
PLUGIN_CALL: POST /usr/lib64/openvpn/plugin/lib/openvpn-down- root.so/PLUGIN_DOWN status=0 PLUGIN_CLOSE: /usr/lib64/openvpn/plugin/lib/openvpn-down- root.so -
验证
/tmp/example5-9.tempfile文件是否已被删除。
它是如何工作的...
down-root 插件在系统启动时注册,当 OpenVPN 服务器进程仍然以 root 权限运行时。插件将在单独的线程中生成,这意味着当主 OpenVPN 进程丧失其 root 权限时,插件仍然可以完全访问 root 权限。当 OpenVPN 关闭时,将调用插件,它会删除服务器启动时由 root 插件创建的文件。
这里是服务器日志文件中的一个有趣部分:
/sbin/ifconfig tun0 0.0.0.0
SIOCSIFADDR: Permission denied
SIOCSIFFLAGS: Permission denied
Linux ip addr del failed: external program exited with error status: 255
PLUGIN_CALL: POST /usr/lib64/openvpn/plugin/lib/openvpn-down-root.so/PLUGIN_DOWN status=0
PLUGIN_CLOSE: /usr/lib64/openvpn/plugin/lib/openvpn-down-root.so
这表明 OpenVPN 进程确实无法执行命令 /sbin/ifconfig tun0 0.0.0.0,证明 root 权限已成功被移除。然后插件被调用,该插件具有 root 权限,因此它能够删除 /tmp 中由 root 用户创建的文件。
注意,插件运行的脚本必须指定一个以 ./ 开头的路径。如果在 Linux 或 Mac OS 上没有指定前导的 ./,则 OpenVPN 将无法找到插件需要运行的脚本,因为当前目录(.)通常不在 PATH 环境变量中。
还要注意,up 脚本会设置 script_type 环境变量,但插件则不然。为了解决这个问题,添加了一个额外的参数,以便同一个脚本可以同时用作 up 和 down-root 脚本。
还有更多...
插件支持 Linux、Net/FreeBSD 和 Windows 操作系统。以下脚本回调可以通过插件进行拦截:
-
up -
down -
route-up -
ipchange -
tls-verify -
auth-user-pass-verify -
client-connect -
client-disconnect -
learn-address
另见
- 下一个食谱,使用 PAM 身份验证插件,它解释了如何使用 OpenVPN 插件来验证远程 VPN 客户端。
使用 PAM 身份验证插件
对于 OpenVPN,一个非常有用的插件是使用 Linux/UNIX PAM 认证系统验证用户名的插件。PAM代表可插拔认证模块,它是一个非常模块化的系统,允许用户访问系统资源。大多数现代 Linux 和 UNIX 变种都使用它,提供一个非常灵活和可扩展的用户认证和授权系统。在本食谱中,我们将使用 PAM 认证插件来替代auth-user-pass-verify脚本,以根据系统 PAM 配置验证远程用户的凭证。
准备工作
在两台计算机上安装 OpenVPN 2.3 或更高版本。确保计算机通过网络连接。使用第二章中的第一个食谱设置客户端和服务器证书,仅客户端-服务器 IP 网络。在本食谱中,服务器计算机运行 CentOS 6 Linux 和 OpenVPN 2.3.11,而客户端运行 Fedora 22 Linux 和 OpenVPN 2.3.11。对于客户端,准备好使用使用 auth-user-pass-verify 脚本食谱中的example5-5-client.conf客户端配置文件。
如何操作...
-
创建服务器配置文件:
proto udp port 1194 dev tun server 10.200.0.0 255.255.255.0 ca /etc/openvpn/cookbook/ca.crt cert /etc/openvpn/cookbook/server.crt key /etc/openvpn/cookbook/server.key dh /etc/openvpn/cookbook/dh2048.pem tls-auth /etc/openvpn/cookbook/ta.key 0 persist-key persist-tun keepalive 10 60 topology subnet user nobody group nobody # nogroup on some distros daemon log-append /var/log/openvpn.log verb 5 suppress-timestamps plugin /usr/lib64/openvpn/plugins/openvpn-plugin-auth-pam.so "login login USERNAME password PASSWORD"请注意,服务器配置文件的最后一行是单行的。将其保存为:
example5-10-server.conf。 -
启动 OpenVPN 服务器:
[root@server]# openvpn --config example5-10-server.conf服务器日志文件现在将显示:
AUTH-PAM: BACKGROUND: INIT service='login' PLUGIN_INIT: POST /usr/lib64/openvpn/plugins/openvpn-plugin- auth-pam.so '/usr/lib64/openvpn/plugins/openvpn-plugin-auth- pam.so] [login] [login] [USERNAME] [password] [PASSWORD]' intercepted=PLUGIN_AUTH_USER_PASS_VERIFY这表示 PAM 插件已在后台成功初始化。
-
启动 OpenVPN 客户端。OpenVPN 首先会提示输入认证用户名和密码:
... OpenVPN 2.3.11 x86_64-redhat-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [MH] [IPv6] built on May 10 2016 ... library versions: OpenSSL 1.0.1e-fips 11 Feb 2013, LZO 2.08 Enter Auth Username: ******** Enter Auth Password: ********
在本食谱中使用的服务器上,创建了一个特殊的用户cookbook。输入用户名和密码后,成功建立与服务器的连接。OpenVPN 服务器日志显示如下:
AUTH-PAM: BACKGROUND: received command code: 0
AUTH-PAM: BACKGROUND: USER: cookbook
AUTH-PAM: BACKGROUND: my_conv[0] query='login:' style=2
AUTH-PAM: BACKGROUND: name match found, query/match-string ['login:', 'login'] = 'USERNAME'
AUTH-PAM: BACKGROUND: my_conv[0] query='Password: ' style=1
AUTH-PAM: BACKGROUND: name match found, query/match-string ['Password: ', 'password'] = 'PASSWORD'
... 192.168.3.22:50887 PLUGIN_CALL: POST /usr/lib64/openvpn/plugins/openvpn-plugin-auth-pam.so/PLUGIN_AUTH_USER_PASS_VERIFY status=0
... 192.168.3.22:50887 TLS: Username/Password authentication succeeded for username 'cookbook'
这表明用户已经通过 PAM 成功认证。
它是如何工作的...
PAM 认证插件会拦截auth-user-pass-verify回调。当 OpenVPN 客户端连接并传递用户名和密码时,插件会被唤醒。它通过查看login模块(这是openvpn-auth-pam.so文件的第一个参数)来查询 PAM 子系统。其他参数被auth-pam插件用来确定 PAM 子系统会提供哪些输入:
login USERNAME password PASSWORD
PAM login子系统会通过显示login prompt来请求用户名,并通过显示password提示来请求密码。auth-pam插件使用这些信息来知道在哪里填写用户名(USERNAME)和密码(PASSWORD)。
用户在通过 PAM 子系统成功认证后,连接被建立。
还有更多...
也可以使用auth-user-pass-verify脚本通过查询 PAM 子系统来验证用户。使用 PAM 插件有两个主要优点:
-
完全不需要使用
script-security指令。 -
插件方法要更快且具有更好的可扩展性。当多个用户同时尝试连接到 OpenVPN 服务器时,使用
auth-user-pass-verify脚本会极大地影响 VPN 性能,因为每个用户连接时都需要启动一个单独的进程,在此过程中会安装 OpenVPN 的主线程。
另请参阅
- 前面的食谱,使用 down-root 插件,解释了如何使用 OpenVPN 插件的基本方法。