zhangguanzhang's Blog

个人办公用 wireguard 组网笔记

字数统计: 3k阅读时长: 14 min
2020/08/05 Share

作为 IT 人员,经常需要连到办公网工作,并不是每个公司都有 vpn,自己搭建的话 openvpn 之类的配置麻烦啰嗦。这里写下 wireguard 的简单搭建。它比 IPSec 更快,更简单,更精简,更有用。它比 OpenVPN 更高效。WireGuard 设计为通用 VPN,适用于多种不同情况。它是跨平台的,可大规模部署。

通常如下图的部署: 一台 ECS 主机,得有公网 IP,下图就是 pc ----> ECS <------ 公司的 pc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
            +----------+
| |
+------->+ ECS +<-----+
| +----------+ |
| |
| |
| |
| | company
| +---+------------------+
+--++ | |
|PC | | +---+ |
+---+ | |PC | |
| +---+ |
| |
+----------------------+

当然,如果你会折腾的话 pc 可以是软路由,有兴趣和条件的可以看我博客 proxmox x86软路由笔记

登录到 ECS 上

得益于 wireguard 中没有 client/server 的概念,只要所有 nat 中的某台机器能够和 gateway 主机建立连接,即可实现共享所有节点的网络资源。这里 ECS 有公网ip,所以担当 gateway

安装 wireguard

官方安装文档 ,或者查看 如何在五分钟内装好 WireGuard? ECS是 linux 系统的话内核要5.x以上,没有就升级下内核,其他个人 pc 电脑则下载客户端,当然软路由的话则去找个带 wireguard的固件。

centos7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
yum -y install https://www.elrepo.org/elrepo-release-7.0-4.el7.elrepo.noarch.rpm
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
yum --enablerepo=elrepo-kernel install -y kernel-lt

yum -y --enablerepo=elrepo-kernel install kernel-lt-{devel,headers,perf}
# 失败就加 --skip-broken


awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg

#看数字
grub2-set-default 2
grub2-mkconfig -o /etc/grub2.cfg
reboot

yum install dkms kmod-wireguard wireguard-tools

reboot

modprobe wireguard

配置

开启转发

1
sysctl -w net.ipv4.ip_forward=1
1
cd /etc/wireguard

生成密钥对

wg 的每个互相之间要一对密钥,例如 A 连 gateway, A 需要 gateway的公钥,gateway 需要 A 的公钥,不能共用一套密钥对。

生成 gateway 的密钥对

1
wg genkey | tee gw-privatekey | wg pubkey > gw-publickey

生成个人电脑的密钥对

1
wg genkey | tee pc-privatekey | wg pubkey > pc-publickey

生成公司电脑的密钥对

1
wg genkey | tee cm-pc-privatekey | wg pubkey > cm-pc-publickey

配置文件

wg 的组网得定义一个网段,这个网段和你所有运行了wg的局域网的ip不能一样,例如我定义的是 10.1.0.1/24,ecs 上配置文件为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
cat > wg0.conf <<EOF
[Interface]
ListenPort = 16000 # 客户端连过来填写的端口,安全组的tcp和udp都要放行
Address = 10.1.0.1/24 #wg之前通信组网的内网ip和段
PrivateKey = $(cat gw-privatekey) # 使用 shell 读取gateway的私钥到这里
# 下面两条是放行的iptables和MASQUERADE
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

# pc
[Peer]
PublicKey = $(cat pc-publickey)
AllowedIPs = 10.1.0.2/32

# company router
[Peer]
PublicKey = $(cat cm-pc-publickey)
AllowedIPs = 10.1.0.3/32, 192.168.2.0/24, 10.243.0.0/16, 10.0.6.0/24, 172.13.0.0/16

EOF

然后是每个客户端的配置文件,下面是我笔记本 wg 的客户端软件配置文件内容。

1
2
3
4
5
6
7
8
9
10
11
12
cat > pc.conf <<EOF
[Interface]
PrivateKey = $(cat pc-privatekey)
Address = 10.1.0.2/24 #wg之前通信组网的内网ip和段,主机位每个得不一样
# DNS = 192.168.2.3

[Peer]
PublicKey = $(cat gw-publickey) # gateway的公钥
AllowedIPs = 10.1.0.0/24, 192.168.2.0/24, 10.243.0.0/16, 10.0.6.0/24, 172.13.0.0/16
Endpoint = $(curl -s ip.sb):16000 #gateway 公网ip和端口
PersistentKeepalive = 10 # 心跳时间
EOF

公司的电脑 wg 配置文件

1
2
3
4
5
6
7
8
9
10
11
cat > cm-pc.conf <<EOF
[Interface]
PrivateKey = $(cat cm-pc-privatekey)
Address = 10.1.0.3/24 #wg之前通信组网的内网ip和段,主机位每个得不一样

[Peer]
PublicKey = $(cat gw-publickey) # gateway的公钥
AllowedIPs = 10.1.0.0/24
Endpoint = $(curl -s ip.sb):16000 # gateway 公网ip和端口
PersistentKeepalive = 10 # 心跳时间
EOF

然后把 pc.confcm-pc.conf 的内容拷贝到对应的 wg 客户端软件里。

讲解下配置文件,我办公室是台式机proxmox里的软路由,并不是上面我说的办公室pc,这样我接入的设备也可以访问。我个人是推荐办公室搞个 proxmox 整虚拟机和软路由。

办公网的路由器网段是 192.168.2.0/24192.168.2.3是软路由,主要是上面有dns server(adguard home),办公网内添加 hosts 我是直接在dns server上添加的。所以我个人PC那里写了 DNS = 192.168.2.3,这样 dns 解析都走到办公网的软路由上,家里不需要本地配置 hosts。

10.243.0.0/16, 10.0.6.0/24, 172.13.0.0/16的网段都是办公网的内网网段。AllowedIPs意思就是把请求目的IP是这些网段的,都发到 wg0 这个接口上,也就是添加路由表。这样我在家里,我个人 pc 打开 wg后,就能访问办公网了。

ECS上启动 wg 和停止 wg

1
2
3
wg-quick up wg0 #默认取 /etc/wireguard/$name.conf
# 指定配置文件启动 wg-quick up /etc/wireguard/wg0.conf
wg-quick down wg0

PostUp 和 PostDown 就是启动后和停止后的命令,是 Linux 的话就推荐写 iptables 放行转发和做 NAT。

查看组网状态,shell 上 wg 回车即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ wg
interface: wg0
public key: FZcFhf0eq2yFgXPNBqYnpoZHnzmgFI7JCLp/5vn1DG0=
private key: (hidden)
listening port: 16000

peer: OtydRPJDt+H8upZDz5zJueRjUQ0tS4tr9P6w4BL2+w0=
endpoint: xxxxxxxxxxx:53956
allowed ips: 10.1.0.3/32, 192.168.2.0/24, 10.243.0.0/16, 10.0.6.0/24, 172.13.0.0/16
latest handshake: 1 minute, 2 seconds ago
transfer: 485.48 MiB received, 55.88 MiB sent

peer: VkhLdmaPS2KmhlSOrPk1XS1MWZrhb+00BdsC0swUBhk=
endpoint: xxxxxxxxxx:13545
allowed ips: 10.1.0.2/32
latest handshake: 21 minutes, 25 seconds ago
transfer: 56.11 MiB received, 476.83 MiB sent

一些注意点

  • 如果是软路由,开了 pxsswxll 代理之类的,记得把 ECS 的 公网IP 设置为不走代理。
  • openwrt 运行 wireguard 的话:网络防火墙常规设置常规设置转发 设置为接受
  • openwrt 的 wireguard 多次配置可能会有问题,gateway上无法看到它上线,重启下后再试试。也可能需要下面规则
    • 网络防火墙自定义规则
      1
      2
      3
      iptables -I FORWARD -i wg0 -j ACCEPT
      iptables -I FORWARD -o wg0 -j ACCEPT
      iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
    • 如果 openwrt 下面的设备比如电脑和手机和组网的段不通,但是openwrt ssh上去ping和curl测试可以访问组网的段,可以加下nat规则iptables -t nat -I POSTROUTING -o wg0 -j MASQUERADE
    • 如果还是无法连到云主机上, br-lan 的 eth0 取消桥接然后重启机器试试

路由器上可以访问任何,但是路由器下的设备访问 peer B 在部分 https 网址一直 timeout , 路由器上 firewall 里 lan -> wan 开下 动态伪装 和 mss 。对比了下规则开启的话就加了下面的(不是让你来加的,是我笔记记录下):

1
2
iptables -t nat -A zone_lan_postrouting -m comment --comment "!fw3" -j FULLCONENAT
iptables -t nat -A zone_lan_prerouting -m comment --comment "!fw3" -j FULLCONENAT
非单口软路由

其实单口软路由也差不多可以尝试下这个,实在不通后可以 wg0 添加一个 wg0 的 firewall zone,然后 zone 里修改,允许转发到 lan(或者全部勾选上)。这个 zone 开 动态伪装 和 mss 。

udp被 qos 下配合 udp2raw 使用

实际使用中很大几率遇到 udp 被 qos 了,导致连接经常断开,这里使用 udp2raw 把 udp 报文伪装成 tcp 避免被 qos。这里我使用 docker 部署的,实体进程和相关文档见 udp2raw运行

Linux server端 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 监听86的tcp端口,把86端口收到的伪装成tcp的udp报文转发到 127.0.0.1:16000 上
docker run \
-d --name udp2raw \
--restart always \
--net host \
--cap-add NET_RAW \
--cap-add NET_ADMIN \
-v /run/xtables.lock:/run/xtables.lock \
zhangguanzhang/udp2raw \
-s -l 0.0.0.0:86 \
-r 127.0.0.1:16000 \
-k passwd123 \
--raw-mode faketcp \
--cipher-mode xor -a

Linux或者软路由系统 client 端,软路径 openwrt 的话 iptables 的锁文件是位于 /var/run/xtables.lock,常规系统是 `/run/xtables.lock :

1
2
3
4
5
6
7
8
9
10
11
12
13
# 监听16000的 udp 端口,把16000端口收到的udp报文伪装成tcp发到 <public_ip>:86 上
docker run --net host \
-d --name udp2raw \
--restart always \
--cap-add NET_RAW \
--cap-add NET_ADMIN \
-v /var/run/xtables.lock:/run/xtables.lock \
zhangguanzhang/udp2raw \
-c -l 0.0.0.0:16000 \
-r <public_ip>:86 \
-k passwd123 \
--raw-mode faketcp \
--cipher-mode xor -a

windows 客户端下载,运行命令参考:

1
./udp2raw_mp.exe -c -l 0.0.0.0:16000 -r <public_ip>:86 -k passwd123 --raw-mode faketcp --cipher-mode xor

所有 client 端的 [peer] 部分里之前连云主机的 ip 都写成127.0.0.1:16000,这样 wg 客户端是先向本地的 udp2raw 客户端发 udp 报文,然后报文被封装成 tcp 发往云主机上的 udp2raw server,再到 wg server 上。

客户端和云主机上 的 wg 的 mtu 设置成 1280(网上有写1200的,但是 windows 的 wg 客户端无法启动,邮件询问作者说最小 1280 才能启动)。例如我路由器配置

1
2
3
4
5
6
7
[Interface]
...
MTU = 1280
[Peer]
...
Endpoint = 127.0.0.1:16000
PersistentKeepalive = 10

windows的 wg 目前 Endpoint必须写本机的 ip(ipconfig命令查看),不能写127.0.0.1,否则无法连 peer(日志会一直刷Failed to send handshake initiation write udp4 0.0.0.0:xxx->127.0.0.1:16000: wsasendto: The requested address is not valid in its context),这个 bug 已经反馈给作者了。

udp2raw 的 client 连上 server 后,双方都会打印下面日志:

1
2
3
4
# server
changed state to server_ready
# client
changed state from to client_handshake2 to client_ready

qos

不同运营商可能不一样,比如你 A 和 B 同时 udp2raw 你云主机,A 可以 B 不可以,可以考虑换下 --raw-mode--seq-mode ,有的可能 faketcp,有的可能 udp ,有的可能 icmp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
usage:
run as client : ./this_program -c -l local_listen_ip:local_port -r server_address:server_port [options]
run as server : ./this_program -s -l server_listen_ip:server_port -r remote_address:remote_port [options]

common options,these options must be same on both side:
--raw-mode <string> available values:faketcp(default),udp,icmp and easy-faketcp
-k,--key <string> password to gen symetric key,default:"secret key"
--cipher-mode <string> available values:aes128cfb,aes128cbc(default),xor,none
--auth-mode <string> available values:hmac_sha1,md5(default),crc32,simple,none
-a,--auto-rule auto add (and delete) iptables rule
-g,--gen-rule generate iptables rule then exit,so that you can copy and
add it manually.overrides -a
--disable-anti-replay disable anti-replay,not suggested
--fix-gro try to fix huge packet caused by GRO. this option is at an early stage.
make sure client and server are at same version.
client options:
--source-ip <ip> force source-ip for raw socket
--source-port <port> force source-port for raw socket,tcp/udp only
this option disables port changing while re-connecting
other options:
--conf-file <string> read options from a configuration file instead of command line.
check example.conf in repo for format
--fifo <string> use a fifo(named pipe) for sending commands to the running program,
check readme.md in repository for supported commands.
--log-level <number> 0:never 1:fatal 2:error 3:warn
4:info (default) 5:debug 6:trace
--log-position enable file name,function name,line number in log
--disable-color disable log color
--disable-bpf disable the kernel space filter,most time its not necessary
unless you suspect there is a bug
--dev <string> bind raw socket to a device, not necessary but improves performance
--sock-buf <number> buf size for socket,>=10 and <=10240,unit:kbyte,default:1024
--force-sock-buf bypass system limitation while setting sock-buf
--seq-mode <number> seq increase mode for faketcp:
0:static header,do not increase seq and ack_seq
1:increase seq for every packet,simply ack last seq
2:increase seq randomly, about every 3 packets,simply ack last seq
3:simulate an almost real seq/ack procedure(default)
4:similiar to 3,but do not consider TCP Option Window_Scale,
maybe useful when firewall doesnt support TCP Option
--lower-level <string> send packets at OSI level 2, format:'if_name#dest_mac_adress'
ie:'eth0#00:23:45:67:89:b9'.or try '--lower-level auto' to obtain
the parameter automatically,specify it manually if 'auto' failed
--wait-lock wait for xtables lock while invoking iptables, need iptables v1.4.20+
--gen-add generate iptables rule and add it permanently,then exit.overrides -g
--keep-rule monitor iptables and auto re-add if necessary.implys -a
--hb-len <number> length of heart-beat packet, >=0 and <=1500
--mtu-warn <number> mtu warning threshold, unit:byte, default:1375
--clear clear any iptables rules added by this program.overrides everything
--retry-on-error retry on error, allow to start udp2raw before network is initialized
-h,--help print this help message

一个注意点

openwrt 上在接口 添加 wireguard 接口,然后 peer 那里的 ip 写 127.0.0.1(也就是openwrt上的udp2raw的ip)可能不行,换成 openwrt 的 局域网 ip试下

参考:

CATALOG
  1. 1. 登录到 ECS 上
    1. 1.1. 安装 wireguard
      1. 1.1.1. centos7
    2. 1.2. 配置
    3. 1.3. 生成密钥对
    4. 1.4. 配置文件
      1. 1.4.1. 一些注意点
        1. 1.4.1.1. 非单口软路由
  2. 2. udp被 qos 下配合 udp2raw 使用
    1. 2.1. qos
    2. 2.2. 一个注意点
  3. 3. 参考: