OpenVPN是一款优秀用于创建虚拟私人网络的软件,但是由于其涉及了服务器证书、TLS密钥、防火墙等一堆衍生概念,因此设置显得比较复杂。本文化繁为简,仅以 “能连通” 这个最低要求,完整地展示了一遍OpenVPN的安装调试过程。万事开头难,在实现了连通的基础上再来探索更多的功能是一种比较好的学习路径。
在安装openvpn的时候之前,大概说下它的结构,整个安装流程涉及以下4个部分:
Server / Client 服务器端程序
Easyrsa 证书生成程序
Server端配置文件
Client 端配置文件

观察上图,其实OpenVPN的服务器端和客户端是合二为一的,并没有采用独立的服务器端程序或者客户端程序来区分其角色,它是通过配置文件来实现功能差异的。因此无论服务器端还是客户端,启动程序都相同,Linux下为openvpn,在windows下则为openvpn.exe。
Easyrsa是一个证书生成器,它是一个独立程序,它可以为OpenVPN生成各种所需要的证书和密钥,所谓的证书就是一种绑定了额外信息的公钥,密钥则可以理解为私钥。这里不做深入解释,有兴趣的同学可以搜索一下非对称加密的相关知识。注意:其中一个tls密钥 ta.key 并非由Easyrsa 而是由OpenVPN主程序生成的。
至于Server 服务器端配置文件以及 Client 客户端配置文件,则由我们手动用记事本输入生成。
目录创建
与其他软件安装不同,OpenVPN的安装过程中将要产生许多临时文件,因此我们最好专门创建一个目录来存放他们:
mkdir -p ~/ovpn
mkdir -p ~/ovpn/keys
软件安装
一般来说,我们的OpenVPN服务器安装在Linux上,而客户端则五花八门,可能是Windows PC或者iOS、安卓等系统。现在先说服务器端安装,以Centos为例:
安装OpenVPN:
dnf -y install openvpn
安装Easyrsa:
cd ~/ovpn
wget https://github.com/OpenVPN/easy-rsa/releases/download/v3.1.0/EasyRSA-3.1.0.tgz
tar xvf EasyRSA-3.1.0.tgz
mv EasyRSA-3.1.0 easyrsa
cd easyrsa
客户端方面,OpenVPN官网有两个版本,可以理解为完整版和精简版,完整版下载地址:
https://openvpn.net/community-downloads/
以上链接包含了所有OpenVPN功能,其中包含了Server和Client。
此外,官网还有一个称为 OpenVPN Connect 的软件,它是一个精简版本,只能实现Client (客户端)功能,下载地址是:
https://openvpn.net/vpn-client/
最后,还有一个类似精简版的第三方程序叫做Pritunl,下载地址是:
https://github.com/pritunl/pritunl-client-electron/releases
PS: 本文以官网完整版for windows 64bit为例。
根证书生成:
cd ~/ovpn/easyrsa
./easyrsa init-pki [初始化,程序将自动创建pki目录]
现在,编辑easyrsa的配置文件 vars:
nano pki/vars
将整个文件清空,只留下两行,并存盘退出:
set_var EASYRSA_ALGO "ec"
set_var EASYRSA_DIGEST "sha512"
输入以下命令,生成根证书:
./easyrsa build-ca
牢记PEM pass phrase,这个就是根证书密码,后面好几个步骤需要用到它
服务器证书生成:
./easyrsa gen-req server nopass
Common Name直接回车使用默认名称:server
签署服务器证书:
./easyrsa sign-req server server
输入刚才设置的根证书密码
客户端证书生成:
./easyrsa gen-req client1 nopass
Common Name直接回车使用默认名称:client1
./easyrsa sign-req client client1
输入刚才设置的根证书密码
tls密钥生成:
openvpn --genkey secret ta.key
这个密钥由openvpn主程序生成,起作用是用密钥取代密码输入
至此,我们就完成了全部所需证书文件的生成,下面将这些零散的文件集中拷贝到 keys 目录中去:
cp ~/ovpn/easyrsa/pki/ca.crt ~/ovpn/keys
cp ~/ovpn/easyrsa/ta.key ~/ovpn/keys
cp ~/ovpn/easyrsa/pki/private/server.key ~/ovpn/keys
cp ~/ovpn/easyrsa/pki/issued/server.crt ~/ovpn/keys
cp ~/ovpn/easyrsa/pki/private/client1.key ~/ovpn/keys
cp ~/ovpn/easyrsa/pki/issued/client1.crt ~/ovpn/keys
编辑Server端配置文件:
nano ~/ovpn/keys/server.conf
port 1194
proto tcp
dev tun
ca ca.crt
cert server.crt
key server.key
dh none
tls-crypt ta.key
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 208.67.222.222"
push "dhcp-option DNS 208.67.220.220"
cipher AES-256-GCM
auth SHA256
keepalive 10 120
verb 3
explicit-exit-notify 0
这个文件的原始版本内容很多,不过里面注释部分占了90%,有兴趣的同学可以通过这个路径找到默认的源文件:
/usr/share/doc/openvpn/sample/sample-config-files/server.conf
我们在这里不做过多解释,大致都是一些常规配置,可能让人费解的是这两行:
push “dhcp-option DNS 208.67.222.222”
push “dhcp-option DNS 208.67.220.220”
意思是说让客户端使用这两个由opendns.com提供的DNS服务器。
编辑Client端配置文件:
nano ~/ovpn/keys/client1.ovpn
client
dev tun
proto tcp
remote 123.123.123.123 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca ca.crt
cert client1.crt
key client1.key
remote-cert-tls server
tls-crypt ta.key 1
cipher AES-256-GCM
auth SHA256
key-direction 1
verb 3
同样的,以上也是精简版本,源文件在这里:
/usr/share/doc/openvpn/sample/sample-config-files/client.conf
其中唯一需要改动的地方是将 123.123.123.123 改为你自己的OpenVPN服务器地址即可。
注意,服务器端与客户端配置文件需要保持一致,例如:
dev tun (如果服务器端是dev tun 而 客户端为dev tap则会报错)
proto tcp (本文以tcp为例,如果此处改为udp,则两边都必须为udp)
tls-crypt (有些教程此处为tls-auth也是可以的,但两边注意同步)
在服务器上输入这条命令开启OpenVPN Server:
openvpn --config ~/ovpn/keys/server.conf
如果没有问题的话,最后一行将会显示:
Initialization Sequence Completed
如果配置有误,则需要仔细排查看看到底问题出在哪。
回到客户端这边,首先将 ~/ovpn/keys 中的这5个文件通过ftp或者别的什么方式拷贝到客户端PC:
ca.crt
ta.key
client1.crt
client1.key
client1.ovpn
然后将这5个文件拷贝到OpenVPN for Windows的安装文件夹:
C:\Program Files\OpenVPN\config
打开OpenVPN GUI ,鼠标右键单击右下角OpenVPN图标,然后选 ”连接“,则会弹出一个Log窗口,如果最后也是显示 Initialization Sequence Completed 的话,则说明基本问题不大了。
<题外话>
C:\Program Files\OpenVPN\bin 这个文件夹里有一个名为openvpn.exe 的文件,其作用与Linux下的 openvpn命令 一样。因此我们可以绕开图形化界面,直接在cmd终端下使用:
openvpn.exe --config client1.ovpn命令进行连接。
现在我们可以输入 ping 10.8.0.1 看看能否ping通服务器网关,如果能连通则说明客户端到服务器之间已经可以正常通讯了,但是此时客户端还不能正常上网,因为服务器还没有进行NAT设置。
这项工作是在服务器上进行的,一般来说与客户端无关。通常的教程都是以 iptable 为例,笔者对这个老工具不甚了解,因此无法提供相应的命令组合或者脚本,下面提供一个 nftables 脚本,其作用与 iptable 一样:
nano openvpn.nft
输入以下内容:
flush ruleset
table inet filter{
chain input {
# 允许tcp 1194通过
tcp dport 1194 accept
}
chain forward {
type filter hook forward priority 0; policy drop;
ct state related,established accept
iifname tun0 oifname ens3 accept
}
}
table ip nat {
chain postrouting {
type nat hook postrouting priority 100;
# 对10.8.0.0网段的地址进行NAT
iifname tun0 oifname ens3 ip saddr 10.8.0.0/8 masquerade
}
chain prerouting {
type nat hook prerouting priority 0;
}
}
然后用这条命令加载即可:
nft -f openvpn.nft
这个脚本需要修改两个地方都很简单:
tun0 为OpenVPN创建的虚拟网卡名称,将它改成你自己的即可。
ens3 为本机的真实网卡名称,改为你自己的即可。
接下来,别忘了允许服务器转发:
nano /etc/sysctl.conf
将 net.ipv4.ip_forward 修改为1,存盘退出。
现在,客户端就可以正常上网了。至此OpenVPN的基础设置就完成了,接下来再来完善一些细节。
将服务器所需的所有文件拷贝到OpenVPN默认目录:
cp ~/ovpn/keys/ca.crt /etc/openvpn/server
cp ~/ovpn/keys/ta.key /etc/openvpn/server
cp ~/ovpn/keys/server.crt /etc/openvpn/server
cp ~/ovpn/keys/server.key /etc/openvpn/server
cp ~/ovpn/keys/server.conf /etc/openvpn/server
修改openvpn-server服务文件:
nano /usr/lib/systemd/system/openvpn-server@.service
ExecStart=/usr/sbin/openvpn --config server.conf
ExecStop=killall openvpn
修改ExecStart 和 ExecStop 启动和关闭命令即可,存盘退出后,再执行:
rm -rf /etc/systemd/system/openvpn-server@server.service.d
因为这个地方会 Override 加载一些没有用的内容而导致加载失败,而这些功能(add-bridge等)都不是我们这个极简教程所需要的,因此将之删除,有兴趣的同学可以在备份好源文件的前提下继续深入研究。现在,重新加载OpenVPN服务:
systemctl daemon-reload
systemctl start openvpn-server@server.service
systemctl enable openvpn-server@server.service
如果没有任何报错,则说明服务设置成功,下次开机系统将会自动启动OpenVPN。
当然,仅仅自启动OpenVPN是不行的,还需要自动加载我们的防火墙脚本:
nano /etc/nftables.conf
这是 nftables 的默认启动脚本,将我们上面的nft防火墙脚本复制粘贴进去然后存盘即可。
另外,确保nftables是自启动的:
systemctl enable nftables
尽管现在我们其实已经可以正常使用OpenVPN了,但客户端需要以下5个文件来支持:
这对单个客户端而言还不算麻烦,但是客户端一多,分发和管理的时候就很费事了,下面我们将这5个文件整合为1个以方便分发(PS: 此步骤并非必须,仅供参考)
切换到Windows 的 C:\Program Files\OpenVPN\config 文件夹
用记事本打开 client1.ovpn
用记事本打开 ca.crt
全选 ca.crt 文件内容,选 ”复制“
切换到 client1.ovpn 在文件末尾添加:
<ca>
在此处粘贴ca.crt的内容
</ca>
依葫芦画瓢,依次打开剩下的4个文件,然后将其粘贴到client1.ovpn里即可,对应的标签分别是:
<cert> client1.crt的内容 </cert>
<key> client1.key的内容 </key>
<tls-crypt> ta.key的内容 </tls-crypt>
至此client1.ovpn 里相当于加塞了4个文件的内容,现在将这4个文件删除(先备份好的前提下),只留下client1.ovpn ,重新打开 OpenVPN GUI 一样可以正常连接,功能不受影响。显然,这在分发的时候就简单清爽多了。
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass
尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub
我正在使用puppet为ruby程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这
我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击
我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是
我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search
我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m
我想设置一个默认日期,例如实际日期,我该如何设置?还有如何在组合框中设置默认值顺便问一下,date_field_tag和date_field之间有什么区别? 最佳答案 试试这个:将默认日期作为第二个参数传递。youcorrectlysetthedefaultvalueofcomboboxasshowninyourquestion. 关于ruby-on-rails-date_field_tag,如何设置默认日期?[rails上的ruby],我们在StackOverflow上找到一个类似的问
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题: