openvpn学习笔记

06/09/2015

什么是openvpn
OpenVPN是一个功能齐全的SSL VPN,它使用SSL/TLS协议实现了OSI模型第二层或第三层的安全网络扩展。
可以通过应用于VPN虚拟接口的防火墙规则为指定用户或用户组设置访问控制策略。
OpenVPN不是一个Web应用程序代理,也不需要通过Web浏览器来进行操作。

注意事项:
服务器和客户端的时钟应该大致同步,否则证书可能无法正常工作。
帮助页面http://openvpn.net/man.html
如果你的服务器计算机拥有多个处理器,每台计算机运行多个OpenVPN后台进程将有利于提高性能表现。
SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层对网络连接进行加密。
安全传输层协议(TLS)用于在两个通信应用程序之间提供保密性和数据完整性。该协议由两层组成: TLS 记录协议(TLS Record)和 TLS 握手协议(TLS Handshake)。
HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code),HMAC运算利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。

认证方式
证书 智能卡 用户名/密码

openvpn下载:http://www.softown.cn/soft/openvpn

安装openvpn
linux安装
赖软件包:
openssl (一SSL协议及相关内容的开源实现)
lzo (无损压缩算法)
pam (身份验证模块)
yum -y install openvpn
cp /usr/share/doc/openvpn-2.3.6/sample/sample-config-files/server.conf /etc/openvpn/

windows安装
必须具备管理员权限才能够安装运行OpenVPN(该限制是Windows自身造成的,而不是OpenVPN)

选择基于路由还是桥接的VPN
(路由模式具有更强大的对指定客户端的访问权限进行控制的能力)
除一下情况,否则都用路由模式
VPN需要处理非IP协议,例如IPX。
在VPN网络中运行的应用程序需要用到网络广播(network broadcasts),例如:局域网游戏。
你想要在没有Samba或WINS服务器的情况下,能够通过VPN浏览Windows文件共享。

设置私有子网
避免IP地址冲突或子网冲突方案:
VPN中不同的网络场所使用相同的局域网子网编号所产生的冲突。
远程访问连接自身使用的私有子网与VPN的私有子网发生冲突。
(简而言之,处于不同局域网的客户端和客户端之间,它们自身所在的局域网IP段不要发生冲突;客户端自身所在的局域网IP段也不要和VPN使用的局域网IP段发生冲突)

创建你自己的CA,并为服务器和客户端生成证书和密钥(缺一张密钥传递过程中的图)
配置的第一步就是建立一个PKI(公钥基础设施)PKI包括:
一个单独的证书(即我们常说的公钥)以及供服务器和每个客户端使用的私钥。
一个主CA证书和密钥,该密钥用于签名标识服务器和客户端的每个证书。
OpenVPN支持基于证书的双向验证,这意味着在连接双方在建立相互信任之前,客户端必须鉴定服务器证书的有效性,服务器也必须鉴定客户端证书的有效性。
服务器和客户端在鉴定其他证书时,首先就是验证对方的证书是否由主CA签名标识,然后测试该证书的头信息,例如:证书Common Name或者证书类型(客户端或服务器)。
从VPN角度来看,该安全模型具备如下值得称道的功能特性:
服务器端只需要它自己的证书/密钥— —不需要知道可能与之建立连接的每个客户端单独的证书。

服务器端只接收那些由主CA签名的证书(我们将会在下面生成这些证书)。并且,由于服务器端能够在不访问自己的CA私钥的情况下,就能够进行签名校验,这样我们就可以将CA私钥(整个PKI中最敏感的密钥)放在一台完全不同的计算机上,哪怕该计算机没有网络连接。

服务器就可以基于证书中内嵌的字段来控制指定客户端的访问权限,例如Common Name。
关键文件

现在我们可以在keys子目录中找到我们新生成的密钥和证书。下面是相关文件的一个说明:
文件名 谁需要 作用 是否需保密
ca.crt 服务器 + 所有客户端 根CA证书 NO
ca.key 密钥签名机 根CA密钥 YES
dh{n}.pem 服务器 迪菲·赫尔曼参数 NO
server.crt 服务器 服务器证书 NO
server.key 服务器 服务器密钥 YES
client1.crt client1 Client1证书 NO
client1.key client1 Client1密钥 YES
client2.crt client2 Client2证书 NO
client2.key client2 Client2密钥 YES
client3.crt client3 Client3证书 NO
client3.key client3 Client3密钥 YES
最后一步就是将所有文件复制到需要它们的计算机上,注意使用安全通道来复制需要保密的文件。
等等,现在你可能会说”没有现有的安全通道,就不可能创建PKI”?
显然,答案为”是”。在上面的示例中,为了简单起见,我们将所有的私钥都生成在了相同的地方。稍加努力,我们也可以不这样做。举个例子,为了不在服务器端生成客户端的证书和私钥,我们应该让客户端在本地生成自己的私钥,并向密钥签名机提交一个证书签名请求(Certificate Signing Request,简称:CSR)。反过来看,密钥签名机就应该处理该证书签名请求,并返回一个签名后的证书给客户端。在不需要一个保密的.key文件离开生成它的计算机的前提下,即可完成该才做。

启动VPN并进行初步连通性测试
请确保OpenVPN服务器能够正常连接网络。这意味着:
开启防火墙上的UDP 1194端口(或者你配置的其他端口)。
或者,创建一个端口转发规则,将防火墙/网关的UDP 1194端口转发到运行OpenVPN服务器的计算机上。
确保开启内核转发(访问内网其他服务器)
为了简化故障排除,使用命令行来初始化启动OpenVPN服务器
openvpn 服务器配置文件
openvpn 客户端配置文件
在客户端使用ping测试
如果你正在使用桥接模式(例如:在服务器配置文件中设置dev tap),请尝试ping一个服务器端的以太网子网中的IP地址。
如果能够ping成功,那么恭喜你,你现在具备VPN功能了。
如果出现错误,请查看FAQ。
http://openvpn.net/index.php/open-source/faq.html(需要翻墙)

系统启动时自动运行
Linux系统
如果你在Linux上通过RPM或DEB软件包来安装OpenVPN,安装程序将创建一个initscript。执行该脚本,initscript将会扫描/etc/openvpn目录下.conf格式的配置文件,如果能够找到,将会为每个文件分别启动一个独立的OpenVPN后台进程。

Windows系统
在服务中将”启动类型”设为”自动”
启动后,OpenVPN服务包装器将会扫描OpenVPN安装路径/config文件夹下.ovpn格式的配置文件,然后为每个文件启动一个单独的OpenVPN进程。

控制运行中的OpenVPN进程
linux:
OpenVPN接受如下几种信号:
SIGUSR1 — 有条件的重新启动,设计用于没有root权限的重启
SIGHUP — 硬重启
SIGUSR2 — 输出连接统计信息到日志文件或syslog中
SIGTERM, SIGINT — 退出
使用writepid指令将OpenVPN的后台进程PID写入到一个文件中,这样你就知道该向哪里发送信号(如果你是以initscript方式启动OpenVPN,该脚本可能已经通过openvpn命令行中的指令–writepid达到了该目的)。

windows:
在Windows中,当OpenVPN以服务方式启动时,控制它的唯一方式是:
通过服务控制管理器(控制面板->管理工具->服务),其中提供了启动/终止操作。
通过管理接口(详情参考下面)。

无需重启服务器进程,配置文件动态更新
修改使用中的OpenVPN配置:
client-config-dir — 该指令设置一个客户端配置文件夹,OpenVPN服务器将会在每个外部连接到来时扫描该目录中的文件,该目录中的文件可以随时更新,而无需重启服务器。请注意,该目录中发生的更改只对新的连接起作用,不包括之前已经存在的连接。
crl-verify — 该指令指定一个证书撤销列表(CRL)文件,相关描述请参考后面的撤销证书部分。该CRL文件能够在运行中被修改,并且修改可以直接对新的连接或那些正在重新建立SSL/TLS通道的现有连接(默认每小时重新建立一次通道)生效。如果你想杀掉一个证书已经添加到CRL中,但目前已连接的客户端,请使用管理接口(参见下方描述)。
状态信息文件:
默认的server.conf文件有这样一行:
status openvpn-status.log
OpenVPN将每分钟输出一次当前客户端连接列表到文件openvpn-status.log中。
使用管理接口

OpenVPN管理接口可以对正在运行的OpenVPN进程进行大量的控制操作。
你可以通过远程登录管理接口的端口来直接使用管理接口,或者使用连接到管理接口的OpenVPN GUI来间接使用管理接口。
为了启用一个服务器或客户端的管理接口,请在配置文件中添加如下指令:
management localhost 7505
这将告诉OpenVPN专为管理接口客户端监听TCP端口7505(端口7505是随意的选择,你也可以使用其他任何闲置的端口)。
OpenVPN运行后,你可以使用telnet客户端连接管理接口。
telnet localhost 7505

扩大OpenVPN使用范围,包含服务器或客户端子网中的其他计算机
包含基于路由模式的VPN服务器端的多台计算机(dev tun):

假设服务器端所在局域网的网段为10.66.0.0/24,VPN IP地址池使用10.8.0.0/24作为OpenVPN服务器配置文件中server指令的传递参数。
首先,你必须声明,对于VPN客户端而言,10.66.0.0/24网段是可以通过VPN进行访问的。你可以通过在服务器端配置文件中简单地配置如下指令来实现该目的:
push “route 10.66.0.0 255.255.255.0”
下一步,你必须在服务器端的局域网网关创建一个路由,从而将VPN的客户端网段(10.8.0.0/24)路由到OpenVPN服务器(只有OpenVPN服务器和局域网网关不在同一计算机才需要这样做)。
另外,请确保你已经在OpenVPN服务器所在计算机上启用了IP和TUN/TAP转发。
包含基于桥接模式的VPN服务器端的多台计算机(dev tap):

使用以太网桥接的好处之一就是你无需进行任何额外的配置就可以实现该目的。

推送DHCP选项命令到客户端
OpenVPN服务器能够推送诸如DNS、WINS服务器地址等DHCP选项参数到客户端
(Windows客户端能接受被推送来的DHCP选项参数,但非Windows系统的客户端需要使用客户端的up脚本才能接受它们,up脚本能够解析foreign_option_n环境变量列表。请进入手册页面或者OpenVPN用户的邮件列表档案查看非Windows系统的foreign_option_n文档和脚本示例。)

假如你希望正在连接的客户端使用一个内部的DNS服务器10.66.0.4或10.66.0.5
push “dhcp-option DNS 10.66.0.4”
push “dhcp-option DNS 10.66.0.5”

为指定客户端配置规则和访问策略(写的很好,我将整章贴下来)
设想一下,我们创建了一个供企业使用的VPN,我们想要分别为3种不同级别的用户单独设置访问策略:
系统管理员 — 允许访问网络内的所有终端
普通职工 — 只允许访问Samba/email服务器
承包商 — 只允许访问特定的服务器
我们所要采取的基本方法是:(a)给不同级别的用户划分不同的虚拟IP地址范围;(b)通过设置防火墙规则来切断客户端的虚拟IP地址,从而控制对其他终端的访问。
在我们的示例中,假设我们有大量的普通职工,但只有1个系统管理员、2个承包商。我们的IP配置方案将会把所有的普通职工放入一个IP地址池中,然后为系统管理员和承包商分配固定的IP地址。
注意,本示例的前提条件之一就是你有一个运行于OpenVPN服务器所在计算机上的软件防火墙,并具备自定义防火墙规则的能力。在我们的例子中,我们假定防火墙是Linux系统的iptables。
首先,我们根据用户级别创建一个虚拟IP地址映射:
Class Virtual IP Range Allowed LAN Access Common Names
普通职工 10.8.0.0/24 Samba/email服务器为10.66.4.4 [数量众多]
系统管理员 10.8.1.0/24 10.66.4.0/24整个网段 sysadmin1
承包商 10.8.2.0/24 承包商服务器为10.66.4.12 contractor1, contracter2
下一步,我们将上述映射转换到OpenVPN服务器配置中。首先,请确保你已经遵循了上述步骤并将10.66.4.0/24网段分配给了所有的客户端(虽然我们配置允许客户端访问整个10.66.4.0/24网段,不过稍后我们将利用防火墙规则强制添加访问限制来实现上述表格中的访问策略)。
首先,为我们的tun接口定义一个静态的单元编号,以便于我们稍后在防火墙规则中使用它:
dev tun0
在服务器配置文件中,定义普通职工的IP地址池:
server 10.8.0.0255.255.255.0
为系统管理员和承包商的IP范围添加路由:(这里没有看懂)
#管理员的IP范围
route 10.8.1.0 255.255.255.0
#承包商的IP范围
route 10.8.2.0 255.255.255.0
因为我们要为指定的系统管理员和承包商分配固定的IP地址,我们将使用一个客户端配置文件:
client-config-dir ccd
现在,我们在ccd子目录中放置专用的配置文件,为每个非普通职工的VPN客户端定义固定的IP地址。
文件ccd/sysadmin1:
ifconfig-push 10.8.1.110.8.1.2
文件ccd/contractor1:
ifconfig-push 10.8.2.110.8.2.2
文件ccd/contractor2:
ifconfig-push 10.8.2.510.8.2.6
ifconfig-push中的每一对IP地址表示虚拟客户端和服务器的IP端点。它们必须从连续的/30子网网段中获取(这里是/30表示xxx.xxx.xxx.xxx/30,即子网掩码位数为30),以便于与Windows客户端和TAP-Windows驱动兼容。明确地说,每个端点的IP地址对的最后8位字节必须取自下面的集合:
[ 1, 2] [ 5, 6] [ 9, 10] [ 13, 14] [ 17, 18]
[ 21, 22] [ 25, 26] [ 29, 30] [ 33, 34] [ 37, 38]
[ 41, 42] [ 45, 46] [ 49, 50] [ 53, 54] [ 57, 58]
[ 61, 62] [ 65, 66] [ 69, 70] [ 73, 74] [ 77, 78]
[ 81, 82] [ 85, 86] [ 89, 90] [ 93, 94] [ 97, 98]
[101,102] [105,106] [109,110] [113,114] [117,118]
[121,122] [125,126] [129,130] [133,134] [137,138]
[141,142] [145,146] [149,150] [153,154] [157,158]
[161,162] [165,166] [169,170] [173,174] [177,178]
[181,182] [185,186] [189,190] [193,194] [197,198]
[201,202] [205,206] [209,210] [213,214] [217,218]
[221,222] [225,226] [229,230] [233,234] [237,238]
[241,242] [245,246] [249,250] [253,254]
这里我们将完成OpenVPN的配置,最后的一步是添加防火墙规则,从而落实我们的访问策略。在下面的例子中,我们使用Linux系统iptables语法的防火墙规则:
# 普通职工规则
iptables -A FORWARD -i tun0 -s 10.8.0.0/24-d 10.66.4.4-j ACCEPT

# 系统管理员规则
iptables -A FORWARD -i tun0 -s 10.8.1.0/24-d 10.66.4.0/24-j ACCEPT

# 承包商规则
iptables -A FORWARD -i tun0 -s 10.8.2.0/24-d 10.66.4.12-j ACCEPT

使用其他身份验证方法(写的很好,我将整章贴下来)
OpenVPN 2.0及以上版本支持OpenVPN服务器安全地从客户端获取用户名和密码,并以该信息作为客户端身份验证的基础。
为了使用该身份验证方法,请先在客户端配置中添加auth-user-pass指令。这使得OpenVPN客户端直接向用户询问用户名/密码,并通过安全的TLS隧道将其传递到服务器
下一步,配置服务器以使用一个身份验证插件,该插件可以是一个脚本、共享的对象或者DLL文件。在每次客户端尝试连接时,OpenVPN服务器就会调用该插件,并将客户端输入的用户名/密码传递给它。身份验证插件通过返回一个表示失败(1)或成功(0)的值,从而控制OpenVPN是否允许该客户端连接。
使用脚本插件:

通过在服务器端配置文件中添加auth-user-pass-verify指令,我们额可以使用脚本插件。例如:
auth-user-pass-verify auth-pam.pl via-file
我们将使用名为auth-pam.pl的perl脚本来验证正在连接的客户端的用户名/密码。详情请查看手册页面中关于auth-user-pass-verify的相关描述。
auth-pam.pl脚本文件位于OpenVPN源代码发行版的sample-scripts子目录中。在Linux服务器上,它将使用PAM认证模块对用户进行身份验证,从而实现shadow密码、RADIUS(远程用户拨入验证服务)或者LDAP(轻量级目录访问协议)验证。 auth-pam.pl主要用于演示目的。至于现实世界中的PAM认证,请使用下面描述的openvpn-auth-pam共享对象插件。
使用共享对象或DLL插件:

共享对象或DLL插件通常是一个经过编译的C模块,它能够在运行时被OpenVPN服务器加载。例如,如果你正在Linux系统中使用基于RPM的OpenVPN,openvpn-auth-pam插件应该已经创建好了。为了使用该插件,请在服务器端配置文件中添加如下语句:
plugin /usr/share/openvpn/plugin/lib/openvpn-auth-pam.so login
这将告诉OpenVPN服务器使用login PAM模块来校验客户端输入的用户名/密码。
对于现实世界生成环境中的使用,最好使用openvpn-auth-pam插件,因为相对使用auth-pam.pl脚本而言,它具有以下几个优点:
共享对象openvpn-auth-pam插件采用更加安全的拆分权限执行模式。这意味着OpenVPN服务器可以运行在使用user nobody、group nobody和chroot等指令来降低权限的情况下,并且能够进行身份验证,而不依赖于只有root用户才能读取的shadow密码文件。
OpenVPN可以通过虚拟内存将用户名/密码传递给插件,而不是通过一个文件或环境变量,对于服务器计算机而言,这将具有更好的本地安全性。
如果你想了解关于开发自己的OpenVPN插件的更多信息,请查看OpenVPN源代码发行版plugin子目录中的README文件。
在Linux中,为了构建openvpn-auth-pam插件,请转到OpenVPN源代码发行版的plugin/auth-pam目录,并运行make。
使用用户名/密码验证作为客户端验证的唯一形式:

默认情况下,在服务器上使用auth-user-pass-verify或者用户名/密码验证插件将会启用双重身份验证,这使得待验证客户端的客户端证书和用户名/密码验证都必须通过。
我们也可以禁用客户端证书,而强制只使用用户名/密码验证,尽管从安全角度来说,不鼓励这样做。在服务器端:
client-cert-not-required
通常还需要这样设置:
username-as-common-name
这将告诉服务器优先使用用户名,就像它使用那些通过客户端证书认证的客户端的Common Name一样(也就是说,使用username作为Common Name,用法与之前使用Common Name时相同)。
注意,client-cert-not-required并不排除对服务器证书的需要,所以一个客户端连接到使用了client-cert-not-required指令的服务器,可以删除客户端配置文件中的cert和key指令,但不能删除ca指令,因为它对于客户端验证服务器端证书来说是必需的。

路由所有客户端流量(包括Web流量)通过VPN
在服务器配置文件中添加如下指令:
push “redirect-gateway def1”
如果你的VPN安装在无线网络上,并且OpenVPN服务器和客户端均处于同一个无线子网中,请添加local标记:
push “redirect-gateway local def1”
在Linux系统中,你可以使用如下命令将VPN客户端的流量NAT转化到internet:
iptables -t nat -A POSTROUTING -s 10.8.0.0/24-o eth0 -j MASQUERADE
当启用了redirect-gateway指令,OpenVPN客户端将路由所有的DNS查询经过VPN,VPN服务器需要处理掉这些查询。在VPN活动期间,我们可以通过推送DNS服务器地址到正在连接的客户端上来完成该操作,从而代替常规的DNS服务器设置:
push “dhcp-option DNS 10.8.0.1”
注意事项:

重定向所有的网络流量通过VPN并不是一个完全没有问题的命题。这里有一些典型的陷阱需要注意:
多数连接internet的OpenVPN客户端计算机会定期与DHCP服务器进行交互,并更新它们的IP地址租约。redirect-gateway选项命令可能会阻止客户端连接到本地DHCP服务器(因为DHCP信息会被路由通过VPN),从而导致丢失IP地址租约。
关于推送DNS地址到Windows客户端存在一些问题。
客户端的Web浏览性能将会明显降低。

在使用动态IP地址的计算机上运行OpenVPN服务器
使用DDNS做域名映射
如果客户端配置正在使用引用了动态DNS名称的remote指令,默认情况下,当服务器IP地址发生改变时,OpenVPN客户端将会感觉到。通常的事件链为:
(a)OpenVPN客户端无法及时接收来自服务器旧的IP地址的活动消息,触发一次重启
(b)重启导致remote指令中的DNS名称被重新解析,从而让客户端重新连接到位于新的IP地址的服务

通过HTTP代理连接OpenVPN服务器
OpenVPN支持以下列身份认证方式通过HTTP代理进行连接:
无需代理身份认证
基本的代理身份认证
NTLM代理身份认证
首先,HTTP代理的用法要求你必须使用TCP协议作为隧道载体。所以,请在客户端和服务器配置中均添加如下语句:
proto tcp
请确保删除(或注释)配置文件中的所有proto udp指令行。
下一步,在客户端配置文件中添加http-proxy指令(请查看手册页面了解该指令的详细描述信息)。
例如,假设客户端局域网有一台位于192.168.4.1的HTTP代理服务器,并在1080端口监听连接。请在客户端配置中添加如下语句:
http-proxy 192.168.4.11080
假设HTTP代理要求基本的身份认证:
http-proxy 192.168.4.11080 stdin basic
假设HTTP代理要求NTLM身份认证:
http-proxy 192.168.4.11080 stdin ntlm
上面的两个身份认证示例将会导致OpenVPN提示从标准输入界面输入一对用户名/密码。如果你希望将这些用户凭据放入一个文件中来代替上述输入操作,请使用一个文件名来替换语句中的stdin,该文件的第1行应该放置用户名,第2行放置密码。

通过OpenVPN连接到Samba共享服务器
在samba中运行vpn IP 网段连接
以及回程路由

实现具备负载均衡/故障转移功能的配置
客户端配置:
OpenVPN客户端配置可以用于实现负载均衡和故障转移功能的多台服务器。例如:
remote server1.mydomain
remote server2.mydomain
remote server3.mydomain
这将指示OpenVPN客户端按照顺序尝试与server1、server2、server3进行连接。如果现有的连接被中断,OpenVPN客户端将会重新尝试连接最近连接过的服务器;如果连接失败,客户端将会尝试与列表中的下一个服务器进行连接。你也可以指示OpenVPN客户端在启动时随机连接列表中的一个服务器,以便于客户端负载能够均等概率地覆盖服务器池。
remote-random
如果你也希望在DNS解析失败时,让OpenVPN客户端移至列表中的下一个服务器,请添加如下命令:
resolv-retry 60
参数60告诉OpenVPN客户端,在移至下一个服务器之前,尝试解析每个remote DNS名称60秒(意即60秒内都无法解析成功,就移至下一个服务器)。
服务器列表还可以引用运行于同一计算机上的多个OpenVPN服务器进程(每个进程监听不同的端口),例如:
remote smp-server1.mydomain 8000
remote smp-server1.mydomain 8001
remote smp-server2.mydomain 8000
remote smp-server2.mydomain 8001
如果你的服务器计算机拥有多个处理器,每台计算机运行多个OpenVPN后台进程将有利于提高性能表现。
OpenVPN也支持remote指令引用在域名配置中拥有多个A记录的DNS名称。在这种情况下,在每次域名被解析时,OpenVPN客户端将会随机选择一个A记录。
服务器端配置:

除了每个服务器使用不同的虚拟IP地址池之外,为集群中的每个服务器使用相同的配置文件,是在服务器端实现负载均衡/故障转移配置的最简单方法。例如:
server1
server 10.8.0.0255.255.255.0
server2
server 10.8.1.0255.255.255.0
server3
server 10.8.2.0255.255.255.0

增强OpenVPN安全性
一个经常被重复提及的网络安全准则就是:不要过分相信一个单一的安全组件,否则它的失败将导致灾难性的安全漏洞。OpenVPN提供多种机制来添加额外的安全层,以避免这样的结果。
tls-auth:

tls-auth指令为所有的SSL/TLS握手数据包添加一个额外的HMAC签名,以验证数据的完整性。无需进一步处理,没有正确的HMAC签名的任何UDP数据包将会被丢弃。tls-auth HMAC签名提供了上面所说的额外安全级别,而不是通过SSL/TLS来提供。它可以防御:
Dos攻击或者UDP端口淹没攻击。
确定服务器UDP端口监听状态的端口扫描。
SSL/TLS实现的缓冲区溢出漏洞。
与未经认证的计算机进行SSL/TLS握手(虽然这样的握手最终会验证失败,但tls-auth可以更加一点地断开它们)。
除了使用标准的RSA证书/密钥之外,使用tls-auth还需要生成一个共享的密钥:
openvpn –genkey –secret ta.key
该命令将生成一个OpenVPN静态密钥,并将其写入到ta.key文件中。该密钥应该通过已有的安全通道拷贝到服务器和所有的客户端。它应该与RSA的.key和.crt文件放在同一目录。
在服务器配置中,添加:
tls-auth ta.key 0
在客户端配置中,添加:
tls-auth ta.key 1
proto udp:

虽然OpenVPN允许使用TCP或者UDP协议作为VPN的连接载体,但UDP协议能够比TCP提供更好的Dos攻击和端口扫描防护:
proto udp
user/group (仅限于非Windows系统):

OpenVPN已经过非常仔细地设计,以允许在初始化后丢弃掉root权限,该特性可以在Linux/BSD/Solaris系统中一直使用。
对于攻击者而言,没有root权限,运行中的OpenVPN服务器进程就远不是一个诱人的目标。
user nobody
group nobody
非特权模式(仅限于Linux系统):

在Linux系统中,OpenVPN可以完全没有特权地正常运行。虽然配置上会稍稍麻烦一点,但却能带来最佳的安全性。
为了使用这种配置来运行,OpenVPN必须配置为使用iproute接口,这可以通过为configure脚本指定–enable-iproute2参数来完成。你的系统也需要有sudo软件包。
该配置使用Linux自身的能力来更改tun设备的权限,以便于非特权的用户也可以访问它。为了执行iproute,也需要使用sudo,从而使得接口属性和路由表可以被修改。
OpenVPN配置:
重写并覆盖位于/usr/local/sbin/unpriv-ip的以下脚本文件:
#!/bin/sh
sudo /sbin/ip $*
执行visudo,添加如下命令以允许用户”user1″执行/sbin/ip:
user1 ALL=(ALL) NOPASSWD:/sbin/ip
你也可以通过如下命令启用一个用户组:
%users ALL=(ALL) NOPASSWD:/sbin/ip
在你的OpenVPN配置中添加如下语句:
dev tunX/tapX
iproute /usr/local/sbin/unpriv-ip
请注意,你必须选择常量X(一般用数字标记,例如:tunX实际为tun0),并且不能同时指定tun或tap。
使用root添加持久化接口,允许用户或用户组来管理它,下面的命令会创建tunX,并且允许user1和用户组访问它。
openvpn –mktun –dev tunX –type tun –user user1 –group users
在非特权用户的上下文环境中运行OpenVPN。
你可以通过核查脚本文件/usr/local/sbin/unpriv-ip的参数来添加进一步的安全约束。
chroot (仅限于非Windows系统):

chroot指令允许你将OpenVPN后台进程锁定到所谓的chroot jail(chroot监狱)中,除了该指令参数给出的指定目录外,chroot监狱中的进程无法访问主机系统的文件系统的任何部分。例如:
chroot jail
将导致OpenVPN进程在初始化时转到jail子目录,然后将它的根文件系统调整为该目录,进程将无法访问jail和它的子目录树以外的任何文件。从安全角度来说,这很重要,因为即使攻击者能够使用代码插入攻击入侵服务器,攻击也会被锁定在服务器的大部分文件系统之外。
注意事项:由于chroot调整了文件系统(仅从后台进程的角度来看),因此有必要将OpenVPN初始化后可能用到的文件放入jail目录中,例如:
文件crl-verify
或者目录client-config-dir
更大的RSA密钥:

我们可以通过文件easy-rsa/vars中的KEY_SIZE变量来控制RSA密钥的大小,该变量必须在所有密钥生成之前进行设置。当前的默认设置为1024,该值可以合理地提高到2048,这对VPN隧道的性能没有什么负面影响,除了稍稍减缓每个客户端每小时一次的SSL/TLS重新协商握手速度,和大幅减慢使用脚本easy-rsa/build-dh生成迪菲赫尔曼参数的一次性过程之外。
更大的对象密钥:

默认情况下,OpenVPN使用Blowfish — 一种128位的对称加密算法。
OpenVPN自动地支持OpenSSL库支持的任何加密算法,同样支持使用更大密钥长度的加密算法。例如,通过在服务器和客户端配置文件中均添加如下语句来使用AES(Advanced Encryption Standard,高级加密标准)的256位版本:
cipher AES-256-CBC
将根密钥(ca.key)保留在一台没有网络连接的计算机上:

使用X509 PKI(OpenVPN也使用)的安全好处之一就是,根CA密钥(ca.key)不需要放在当前的OpenVPN服务器所在计算机上。在一个高度安全的环境中,你可能想特别指定一台计算机用于密钥签名,让该计算机受到良好的保护,并断开所有的网络。必要时,可以使用软盘来回移动该密钥文件。这些措施使得攻击者想要窃取根密钥变得非常困难(对于密钥签名计算机的物理盗窃而言,则一点都不难)。

撤销证书
撤销一个证书 就是让一个已签名的证书作废,以便其无法再用于身份认证。
示例:

举这样一个例子,我们将撤销证书client2,该证书是我们在前面的操作指南的”生成密钥”部分生成的。
首先,打开shell或命令提示符窗口,转到之前”生成密钥”部分操作过的easy-rsa目录。在Linux/BSD/Unix系统中:
../vars
./revoke-full client2
在Windows系统中:
vars
revoke-full client2
你可以看到类似这样的输出:
Using configuration from /root/openvpn/20/openvpn/tmp/easy-rsa/openssl.cnf
DEBUG[load_index]: unique_subject = “yes”
Revoking Certificate 04.
Data Base Updated
Using configuration from /root/openvpn/20/openvpn/tmp/easy-rsa/openssl.cnf
DEBUG[load_index]: unique_subject = “yes”
client2.crt: /C=KG/ST=NA/O=OpenVPN-TEST/CN=client2/[email protected]
error 23 at 0 depth lookup:certificate revoked
请注意最后一行的”error 23″。那就是你想要看到的,因为它表明已被撤销的证书的证书校验失败。
revoke-full脚本将会在keys子目录中生成一个叫做crl.pem的CRL(证书撤销列表)文件。该文件应该被复制到一个OpenVPN服务器可以访问的目录,然后在服务器配置中启用CRL验证:
crl-verify crl.pem
现在,所有正在连接的客户端的证书会与CRL进行比对校验,任何正匹配都将导致该连接被丢失。
CRL注意事项:

当OpenVPN使用crl-verify选项命令后,任何新的客户端连接或者现有客户端重新建立SSL/TLS连接(默认每小时一次)都将使得CRL文件被重新读取。
如果一个刚刚撤销了证书的客户端早已经连接到服务器,你可以通过一个信号(SIGUSR1或者SIGHUP)来重启服务器,并刷新所有的客户端
你可以远程登录管理接口,明确杀掉服务器上指定的客户端实例对象,而不干扰其他客户端。

虽然OpenVPN服务器和客户端都可以使用crl-verify指令,但通常不必将CRL文件分发到客户端,除非服务器证书已被撤销。

CRL文件无需保密,并且应该设为所有用户可读,以便于OpenVPN进程在没有root权限的情况下能够读取该文件。

如果你正使用chroot指令,请确保在chroot目录放置一份CRL文件的拷贝,因为不像OpenVPN读取的其他大多数文件,CRL将会在执行chroot调用之后被读取,而不是在此之前。

需要撤销证书的一个常见原因是,用户使用密码加密了自己的私钥,然后忘记了密码。通过撤销原来的证书,用户也可以使用原来的Common Name来生成新的证书/密钥对。

关于中间人攻击的重要注意事项(如果客户端没有验证与之连接的服务器端证书)
为了避免可能的中间人攻击(一个经过认证的客户端尝试连接到重复服务器的另一个客户端),请确保客户端执行某种服务器证书验证。当前有5种不同的方式来完成它,已按照优先顺序在下面列出:
[OpenVPN 2.1 及以上版本]使用指定的密钥用法和扩展密钥用法来创建你的服务器证书。RFC3280 确定了应该为TLS连接提供下列属性:
模式 密钥用法 扩展密钥用法
客户端 数字签名 TLS Web客户端认证
密钥协议
数字签名,密钥协议
服务器 数字签名,密钥加密 TLS Web服务器认证
数字签名,密钥协议
你可以使用build-key-server脚本来创建你的服务器证书(详情请查看easy-rsa文档)。通过设置正确的属性,指定证书作为一个服务器端证书。现在,请在你的客户端配置中添加如下语句:
remote-cert-tls server
[OpenVPN 2.0 及以下版本] 使用build-key-server脚本来创建你的服务器证书(详情请查看easy-rsa文档)。通过设置nsCertType=server,指定该证书作为服务器端证书。现在,请在你的客户端配置中添加如下语句:
ns-cert-type server
这将阻止客户端连接任何没有在证书中指定nsCertType=server的服务器,即使该证书已经通过OpenVPN配置文件中的ca文件进行了签名。

在客户端使用tls-remote指令,根据服务器证书的Common Name来判断接受或拒绝该服务器连接。

使用tls-verify脚本或插件,根据服务器证书的嵌入式X509附属条目中的自定义测试来判断接受/拒绝该服务器连接。

使用一个CA给服务器证书签名,使用另一个不同的CA给客户端证书签名。客户端配置的ca指令应该引用为服务器签名的CA文件,而服务器配置的ca指令应该引用为客户端签名的CA文件。