如何部署 OpenVPN:一步一步的指南
引言
在当前的数字时代,保护您的网络安全比以往任何时候都更加重要。
OpenVPN
是一种流行的虚拟私人网络(VPN)
解决方案,它可以帮助保护您的互联网连接,确保数据安全和隐私,当然,也可以用作打通异地网络。
本文将指导您如何从头开始部署OpenVPN
。
什么是OpenVPN
?
OpenVPN
是一个开源的VPN
软件,支持创建安全的点对点或站点对站点连接。
它使用了最先进的加密技术,可在各种操作系统上运行,包括Windows
、macOS
、Linux
以及移动平台。
OpenVPN
部署的先决条件
在开始部署之前,您需要准备以下事项:
- 一台可作为
VPN
服务器的计算机,具有固定的公网IP
地址。 - 一定的网络知识,包括对
IP
地址、DNS
和路由的基本了解。 - 服务器的管理员访问权限。
需求规划
公司需要把内部网络与云上网络(腾讯云)打通,实现网络互通,经研究选择OpenVPN
作为实现方法。
拓扑图如下:
解析一下:
- 内网部署
VPN
客户端,云上有公网IP
的服务器部署VPN
服务端,双方互通。 - 内网配置核心交换机做路由转发,拦截目的地为云上网络的数据,转发到
VPN
客户端,通过iptables
鉴权判断是否允许转发到VPN
服务端,如不允许则丢弃数据包,允许则转发。 - 目的地非云上网络的数据则正常转发,从防火墙出去互联网。
- 目的地为云上网络的数据抵达
VPN
服务端后,由VPN
服务端内部网卡做转发,实现访问云上网络内部资源。
机器规划:
此处说明一下
云上内网网段为 10.5.0.0/16
VPN
内网网段为 10.9.0.0/24
系统 | 主机名 | IP 地址 |
服务 | 作用 |
---|---|---|---|---|
Centos7.9 | openvpn-2023-1-60 | 10.200.1.60 | OpenVPN Client | VPN 客户端 |
Centos7.9 | OpenVPN-20231227 | 1.144.144.144 | OpenVPN Server | VPN 服务端 |
部署OpenVPN
服务器初始化(服务端/客户端均需要)
# 修改一些安装源
yum install -y epel-release
sed -e 's|^mirrorlist=|#mirrorlist=|g' \
-e 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos|g' \
-i.bak \
/etc/yum.repos.d/CentOS-*.repo
sed -e 's!^metalink=!#metalink=!g' \
-e 's!^#baseurl=!baseurl=!g' \
-e 's!http://download\.fedoraproject\.org/pub/epel!https://mirrors.tuna.tsinghua.edu.cn/epel!g' \
-e 's!http://download\.example/pub/epel!https://mirrors.tuna.tsinghua.edu.cn/epel!g' \
-i /etc/yum.repos.d/epel*.repo
# 安装基础包
yum install -y chrony conntrack ipvsadm ipset jq iptables curl sysstat libseccomp wget socat git gcc-c++ make yum-utils testice-mapper-persistent-data lvm2 bash-completion nfs-utils lrzsz tree
# 关闭防火墙
systemctl stop firewalld && systemctl disable firewalld
systemctl stop iptables && systemctl disable iptables
# 关闭 Selinux
setenforce 0 && sed -i 's/SELINUX=.*/SELINUX=disabled/g' /etc/selinux/config
# 时间同步
(crontab -l;echo '*/30 * * * * /usr/sbin/ntpdate ntp1.aliyun.com && /usr/sbin/hwclock -w') | crontab
# 内核优化
echo "* - nofile 65535" >> /etc/security/limits.conf
echo "* - nproc 65536" >> /etc/security/limits.conf
sed -i 's#4096#65536#g' /etc/security/limits.d/20-nproc.conf
cat >> /etc/security/limits.d/nofile.conf <<EOF
* soft nofile 65536
* hard nofile 65536
EOF
cat >> /etc/sysctl.conf <<EOF
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 20480
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2
net.core.somaxconn = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_max_tw_buckets = 10240
net.ipv4.ip_forward = 1
net.ipv4.tcp_tw_recycle = 0
net.ipv4.neigh.default.gc_thresh1 = 1024
net.ipv4.neigh.default.gc_thresh1 = 2048
net.ipv4.neigh.default.gc_thresh1 = 4096
vm.swappiness = 0
vm.overcommit_memory = 1
vm.panic_on_oom = 0
fs.inotify.max_user_instances = 8192
fs.inotify.max_user_watches = 1048576
fs.file-max = 52706963
fs.nr_open = 52706963
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 10
EOF
sysctl -p >/dev/null 2>&1
# 升级系统内核
# 载入公钥、安装 elrepo
rpm -import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm
# 修改源链接
sed -i 's|^mirrorlist=|#mirrorlist=|g' /etc/yum.repos.d/elrepo.repo
sed -i 's|http://elrepo.org/linux|https://mirrors4.tuna.tsinghua.edu.cn/elrepo|g' /etc/yum.repos.d/elrepo.repo
yum makecache fast
# 更新系统
yum update -y
# 载入 elrepo-kernel 元数据
yum --disablerepo=\* --enablerepo=elrepo-kernel repolist
# 查看可用内核
yum --disablerepo=\* --enablerepo=elrepo-kernel list kernel*
# 安装指定内核
yum remove -y kernel-tools-libs.x86_64 kernel-tools.x86_64
yum -y --enablerepo=elrepo-kernel install kernel-lt.x86_64 kernel-lt-tools.x86_64
# 重启一下
init 6
# 查看可用内核
awk -F\' '$1=="menuentry " {print $2}' /etc/grub2.cfg
# 指定内核启动(从0开始)
grub2-set-default 0
# 创建内核配置文件
grub2-mkconfig -o /boot/grub2/grub.cfg
# 再重启一下
init 6
部署服务端
# 安装openvpn和easy-rsa
yum install -y openvpn easy-rsa
mkdir -p /etc/openvpn/easy-rsa
cp -r /usr/share/easy-rsa/3.0.8/* /etc/openvpn/easy-rsa/
mkdir -p /etc/openvpn/easy-rsa/keys
cp -r /usr/share/doc/easy-rsa-3.0.8/vars.example /etc/openvpn/easy-rsa/vars
# 编写配置文件
cd /etc/openvpn/easy-rsa/ && vim /etc/openvpn/easy-rsa/vars
# 检查是否直接执行vars文件。如果是,显示错误信息并退出。
# 在Easy-RSA 3.x中,不推荐直接source vars文件。
if [ -z "$EASYRSA_CALLER" ]; then
echo "You appear to be sourcing an Easy-RSA 'vars' file." >&2
echo "This is no longer necessary and is disallowed. See the section called" >&2
echo "'How to use this file' near the top comments for more details." >&2
return 1
fi
# 设置Easy-RSA工作目录为当前目录。
set_var EASYRSA "`pwd`"
# 指定使用的OpenSSL命令。
set_var EASYRSA_OPENSSL "openssl"
# 指定pkcs11-tool命令,通常用于与硬件安全模块(HSM)交互。
set_var EASYRSA_PKCS11_TOOL "pkcs11-tool"
# 定义使用的grep命令。
set_var EASYRSA_GREP "grep"
# 设置存放私钥基础设施(PKI)的目录为当前目录下的keys文件夹。
set_var EASYRSA_PKI "`pwd`/keys"
# 设置RSA密钥的大小为2048位。
set_var EASYRSA_KEY_SIZE 2048
# 设置CA证书的有效期为36500天(约100年)。
set_var EASYRSA_CA_EXPIRE 36500
# 设置普通证书的有效期为36500天(约100年)。
set_var EASYRSA_CERT_EXPIRE 36500
# 设置证书请求(CSR)时使用的国家为中国。
set_var EASYRSA_REQ_COUNTRY "CN"
# 设置证书请求(CSR)时使用的省份为广东。
set_var EASYRSA_REQ_PROVINCE "GuangDong"
# 设置证书请求(CSR)时使用的城市为深圳。
set_var EASYRSA_REQ_CITY "ShenZhen"
# 设置证书请求(CSR)时使用的组织名。
set_var EASYRSA_REQ_ORG "XXXXX"
# 设置证书请求(CSR)时使用的电子邮箱地址。
set_var EASYRSA_REQ_EMAIL "[email protected]"
# 设置证书请求(CSR)时使用的组织单位为运维。
set_var EASYRSA_REQ_OU "YunWei"
# 设置证书请求(CSR)时使用的通用名称为EasyRSA。
set_var EASYRSA_REQ_CN "EasyRSA"
# 创建server证书类型的配置文件
cat >> /etc/openvpn/easy-rsa/x509-types/server <<EOF
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
extendedKeyUsage = serverAuth
keyUsage = digitalSignature,keyEncipherment
EOF
# 创建client证书类型的配置文件
cat >> /etc/openvpn/easy-rsa/x509-types/client <<EOF
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
extendedKeyUsage = clientAuth
keyUsage = digitalSignature
EOF
# 初始化
./easyrsa init-pki
# 生成ca证书文件
./easyrsa build-ca nopass
# 生成server端证书和私钥文件
./easyrsa gen-req server nopass
# 给server端证书签名
./easyrsa sign-req server server
# 生成client端证书和私钥文件
./easyrsa gen-req client nopass
# 给client端证书签名
./easyrsa sign-req client client
# 为安全的TLS通信生成Diffie-Hellman参数
./easyrsa gen-dh
# 生成openvpn服务端配置文件
cat >> /etc/openvpn/server.conf <<EOF
# 设定OpenVPN监听的本地地址,0.0.0.0 表示监听所有IP地址
local 0.0.0.0
# 设置OpenVPN服务器监听的端口
port 11211
# 设置使用的协议,这里是 UDP
proto udp
# 设置虚拟网络接口类型,这里使用 tun,适用于路由/点对点配置
dev tun
# CA(证书颁发机构)证书的位置
ca /etc/openvpn/easy-rsa/keys/ca.crt
# 服务器证书的位置
cert /etc/openvpn/easy-rsa/keys/issued/server.crt
# 服务器私钥的位置
key /etc/openvpn/easy-rsa/keys/private/server.key
# Diffie-Hellman 参数文件的位置
dh /etc/openvpn/easy-rsa/keys/dh.pem
# 配置服务器的VPN子网,客户端将从这个子网中获得IP地址
server 10.9.0.0 255.255.255.0
# 向客户端推送路由,表示客户端访问10.5.0.0子网时应通过VPN
push "route 10.5.0.0 255.255.0.0"
# 持久化客户端IP池,即使服务重启也保持IP地址分配的一致性
ifconfig-pool-persist ipp.txt
# 允许客户端之间相互通信
client-to-client
# 设置心跳检测间隔和超时时间
keepalive 10 120
# 开启压缩,可以提高传输效率
comp-lzo
# 最大客户端数量限制(100)
max-clients 100
# 持久化密钥,即在重启后不需要重新读取
persist-key
# 持久化虚拟网络接口,即在重启后不需要重新创建
persist-tun
# 日志文件,记录连接状态
status openvpn-status.log
# 日志文件的位置,记录详细的日志信息
log /var/log/openvpn.log
# 日志详细程度
verb 3
EOF
# 启动服务端
systemctl start openvpn@server && systemctl enable openvpn@server
# 记录客户端证书
cat /etc/openvpn/easy-rsa/keys/ca.crt
......
cat /etc/openvpn/easy-rsa/keys/issued/client.crt
......
cat /etc/openvpn/easy-rsa/keys/private/client.key
......
# 生成客户端配置文件
cat >> /etc/openvpn/client.conf <<EOF
# 客户端模式配置
client
# 使用的虚拟网络接口类型,这里是 tun
dev tun
# 设置使用的协议,这里是 UDP
proto udp
# 指定服务器的地址和端口
remote 1.144.144.144 11211
# 如果无法连接到服务器,则无限次重试
resolv-retry infinite
# 不绑定到特定的本地端口
nobind
# 持久化密钥,即在重启后不需要重新读取
persist-key
# 持久化虚拟网络接口,即在重启后不需要重新创建
persist-tun
# 嵌入CA证书,开始和结束标记之间是CA证书的内容
<ca>
-----BEGIN CERTIFICATE-----
[CA证书内容]
-----END CERTIFICATE-----
</ca>
# 嵌入客户端证书,开始和结束标记之间是客户端证书的内容
<cert>
Certificate:
[客户端证书内容]
-----END CERTIFICATE-----
</cert>
# 嵌入客户端私钥,开始和结束标记之间是私钥的内容
<key>
-----BEGIN PRIVATE KEY-----
[客户端私钥内容]
-----END PRIVATE KEY-----
</key>
# 开启压缩,可以提高传输效率
comp-lzo
# 日志详细程度
verb 3
EOF
# 配置服务端iptables
yum install -y iptables-services
systemctl start iptables && systemctl enable iptables
# 清空所有已有的规则,这将删除所有自定义规则,在每个链(INPUT, FORWARD, OUTPUT)中。
iptables -F
# 将默认策略设置为接受所有传入流量。
# 这意味着所有进入服务器的流量都将被允许,除非有特定规则拒绝它。
iptables -P INPUT ACCEPT
# 将默认策略设置为接受所有传出流量。
# 这意味着所有从服务器发出的流量都将被允许。
iptables -P OUTPUT ACCEPT
# 将默认策略设置为接受所有转发流量。
# 这对于路由器和网关非常重要,它允许流量通过服务器路由到其它网络。
iptables -P FORWARD ACCEPT
# 在NAT表的POSTROUTING链上添加一条规则,对所有源自10.9.0.0/24的出站流量进行MASQUERADE操作。
# 这意味着当这些数据包离开服务器时,它们的源IP地址将被更改为服务器的IP地址。
iptables -t nat -A POSTROUTING -s 10.9.0.0/24 -j MASQUERADE
# 保存当前的iptables规则设置。
# 这通常是通过将规则写入一个文件来实现,这样在服务器重启时规则可以被重新加载。
service iptables save
部署客户端
# 客户端安装openvpn
yum -y install openvpn
# 把服务端生成的client.conf放到/etc/openvpn目录下
# 启动客户端
systemctl start openvpn@client && systemctl enable openvpn@client
# 测试是否连通
# 可以见到客户端已经和服务端联通,此处的10.5.0.9是服务端的内网IP地址
[root@openvpn-2023-1-60 openvpn]# ping 10.5.0.9
PING 10.5.0.9 (10.5.0.9) 56(84) bytes of data.
64 bytes from 10.5.0.9: icmp_seq=1 ttl=64 time=8.27 ms
64 bytes from 10.5.0.9: icmp_seq=2 ttl=64 time=8.15 ms
64 bytes from 10.5.0.9: icmp_seq=3 ttl=64 time=8.37 ms
64 bytes from 10.5.0.9: icmp_seq=4 ttl=64 time=8.34 ms
64 bytes from 10.5.0.9: icmp_seq=5 ttl=64 time=8.59 ms
64 bytes from 10.5.0.9: icmp_seq=6 ttl=64 time=7.98 ms
64 bytes from 10.5.0.9: icmp_seq=7 ttl=64 time=8.29 ms
64 bytes from 10.5.0.9: icmp_seq=8 ttl=64 time=8.39 ms
64 bytes from 10.5.0.9: icmp_seq=9 ttl=64 time=8.39 ms
64 bytes from 10.5.0.9: icmp_seq=10 ttl=64 time=8.33 ms
^C
--- 10.5.0.9 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9013ms
rtt min/avg/max/mdev = 7.987/8.314/8.599/0.187 ms
此时还没有实现我们的需求,因为只是把服务端和客户端打通了,内网网络还是没法访问云上网络,别着急,我们接下来继续操作!
路由转发
核心交换机配置
此处以华为三层交换机为例,如下图一般配置,即可把访问 10.5.0.0/16
的云上网段数据做一个拦截操作。
客户端做转发
配置iptables
# 客户端安装iptables-services并启动
yum -y install iptables-services
systemctl start iptables && systemctl enable iptables
# 配置iptables
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
iptables -D FORWARD -j REJECT --reject-with icmp-host-prohibited
iptables -A FORWARD -s 10.220.210.0/24 -d 10.5.0.0/16 -j ACCEPT
iptables -A FORWARD -d 10.5.0.0/16 ! -p icmp -j DROP
iptables -I FORWARD -s 10.210.2.45/32 -d 10.5.0.0/16 -m comment --comment "我的电脑" -j ACCEPT
# 保存规则
service iptables save
解释一下
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
:
- 在
NAT
表的POSTROUTING
链上添加了一个规则,对于经过tun0
(OpenVPN
的通常接口)的出站流量,使用MASQUERADE
进行源地址转换。
iptables -D FORWARD -j REJECT --reject-with icmp-host-prohibited
:
- 从
FORWARD
链中删除了一条拒绝所有转发流量的规则,这是为了不阻碍需要的特定流量。
iptables -A FORWARD -s 10.220.210.0/24 -d 10.5.0.0/16 -j ACCEPT
:
- 允许从
10.220.210.0/24
到10.5.0.0/16
的流量转发。
iptables -A FORWARD -d 10.5.0.0/16 ! -p icmp -j DROP
:
- 丢弃目的地为
10.5.0.0/16
,但不是ICMP
协议的流量。
iptables -I FORWARD -s 10.210.2.45/32 -d 10.5.0.0/16 -m comment --comment "我的电脑" -j ACCEPT
:
- 插入(
-I
)一条新规则到FORWARD
链的最前面,允许从IP
地址10.210.2.45
到10.5.0.0/16
的流量转发,并附上了一个注释。
测试一下
总结
至此,实现了内部网络通过OpenVPN
和云上网络打通的需求了。
当然,目前只是内部网络能访问云上网络,并没有做云上网络访问内部网络的操作,这个我这暂时没需求,就不琢磨了。
同时,我这还添加了FORWARD
规则用作一个简单的权限控制,仅供参考。