Wireguard是一个市面上最好用的VPN软件之一,常常用来做内网穿透、异地组网等用途。通过Wireguard搭建VPN,通常需要你家里的路由器有一个公网IP,或者是你一个云服务器可以用来做流量的中转。
通常Wireguard使用的架构是如下的架构:
在如上的架构当中,通过10.0.9.1这台机器搭建起Wireguard-Server充当路由器的角色,接着10.0.9.1和10.0.9.2这两台机器作为Enpoint端点去连接上Wireguard-Server路由器,这两台机器可以是位于局域网内的两台设备,也可以是位于不同的网络(可以是不同的地区、不同的省份、不同的国家)。
Wireguard的实现原理是:
- Wireguard的各个端点Enpoint会在本地新增一个虚拟的网卡设备接口,当Wireguard网卡接口收到符合Wireguard的网段要求的请求时,这个网卡设备接口会将整个数据包加密后,传输给Wireguard服务端。
- Wireguard服务端根据访问的IP地址,找到要访问的目标机器(比如10.0.9.3),此时Wireguard-Server则通过自己已经建立的链接,找到目标机器(10.0.9.3)的位置,再将数据包解密之后,传输给Wireguard的目标Enpoint。
1.Wireguard客户端和服务端的搭建
说明:其实Wireguard本身是P2P的架构,是不区分服务端和客户端的。为了说明简单,我们这里把有公网IP的机器称为Wireguard的服务端,其他的机器称为Wireguard的客户端。
1.1 部署Wireguard服务端
服务端通常在Linux服务器上进行安装(例如云服务器/VPS),但是需要注意的是服务器的带宽和家用宽带的带宽,共同决定了VPN隧道传输的带宽。
我们可以通过如下的命令去安装Wireguard软件:
# ubuntu
apt install resolvconf wireguard-tools
# centos
yum install resolvconf wireguard-tools
# macos
brew install wireguard-tools
接着我们使用如下的命令生成Wireguard的服务端的公私钥文件(私钥放在server.privatekey文件当中,公钥放在server.publickey文件当中):
wg genkey | tee server.privatekey | wg pubkey > server.publickey
对于客户端的公私钥同理,采用相同的方式生成即可(假如需要生成为mac客户端生成公私钥文件,我们把私钥放在mac.privatekey文件当中,把公钥放在mac.publickey文件当中),如果有多个客户端,则多次执行命令,使用不同的文件分别去接收公私钥文件即可。
wg genkey | tee mac.privatekey | wg pubkey > mac.publickey
接着,我们需要类似如下的编写Wireguard服务端的配置文件wg0.conf
:
[Interface]
Address = 10.0.8.1/32
MTU = 1200
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;
PreDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE;
ListenPort = 51820
PrivateKey = {server.privatekey}
[Peer]
PublicKey = {client1.publickey}
AllowedIPs = 10.0.8.2/32
[Peer]
PublicKey = {client2.publickey}
AllowedIPs = 10.0.8.3/32
对于Wireguard的配置,主要由服务端(Interface
)和客户端(对端,Peer
)两个部分组成。
首先我们来介绍一下服务端Interface
的配置:
- 1.Interface部分的Address对应的就是Wireguard服务器在VPN网络当中的IP地址,需要是局域网IP的网段(比如
10.0.9.1/32
,192.168.1.1/32
)。 - 2.Interface部分的PostUp和PreDown主要是对于将Wireguard服务器作为路由器中的一些基础配置,主要需要修改的是其中的
eth0
,需要修改成为你用于上外网的网卡接口地址(可以通过ifconfig
命令进行查看)。 - 3.Interface部分的ListenPort,用于指定当前的Wireguard服务端,需要在哪个端口(注意是UDP端口)进行启动,可以自定义也可以用默认的51820端口。
- 4.Interface部分的PrivateKey需要填写上面我们生成的Server端的私钥文件(注意这个PrivateKey是绝对不能对外公开的,如果公开,意味着网络就存在泄露,会有安全问题!)。
接着我们来介绍一下客户端Peer
的配置,一个服务端可以配置多个Peer,对于多个Peer的配置方式完全相同。
- 1.Peer的PublicKey需要填写之前我们生成的客户端的公钥文件。
- 2.Peer的AllowedIPs需要填写的是,对于哪些IP地址网段的流量需要发送给这个客户端(通常配置客户端自己的内网地址即可,自己随便定义,和Interface部分的Address在一个网段内即可)。
比如我们最终生成如下的一个服务端的wg0.conf
配置文件:
[Interface]
Address = 10.0.8.1/32
MTU = 1200
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o ens34 -j MASQUERADE;
PreDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o ens34 -j MASQUERADE;
ListenPort = 51820
PrivateKey = GJ3c8D95DxoNlvVpSB0Aiy1DNesMoci6Yu+Wxo6TT18=
[Peer]
PublicKey = DYcfoo2THssKvVBgQ5DX4OaeBkiO0FfIPJ8YknCJvTQ=
AllowedIPs = 10.0.8.2/32
[Peer]
PublicKey = nKgSmHlspVHzyuFOYSjnwHgkHmnfMzfXlhZM6vbk0VA=
AllowedIPs = 10.0.8.3/32
接着,我们在VPS上使用如下的命令去启动Wireguard服务端:
wg-quick up ./wg0.conf
使用如下的命令去停止Wireguard服务端:
wg-quick down ./wg0.conf
我们可以通过wg
命令查看虚拟网卡的工作状态,当如果出现类似如下的结果则说明Wireguard服务已经启动成功。
$ sudo wg
interface: wg0
public key: lMyEX4Q/1J408339pYA4j0RR2cDM82yrProwx680N3E=
private key: (hidden)
listening port: 51820
peer: DYcfoo2THssKvVBgQ5DX4OaeBkiO0FfIPJ8YknCJvTQ=
allowed ips: 10.0.8.2/32
peer: nKgSmHlspVHzyuFOYSjnwHgkHmnfMzfXlhZM6vbk0VA=
allowed ips: 10.0.8.3/32
如果服务器存在有防火墙配置的话,那么需要在服务器控制台的防火墙放开51820端口的UDP协议才能被外部客户端进行连接和访问。
1.2 Wireguard客户端的配置
接下来我们来配置Wireguard客户端的配置文件wg0.conf
,其实和服务端的配置完全类似,也是由Interface
和Peer
两个部分组成。配置格式参考如下:
[Interface]
PrivateKey = {client.privatekey}
Address = 10.0.8.2/32
MTU = 1420
DNS = 1.1.1.1
[Peer]
PublicKey = {server.publicKey}
AllowedIPs = 10.0.8.0/24
Endpoint = {wireguard-server-ip}:51820
PersistentKeepalive = 25
Interface
部分则是配置当前机器的端点配置信息:
- 1.PrivateKey是配置当前客户端的私钥信息,需要和服务端的PublicKey对应的一对私钥PrivateKey。
- 2.Address是配置当前客户端在VPN网络当中的IP地址(例如
10.0.8.2/32
)。 - 3.DNS则需要配置Wireguard端点的DNS服务器。
Peer中则需要配置的是远程Wireguard服务端的配置信息:
- 1.PublicKey配置的是服务端的公钥{server.publicKey},是{server.privateKey}对应的公钥。
- 2.AllowedIPs配置的是Wireguard这个网卡接口需要处理的流量的IP地址段,我们直接配置
10.0.8.1/32
对应的网段10.0.8.0/24
即可。 - 3.Endpoint需要配置的是Wireguard服务端的IP地址和端口号。
- 4.PersistentKeepalive是配置的Wireguard通信的心跳检测的时间,用来检测Wireguard端点之间的连通性。
通过以上的这些配置,我们最终生成类似如下的wg0.conf
的配置文件:
[Interface]
PrivateKey = AE9bsIkOCw9ZyDO8DLwtXl8v0y45GnUFn3KrYpM0QkA=
Address = 10.0.8.2/32
MTU = 1420
DNS = 1.1.1.1
[Peer]
PublicKey = lMyEX4Q/1J408339pYA4j0RR2cDM82yrProwx680N3E=
AllowedIPs = 10.0.8.0/24
Endpoint = 192.168.11.50:51820
PersistentKeepalive = 25
对于后续我们的所有的客户端接入方式,都需要我们去准备好上面的配置文件,然后再进行后续的操作。
1.2.1 命令行方式进行Wireguard客户端的配置(Linux/MacOS)
对于Linux和Macos,都可以通过Wireguard命令工具进行配置,各个操作系统可以分别用如下的命令进行软件的安装Wireguard:
# ubuntu
apt install resolvconf wireguard-tools
# centos
yum install resolvconf wireguard-tools
# macos
brew install wireguard-tools
在准备好上面的配置文件之后,我们使用如下的命令进行启动Wireguard客户端。
wg-quick up ./wg0.conf
启动好Wireguard客户端之后,我们在客户端通过wg
命令去查看wg客户端与服务端之间的连接的状态信息,出现如下的状态,说明Wireguard客户端和服务端之间连接成功。
$ sudo wg
interface: utun23
public key: DYcfoo2THssKvVBgQ5DX4OaeBkiO0FfIPJ8YknCJvTQ=
private key: (hidden)
listening port: 61885
peer: lMyEX4Q/1J408339pYA4j0RR2cDM82yrProwx680N3E=
endpoint: 192.168.11.50:51820
allowed ips: 10.0.8.0/24
latest handshake: 55 seconds ago
transfer: 184 B received, 584 B sent
persistent keepalive: every 25 seconds
我们在客户端尝试去ping服务端的IP地址10.0.8.1
,发现能成功ping通,说明我们的网络配置是没问题的。
$ ping 10.0.8.1
PING 10.0.8.1 (10.0.8.1): 56 data bytes
64 bytes from 10.0.8.1: icmp_seq=0 ttl=64 time=10.984 ms
64 bytes from 10.0.8.1: icmp_seq=1 ttl=64 time=7.372 ms
64 bytes from 10.0.8.1: icmp_seq=2 ttl=64 time=5.107 ms
^C
--- 10.0.8.1 ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 5.107/7.821/10.984/2.420 ms
我们同样在服务端使用wg命令查看到各个客户端的状态,我们看到10.0.8.2/32
这个客户端的一些状态信息。
latest handshake: 52 seconds ago
说明上次握手的时间transfer: 244 B received, 92 B sent
告诉我们客户端和服务端之间传输的数据量(同时有received
和sent
才是正常的连通状态)。
$ sudo wg
interface: wg0
public key: lMyEX4Q/1J408339pYA4j0RR2cDM82yrProwx680N3E=
private key: (hidden)
listening port: 51820
peer: DYcfoo2THssKvVBgQ5DX4OaeBkiO0FfIPJ8YknCJvTQ=
endpoint: 192.168.11.237:61885
allowed ips: 10.0.8.2/32
latest handshake: 52 seconds ago
transfer: 244 B received, 92 B sent
peer: nKgSmHlspVHzyuFOYSjnwHgkHmnfMzfXlhZM6vbk0VA=
allowed ips: 10.0.8.3/32
1.2.2 扫码配置客户端(Android/iOS)
对于移动端(Android/iOS/iPadOS),没办法使用wg-quick
的命令给工具进行使用,可以使用二维码扫描的方式进行安装。
Wireguard软件的下载地址:Wireguard-Install。对于iOS/iPadOS在中国区是无法在App Store进行下载的,需要你拥有一个美区的AppleID才能进行下载。共享美区账号链接参考:共享美区账号。需要注意的是,千万别使用在手机的设置当中登录AppleID,因为你登录的是别人的号,万一别人改密码了,你的手机也就锁定了,没办法进去了,手机就废了!
首先我们需要在Wireguard服务端去安装好qrencode软件,进行二维码的生成:
sudo apt install qrencode
接着使用如下的命令,基于我们刚刚准备好的客户端的wg0.conf
文件(注意不是服务端的wg0.conf
文件),会在命令行当中生成一个二维码,接着客户端扫码即可。
qrencode -t ansiutf8 < ./wg0.conf
在服务端通过wg命令,发现我们客户端已经成功连接成功。
wanna@openrestry-manager:~/wg0$ sudo wg
interface: wg0
public key: lMyEX4Q/1J408339pYA4j0RR2cDM82yrProwx680N3E=
private key: (hidden)
listening port: 51820
peer: DYcfoo2THssKvVBgQ5DX4OaeBkiO0FfIPJ8YknCJvTQ=
endpoint: 192.168.11.191:59465
allowed ips: 10.0.8.2/32
latest handshake: 17 seconds ago
transfer: 1.46 KiB received, 1.42 KiB sent
peer: nKgSmHlspVHzyuFOYSjnwHgkHmnfMzfXlhZM6vbk0VA=
allowed ips: 10.0.8.3/32
2.TCP封包UDP解决运营商QOS限速
Wireguard默认是通过51820的UDP端口进行的通信,但是由于国内运营商大多都对UDP协议存在QOS限速,就算服务器是100Mbps的带宽,家用宽带是1000Mbps的宽带,可能Wireguard VPN隧道之间的网络通信都只能跑到不到10Mbps,甚至远程访问网页都会卡卡的。
市面上有很多的工具,可以将UDP转成TCP进行通信,比如常见的wstunnel,udp2raw等工具。
2.1 基于wstunnel封装Wireguard的UDP数据包
wstunnel的作用是:服务端启动一个WebSocket服务去代理本机51820端口的流量,后续Wireguard客户端发送给Wireguard服务端的流量,都将Wireguard数据包套在WebSocket数据包当中进行传输,WebSocket是标准的TCP流量,无法被运营商检测到从而避免被QOS。
家用网络300Mbps的下载、80Mbps的上传,VPS是200Mbps的上下行带宽。配置wstunnel后的效果预览:速度从100K/s->10M/s,提高100倍。
WsTunnel的官方Github仓库地址:GitHub-WsTunnel。wstunnel需要在客户端和服务端(一般是有公网IP的机器,比如VPS)同时安装,可以使用如下的命令下载和安装wstunnel。
mkdir wstunnel
cd wstunnel
wget https://github.com/erebe/wstunnel/releases/download/v10.4.4/wstunnel_10.4.4_linux_amd64.tar.gz
我们可以使用如下的命令进行安装wstunnel:
# 解压缩
tar -xvzf wstunnel*.tar.gz
# 给wstunnel加上可执行权限
chmod a+x wstunnel
2.1.1 服务端启动wstunnel
启动wstunel-server命令
服务端可以使用如下的命令去启动wstunnel:
wstunnel server --restrict-to localhost:51820 wss://[::]:1443
- 这里需要将
--restrict-to localhost:51820
修改为自己的Wireguard服务所启动的端口(通常默认为51820,如果有修改过的话,需要手动修改),用于限制客户端流量转发时只能转发到51820端口,如果不限制的话可能会被当做任意转发的代理工具。 wss://[::]:1443
代表,需要服务端会在1443端口上启动一个WebSocket服务,客户端到时候会将流量发送到这个端口上,WSTunnel再将流量转发到51820端口上。
配置开机自启动
可以通过如下的步骤去配置开机自启动。首先要新增一个wstunnel的Service。
sudo vim /etc/systemd/system/wstunnel.service
往其中去启动添加如下的内容(ExecStart部分需要和上面的启动命令一致):
[Unit]
Description=wstunnel Service
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/root/wstunnel
ExecStart=/root/wstunnel/wstunnel server --restrict-to localhost:51820 wss://[::]:1443
StandardOutput=append:/root/wstunnel/wstunnel.log
StandardError=inherit
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
接着我们使用如下的命令去配置好开机自启动wstunnel服务。
# 重新加载配置文件
sudo systemctl daemon-reload
# 启动wstunnel服务
sudo systemctl start wstunnel
# 配置开机自启动
sudo systemctl enable wstunnel
# 查看wstunnel服务的状态
sudo systemctl status wstunnel
2.1.2 客户端配置Wstunnel
(1) 在Linux配置客户端
服务端当中,配置了接收1443的WebSocket请求,1443将请求转发给UDP的31820端口。接下面我们来配置wstunnel客户端,我们使用如下的命令启动wstunnel客户端。
wstunnel client -L 'udp://51820:localhost:51820?timeout_sec=0' wss://{wstunnel-server-ip}:1443
udp://51820:localhost:51820
含义如下:1.转发的协议为udp
;2.需要将本机的51820
端口(udp://51820
)的流量转发给远程机器的localhost:51820
端口。wss://{wstunnel-server-ip}:1443
则是配置远程服务器的WebSocket地址,对于Wireguard数据包需要发送的目标地址。
因此上面的命令的含义就是:将本机的51820端口的UDP流量,转发给目标服务器的51820端口。具体是哪一台服务器呢?是wss://{wstunnel-server-ip}:1443
这台服务器。
可以通过如下的命令配置开机自启动:
sudo vim /etc/systemd/system/wstunnel.service
往其中加入如下的内容,ExecStart需要换成刚刚我们的启动命令。
[Unit]
Description=wstunnel Service
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/home/wanna/wg/wstunnel-wg
ExecStart=/home/wanna/wg/wstunnel-wg/wstunnel client -L 'udp://51820:localhost:51820?timeout_sec=0' wss://{wstunel-server-ip}:1443
StandardOutput=append:/home/wanna/wg/wstunnel-wg/wstunnel.log
StandardError=inherit
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
接着我们使用如下的命令去配置好开机自启动wstunnel服务。
# 重新加载配置文件
sudo systemctl daemon-reload
# 启动wstunnel服务
sudo systemctl start wstunnel
# 配置开机自启动
sudo systemctl enable wstunnel
# 查看wstunnel服务的状态
sudo systemctl status wstunnel
(2) 在Mac设备配置客户端
Mac当中配置自启动,和Linux类似,需要新建一个服务(Service
)。
需要在~/Library/LaunchAgents
下新建一个文件xxx.plist
,这里起名叫com.wanna.wstunnel.plist
。
vim ~/Library/LaunchAgents/com.wanna.wstunnel.plist
往其中加入如下的配置:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AbandonProcessGroup</key>
<true/>
<key>Label</key>
<string>com.wanna.wstunnel</string>
<key>ProgramArguments</key>
<array>
<string>/path/to/wstunnel_10.4.4_darwin_arm64/wstunnel</string>
<string>client</string>
<string>-L</string>
<string>udp://51820:localhost:51820?timeout_sec=0</string>
<string>wss://{wstunnel-server-ip}:1443</string>
</array>
<key>StartInterval</key>
<integer>3600</integer>
</dict>
</plist>
使用如下的命令启动服务。
launchctl load ~/Library/LaunchAgents/com.wanna.wstunnel.plist
launchctl start com.wanna.wstunnel
2.1.3 客户端修改Wireguard配置文件Endpoint
找到客户端的Wireguard配置文件wg0.conf
中的Endpoint配置:
Endpoint: {wireguard-server-ip}:51820
修改成为指向本机的51820端口:
Endpoint: 127.0.0.1:51820
配置好之后:后续所有的Wireguard流量,都会先发给本地的51820端口,51820端口的wstunnel会用WebSocket协议数据包的方式,将Wireguard数据包,去发送到Wireguard服务端的1443端口,再转发给服务器的51820端口。
评论