Wireguard是一个市面上比较好用的VPN工具,也常常被用来做内网穿透。Wireguard支持利用公私钥对流量进行加密(支持加密是很重要的,不然数据传输存在有很大的不安全性),Wireguard可能已经被视为业内最安全、最易于使用和最简单的 VPN 解决方案。Wireguard的官网如下:Wireguard。
首先我们需要知道的是,想要搭建一个VPN服务器,需要有一个公网IP,比如说拥有一个公网IP的云服务器也行。
实现原理是:Wireguard会在本地新增一个虚拟的网卡设备接口,当访问符合Wireguard的网段的要求的请求时,这个网卡设备接口会将流量加密后,传输给Wireguard服务端。Wireguard根据访问的目标虚拟IP,找到对应的真实机器,向目标机器发起请求。Wireguard服务器为两台机器之间建立连接,建立起了一条安全的隧道。
Wireguard的网络通信模型如下,对于所有的网络流量都将会通过Wireguard服务器进行转发,无法实现P2P的功能。
Wireguard的整体数据传输加密方式实现,和SSH的免密登录的实现方式类似。对于每个接入Wireguard的服务端,和Wireguard的客户端,都会拥有自己的公钥和私钥,在生成Wireguard配置文件时,则需要首先生成各自的公钥和私钥。对于服务端,持有所有的客户端的公钥,对于每个客户端的流量进行身份验证;对于客户端,持有服务端,则持有服务端的公钥,对服务端发送的流量进行身份验证。
我们下面的Wireguard的搭建过程,都是在云服务器上进行的搭建,因为云服务器有带公网IP,但是也会存在有问题,因为云服务器的带宽较小,流量传输的带宽上限也会在服务器的公网带宽。
Wireguard有很多种部署方式,包括原生的二进制、Docker、K8S等多种部署方式。我们下面主要介绍以Docker/K8S的方式进行Wireguard的部署的几种方式:
1.基于官方原生镜像搭建Wireguard服务
基于官方原生镜像搭建Wireguard时,需要自己准备好Wireguard的配置文件wg0.conf文件。对于客户端和服务端,都需要准备好对应的Wireguard配置文件。
1.1 Wireguard公钥和私钥的生成
首先需要在一台机器上安装Wireguard工具,用于进行各个客户端和服务端的公钥、私钥的生成。可以使用如下的命令去安装Wireguard:
# ubuntu
apt install resolvconf wireguard-tools
# centos
yum install resolvconf wireguard-tools
接着,我们需要使用如下的命令,生成Wireguard的私钥文件和私钥文件。
# 生成服务端的私钥
wg genkey > server.privatekey
# 利用服务端的私钥去生成服务端的公钥
wg pubkey < server.privatekey > server.publickey
如果想要生成一个客户端的公钥私钥,和生成一个服务端的公钥私钥的流程完全一致。
wg genkey > client1.privatekey
wg pubkey < client1.privatekey > client1.publickey
生成服务端和客户端都公私钥文件之后,下面我们来生成服务端和客户端的wg0配置文件。
服务端的Wireguard配置文件编写
在Interface处配置Wireguard的服务端的配置信息:
- PrivateKey配置在上面生成的server.privatekey的当中的内容。私钥禁止泄露,它泄露代表了整个VPN下的所有的机器都已经泄露,非常危险!!!
- ListenPort配置Wireguard服务端启动的端口号(注意是UDP协议,默认是51820端口,需要在防火墙放开51820的UDP端口)。
- Address指定Wireguard服务端将会分配的IP地址。
在Peer处配置配置各个客户端的配置信息,如果有多个客户端,那么就需要配置多个Peer:
- PublicKey处配置Wireguard客户端的公钥,也就是上面的client1.privatekey文件当中的内容。
- AllowedIPs处用于配置,Wireguard服务端对于这个Peer客户端来说,需要处理哪些网段的数据包。
# Server
[Interface]
PrivateKey = {服务端私钥}
Address = 10.0.8.1/24
ListenPort = 51820
PreUp =
PostUp = iptables -t nat -A POSTROUTING -s 10.0.8.0/24 -o eth0 -j MASQUERADE; iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT;
PreDown =
PostDown = iptables -t nat -A POSTROUTING -s 10.0.8.0/24 -o eth0 -j MASQUERADE; iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT;
# Client1
[Peer]
PublicKey = {客户端公钥}
AllowedIPs = 10.0.8.2/32
# Client2
[Peer]
PublicKey = {客户端公钥}
AllowedIPs = 10.0.8.3/32
我们来解释一下上面的配置含义:Server端在50820端口UDP启动,在Wireguard虚拟网络当中的IP地址是10.0.8.1/24
,有两个客户端10.0.8.2/32
和10.0.8.3/32
,比如说10.0.8.2/32
这个客户端在浏览器输入了10.0.8.3
这个IP地址时,请求将会到达Wireguard服务端,在服务端进行流量的转发,由服务端将流量转发到10.0.8.3
这个虚拟IP的Wireguard客户端。
还有一部分也是很重要的,PostUp
是Wireguard服务启动之后会执行的回调钩子,PostDown
是Wireguard服务在关闭之后会执行的钩子,修改本机的iptables流量转发的规则:
PreUp =
PostUp = iptables -t nat -A POSTROUTING -s 10.0.8.0/24 -o eth0 -j MASQUERADE; iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT;
PreDown =
PostDown = iptables -t nat -A POSTROUTING -s 10.0.8.0/24 -o eth0 -j MASQUERADE; iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT;
我们来一条一条分析PostUp
中的规则:
iptables -t nat -A POSTROUTING -s 10.0.8.0/24 -o eth0 -j MASQUERADE
规则的含义:对于服务端出口的流量,如果IP网段是10.0.8.0/24
的话,把它交给eth0
网卡进行流出,并通过-j MASQUERADE
将源IP隐藏,修改成为eth0
网卡的IP,实现的其实就是路由器的NAT地址转换功能。iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT
规则的含义:对于入流量访问UDP协议的51820端口直接放行(-j ACCEPT
),其实就是放开入流量的UDP协议51820端口的防火墙规则,保证外网可以访问服务器的51820端口。iptables -A FORWARD -i wg0 -j ACCEPT
规则的含义:对于从wg0
虚拟网卡接口进入进行转发的流量,一律放行。iptables -A FORWARD -o wg0 -j ACCEPT
规则的含义:对于从wg0
虚拟网卡接口转发出去的流量,一律放行。
PostDown
中的规则,就是把启动时新增的iptables规则进行删除掉,避免因为Wireguard的启停导致iptables规则产生突然。对于和PostUp
在命令上的区别,就是将-A
改为-D
。
客户端的Wireguard配置文件的编写
客户端的配置信息,和服务端的配置方式类似。
在Interface处填写客户端虚拟网卡接口的配置信息:
- PrivateKey填写客户端私钥,不要对外泄露。
- Address填写在VPN网络中的当前客户端的IP地址。
Peer处则需要填写当前客户端需要怎么和服务端进行交互:
- PublicKey处填写服务端的公钥。
- AllowedIPs处填写对于客户端中的Wireguard虚拟网卡,需要处理哪些网段的数据包。
- PersistentKeepalive用于填写,客户端和服务端之间的心跳包的时间。
- Endpoint处填写服务端的目标地址,可以是IP+端口号的格式,也可以是域名+端口号的格式。
[Interface]
PrivateKey = {客户端私钥}
Address = 10.0.8.2/24
DNS = 114.114.114.114
[Peer]
PublicKey = {服务端公钥}
AllowedIPs = 10.0.8.0/24
PersistentKeepalive = 25
Endpoint = xxx.yyy.com:51820
1.2 基于Docker部署Wireguard服务端
如果是Docker进行的部署,那么可以使用如下的命令,去启动一个Wireguard服务的服务端。
docker run -d \
--name=wireguard \
-v ~/wireguard/wg0.conf:/config/wg0.conf \
-p 51820:51820/udp \
--cap-add=NET_ADMIN \
--cap-add=SYS_MODULE \
--sysctl="net.ipv4.conf.all.src_valid_mark=1" \
--sysctl="net.ipv4.ip_forward=1" \
--restart unless-stopped \
linuxserver/wireguard
我们通过-v ~/wireguard/wg0.conf:/config/wg0.conf
参数,将本机下的~/wireguard/wg0.conf
文件挂载到Docker容器的/config/wg0.conf
下。
1.3 基于K8S部署Wireguard服务端
只需要基于如下的K8S资源清单即可部署Wireguard服务:
apiVersion: v1
kind: Namespace
metadata:
name: wireguard
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wireguard-pvc
namespace: wireguard # 在 wireguard 命名空间中创建 PVC
spec:
accessModes:
- ReadWriteMany # NFS 允许多个节点同时访问
resources:
requests:
storage: 10Gi # 需要的存储大小
storageClassName: nfs-storage # 使用 nfs-storage storage class
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wireguard
namespace: wireguard # 在 wireguard 命名空间中创建 Deployment
labels:
app: wireguard
spec:
replicas: 1
selector:
matchLabels:
app: wireguard
template:
metadata:
labels:
app: wireguard
spec:
initContainers:
- name: set-systemctl
image: harbor.wanna1314y.top/library/busybox
command:
- "sh"
- "-c"
- |
sysctl -w net.ipv4.conf.all.src_valid_mark=1
sysctl -w net.ipv4.ip_forward=1
securityContext:
privileged: true
imagePullPolicy: Always
containers:
- name: wireguard
image: harbor.wanna1314y.top/linuxserver/wireguard:latest
volumeMounts:
- name: wireguard-config
mountPath: /config
ports:
- containerPort: 51820
protocol: UDP
securityContext:
capabilities:
add: ["NET_ADMIN", "SYS_MODULE"]
resources: {}
volumes:
- name: wireguard-config
persistentVolumeClaim:
claimName: wireguard-pvc # 使用 PVC
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: wireguard-service
namespace: wireguard # 在 wireguard 命名空间中创建 Service
spec:
selector:
app: wireguard
ports:
- protocol: UDP
port: 51820
targetPort: 51820
nodePort: 31820 # 指定 NodePort 的端口号
name: wireguard-udp
type: NodePort # 使用 NodePort 类型
2. Wireguard客户端接入
2.1 Linux/Macos客户端通过命令行的方式接入
客户端需要安装openresolv和wireguard-tools工具。
# ubuntu/debain
apt update
apt install resolvconf wireguard-tools
# centos
yum install resolvconf wireguard-tools
# macos
brew install wireguard-tools
接着,需要从Wireguard后台Web页面下载客户端的Wireguard配置文件,接着执行wg-quick脚本,需要指定wg的配置文件。
# 启动wireguard客户端, 指定配置文件
wg-quick up ./wg0.conf
# 停止wireguar客户端, 指定配置文件
wg-quick down ./wg0.conf
如果出现下面的情况,说明这个文件的权限太高了,我们可以降低一下权限,不让其他人可以查看,可以执行sudo chmod 700 node.conf
进行降低权限。
Warning: `/root/wg/node.conf' is world accessible
接着通过ifconfig
查看机器的网卡可以发现,新增了一个虚拟的网卡设备。
node: flags=209<UP,POINTOPOINT,RUNNING,NOARP> mtu 1420
inet 10.0.8.3 netmask 255.255.255.0 destination 10.0.8.3
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 1000 (UNSPEC)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 27 bytes 3996 (3.9 KB)
TX errors 0 dropped 1 overruns 0 carrier 0 collisions 0
Linux客户端实现Wireguard自启动,比如目前我有一个配置文件wg0.conf(需要更换成为自己的配置文件名),那么我可以使用如下的命令去实现Wireguard客户端的自启动。
# 拷贝Wireguard客户端配置文件
sudo cp ./wg0.conf /etc/wireguard/
# 确保文件有权限
sudo chmod 600 /etc/wireguard/wg0.conf
# 启动Wireguard自启动的service
sudo systemctl enable wg-quick@wg0.service
# 使用systemctl命令查看service是否已经开启自启动(看看有没有enabled属性)
sudo systemctl status wg-quick@wg0.service
验证配置开启自启动是否生效:
# 重启服务器
reboot
# 查看网卡端口是否包含我们新创建的Wireguard的网卡接口
ifconfig
2.2 Mac/iphone客户端通过官方客户端接入
Wireguard的客户端下载链接如下:Wireguard-Install
iphone接入需要一个美区账号,在AppleStore才能下载,国区账号,好像没有什么别的好办法。可以去淘宝买一个美区账号,登录AppleStore,下载然后退出账号!
需要注意的是,千万别使用在手机的设置当中登录,因为你登录的是别人的号,万一别人改密码了,你的手机也就锁定了,没办法进去了,手机就废了!
共享的美区账号链接参考:美区账号。
下载Wireguard APP之后,选择导入配置文件即可,导入我们上面生成的wg0配置文件。在后面我们接入图形化的Wireguard之后,iphone客户端可以通过扫码的方式导入wg0配置文件。
3. 基于wg-easy搭建Wireguard
Wg-easy在基础的Wireguard的基础上,新增了简单的图形化管理页面,可以方便我们快速进行管理Wireguard的客户端。
3.1基于Docker部署wg-easy
Wireguard服务器的搭建
可以参考Github:
- https://github.com/wg-easy/wg-easy/tree/production
- https://github.com/WeeJeWel/wg-easy?tab=readme-ov-file#updating
我们使用docker pull命令去进行拉取镜像:
docker pull weejewel/wg-easy
接着,我们使用如下的命令去启动wireguard容器。
docker run -d \
--name=wg-easy \
-e WG_HOST=1.1.1.1 \
-e PASSWORD=passwd123 \
-e WG_DEFAULT_ADDRESS=10.0.8.x \
-e WG_DEFAULT_DNS=114.114.114.114 \
-e WG_ALLOWED_IPS=10.0.8.0/24 \
-e WG_PERSISTENT_KEEPALIVE=25 \
-v ~/.wg-easy:/etc/wireguard \
-p 51820:51820/udp \
-p 51821:51821/tcp \
--cap-add=NET_ADMIN \
--cap-add=SYS_MODULE \
--sysctl="net.ipv4.conf.all.src_valid_mark=1" \
--sysctl="net.ipv4.ip_forward=1" \
--restart unless-stopped \
weejewel/wg-easy
这里有几项是我们必须要进行修改的:
- WG_HOST指定Wireguard服务器的地址,可以是IP和域名。
- PASSWORD指定Wireguard服务器的后台登录密码。
-p 51820:51820/udp
指定将Wireguard服务器启动的UDP协议的51820端口,映射成为宿主机的51820端口。-p 51821:51821/tcp
指定将Wireguard服务器WebUI的TCP协议的51821端口,映射成为宿主机的51821端口,后续我们可以通过Wireguard服务器IP加上51821端口进行访问Wireguard服务。
启动完Docker容器之后,我们访问服务器的公网IP的51821端口,即可进入到Wireguard的网页管理页面,并输入密码进入到Wireguard后台。
进入到Wireguard后台之后,可以点击New按钮,新增一个Wireguard客户端。
Wireguard客户端接入
新建客户端之后,可以点击客户端右侧的,二维码和下载按钮,二维码是给手机客户端准备的,下载按钮是给电脑端/Linux等系统准备的。
当成功连接上时,客户端上会有一个小红点,代表这个客户端已经在线。
页面上这里展示的10.0.8.3就是这个客户端的IP,后续我们可以通过10.0.8.3这个IP去访问我们的NAS。
我们同时将NAS和Mac接入Wireguard客户端之后,我们就可以在Mac设备上通过VPN的虚拟IP去访问NAS的服务了。
3.2通过K8S部署wg-easy
可以通过如下的K8S资源清单,去创建Wireguard的Deployment和Service。
apiVersion: v1
kind: Namespace
metadata:
name: wireguard
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wg-easy-pvc
namespace: wireguard # 在 wireguard 命名空间中创建 PVC
spec:
accessModes:
- ReadWriteMany # NFS 允许多个节点同时访问
resources:
requests:
storage: 10Gi # 需要的存储大小
storageClassName: k8s-nfs-storage # 使用 nfs-storage storage class
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wg-easy
namespace: wireguard # 在 wireguard 命名空间中创建 Deployment
labels:
app: wg-easy
spec:
replicas: 1
selector:
matchLabels:
app: wg-easy
template:
metadata:
labels:
app: wg-easy
spec:
initContainers:
- name: set-systemctl
image: harbor.wanna1314y.top/library/busybox
command:
- "sh"
- "-c"
- |
sysctl -w net.ipv4.conf.all.src_valid_mark=1
sysctl -w net.ipv4.ip_forward=1
securityContext:
privileged: true
imagePullPolicy: Always
containers:
- name: wg-easy
image: weejewel/wg-easy # harbor.wanna1314y.top/linuxserver/wg-easy
env:
- name: WG_HOST
value: "xxx.yyy.com" # 外部 IP 地址(宿主机 IP 或外网 IP)
- name: PORT
value: "51821"
- name: PASSWORD
value: "passwd123" # 密码
- name: WG_PORT
value: "31820"
- name: WG_CONFIG_PORT
value: "31824"
- name: WG_DEFAULT_ADDRESS
value: "10.0.8.x" # 默认的 WireGuard 地址
- name: WG_DEFAULT_DNS
value: "114.114.114.114" # 默认 DNS
- name: WG_ALLOWED_IPS
value: "10.0.8.0/24" # 允许的 IP 范围
- name: WG_PERSISTENT_KEEPALIVE
value: "25" # Keepalive 时间
- name: UI_CHART_TYPE
value: "1"
volumeMounts:
- name: wg-easy-config
mountPath: /etc/wireguard
ports:
- containerPort: 51820
protocol: UDP
- containerPort: 51821
protocol: TCP
securityContext:
capabilities:
add: ["NET_ADMIN", "SYS_MODULE"]
resources: {}
volumes:
- name: wg-easy-config
persistentVolumeClaim:
claimName: wg-easy-pvc # 使用 PVC
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: wg-easy-service
namespace: wireguard # 在 wireguard 命名空间中创建 Service
spec:
selector:
app: wg-easy
ports:
- protocol: UDP
port: 51820
targetPort: 51820
nodePort: 31820 # 指定 NodePort 的端口号
name: wireguard-udp
- protocol: TCP
port: 51821
targetPort: 51821
nodePort: 31821 # 指定 NodePort 的端口号
name: wireguard-webui
type: NodePort # 使用 NodePort 类型
需要注意的是:
- WG_HOST环境变量的配置,在K8S当中配置成为IP并不合适,因为K8S当中不一定会将这个Pod调度哪台宿主机器,适合配置成为域名的方式。
- WG_PORT环境变量的配置,要和Service的
wireguard-udp
的nodePort
端口一致,因为WG_PORT的配置实际上是用于wg的客户端配置文件的生成,并不会影响Wireguard在哪个端口启动,Wireguard默认在51820端口暴露,好像wg-easy没有提供修改Wireguard的默认51820的配置。 - PORT环境变量配置的是Pod当中Wireguard的WebUI页面的启动端口,默认是51821,没必要修改,通过Service映射成为31821的端口。
- 最终生成客户端的配置文件,连接Wireguard服务的地址,是用的
WG_HOST:WG_PORT
的格式进行生成的,如果出现连不上的情况,需要检查这两个配置是否正确。
部署之后,可以通过访问机器的31821
端口进入到Wireguard的后台管理页面,使用方式,以及客户端的连接,和上面的Docker的使用方式一样。
如果后台页面需要使用HTTPS的方式访问(推荐使用,Wireguard使用HTTPS可以最大程度保证安全性,因为Wireguard管理页面密码泄露,意味着其他人都可以新增Wireguard访问我们的内网中的服务),或者想要通过Ingress进行对外访问,可以新增一条Ingress路由配置。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: wireguard-nginx-ingress
namespace: wireguard
spec:
ingressClassName: nginx
tls:
- hosts:
- {domain}
secretName: {ssl-secret}
rules:
- host: {domain} # 指定当访问这个域名时才将请求交给下面的Service
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: wg-easy-service # 指定要路由到的 Service 名称
port:
number: 51821 # 这里需要配置Service暴露的端口号, 而不是NodePort的端口号
3.3 wg-easy总结
使用wg-easy安装Wireguard最大的优点,就是它真的特别简单,容器部署成功之后,只需要在页面上新建客户端,接着只需要扫码/导入配置文件,即可轻松接入Wireguard。
但是wg-easy对于爱折腾的人来说,它也会存在有一些问题,扩展性不够高,比如我想要自定义Wireguard的PostUp和PreDown脚本时,wg-easy似乎没有给我们提供相关的功能,但是wg0配置文件还是交给wg-easy进行管理的,我们又不能自定义wg0配置文件。
wg-easy使用简单的密码登录,但是作为Wireguard这种VPN工具来说,安全也很重要,可以考虑接入动态OTP的方式去提高安全性。
如果想要兼容简单、安全、扩展性好,可以使用下面我们会介绍到的wgdashboard工具,这个工具会牺牲一定的简单的特性,但是安全和扩展性都已经做的比较好。
4. 通过WGdashboard安装Wireguard
wgdashboard是Github一个开源项目,提供了好看的WebUI作为后台Wireguard的管理后台页面,方便我们通过图形化的方式去管理Wireguard,并且其安全性做的很好,支持接入OTP动态验证码提供身份认证。
具体可以参考Github:Github-donaldzou/WGDashboard
4.1 通过Docker方式安装wgdashboard
Docker解决方案参考WGDashboard-docker
只需要通过如下的命令,即可轻松配置wgdashboard。
docker run -d \
--name wgdashboard \
--restart unless-stopped \
-e enable=wg0 \
-e isolate=wg0 \
-p 10086:10086/tcp \
-p 51820:51820/udp \
-v ~/data:/data \
-v ~/conf:/etc/wireguard \
--cap-add=SYS_MODULE \
--sysctl="net.ipv4.conf.all.src_valid_mark=1" \
--sysctl="net.ipv4.ip_forward=1" \
--cap-add NET_ADMIN \
donaldzou/wgdashboard:latest
4.2 通过K8S部署wgdashboard
使用如下的K8S资源清单去部署WgDashboard:
apiVersion: v1
kind: Namespace
metadata:
name: wireguard
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wireguard-dashboard-data-pvc
namespace: wireguard # 在 wireguard 命名空间中创建 PVC
spec:
accessModes:
- ReadWriteMany # NFS 允许多个节点同时访问
resources:
requests:
storage: 10Gi # 需要的存储大小
storageClassName: k8s-nfs-storage # 使用 nfs-storage storage class
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wireguard-dashboard-config-pvc
namespace: wireguard # 在 wireguard 命名空间中创建 PVC
spec:
accessModes:
- ReadWriteMany # NFS 允许多个节点同时访问
resources:
requests:
storage: 10Gi # 需要的存储大小
storageClassName: k8s-nfs-storage # 使用 nfs-storage storage classapiVersion: v1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wireguard-dashboard
namespace: wireguard # 在 wireguard 命名空间中创建 Deployment
labels:
app: wireguard-dashboard
spec:
replicas: 1
selector:
matchLabels:
app: wireguard-dashboard
template:
metadata:
labels:
app: wireguard-dashboard
spec:
initContainers:
- name: set-systemctl
image: harbor.wanna1314y.top/library/busybox
command:
- "sh"
- "-c"
- |
sysctl -w net.ipv4.conf.all.src_valid_mark=1
sysctl -w net.ipv4.ip_forward=1
securityContext:
privileged: true
imagePullPolicy: Always
containers:
- name: wireguard
image: harbor.wanna1314y.top/linuxserver/wgdashboard:latest
env:
- name: enable
value: "wg0"
- name: isolate
value: "wg0"
- name: public_ip
value: "xxx.yyy.com"
volumeMounts:
- name: host-time
readOnly: true
mountPath: /etc/localtime
- name: wireguard-dashboard-config # wg config
mountPath: /etc/wireguard
- name: wireguard-dashboard-data # wg data
mountPath: /data
ports:
- containerPort: 31820
protocol: UDP
- containerPort: 31821
protocol: UDP
- containerPort: 31822
protocol: UDP
- containerPort: 10086
protocol: TCP
securityContext:
capabilities:
add: ["NET_ADMIN", "SYS_MODULE"]
resources: {}
readinessProbe: # 就绪探测
httpGet:
path: / # 根据应用的配置,调整探测的路径
port: 10086
scheme: HTTP
initialDelaySeconds: 60 # 容器启动后探测的延迟时间
periodSeconds: 5 # 探测间隔
timeoutSeconds: 3 # 探测超时时间
successThreshold: 1 # 成功次数阈值
failureThreshold: 3 # 失败次数阈值
livenessProbe: # 存活探测
httpGet:
path: / # 根据应用的配置,调整探测的路径
port: 10086
scheme: HTTP
initialDelaySeconds: 300 # 容器启动后探测的延迟时间
periodSeconds: 10 # 探测间隔
timeoutSeconds: 3 # 探测超时时间
successThreshold: 1 # 成功次数阈值
failureThreshold: 3 # 失败次数阈值
volumes:
- name: host-time
hostPath:
path: /etc/localtime
type: ''
- name: wireguard-dashboard-config
persistentVolumeClaim:
claimName: wireguard-dashboard-config-pvc # 使用 PVC
- name: wireguard-dashboard-data
persistentVolumeClaim:
claimName: wireguard-dashboard-data-pvc # 使用 PVC
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: wireguard-dashboard-service
namespace: wireguard # 在 wireguard 命名空间中创建 Service
spec:
selector:
app: wireguard-dashboard
ports:
- protocol: UDP
port: 31820
targetPort: 31820
nodePort: 31820 # 指定 NodePort 的端口号
name: wireguard-udp
- protocol: TCP
port: 10086
targetPort: 10086
nodePort: 30086 # 指定 NodePort 的端口号
name: wireguard-dashboard
type: NodePort # 使用 NodePort 类型
接着,可以访问30086端口去访问Web页面,当然也可以部署Ingress实现HTTPS访问。
4.3 客户端接入wgdashboard服务
部署好之后,使用admin作为账号和密码进行登录,接着注册账号和密码之后,可以进入到如下的WGDashboard后台管理页面。
可以新增Wg配置文件:
下方的高级设置当中,还可以自定义PostUp/PreDown等脚本,如果需要进行更多的自定义的话,可能需要用到这个自定义脚本。
接着,可以点击左侧的wg1,进入到wg1的配置文件的管理页面当中来,wg1默认是没生效的,可以点击右上角的Status按钮,让当前配置文件生效。
如果后期需要要修改Wg服务器的配置信息,那么可以点击右下角的设置,去修改Wg服务器相关的配置信息。
如果想要新增连接的终端设备的话,可以点击左下角的Peer去进行新增,接着进入到下面这样的Peer配置页面。
编辑完成之后,点击Add新增一个Peer,回到主页。
在主页当中,可以点击下载、二维码、查看当前Peer的配置文件信息,对于一般的Linux系统、Mac系统之类的可以使用下载的方式,对于使用Wg的移动端(Android/IOS),可以使用二维码的方式进行配置文件的加载。
对于端点Peer的配置规则如下:
- "允许的IP地址"(
AllowedIPs
)用于生成服务端的Peer当中的AllowedIPs和客户端的Address。- 其实就是,对于Wireguard服务端来说,这个Peer需要处理哪些IP网段的报文。比如
10.0.9.3/32,10.168.1.0/24
,就代表这个Peer需要处理这两个网段的网络交互,对于一个10.168.1.100
的请求,Wireguard服务端就会根据网段去定位到这个客户端,将流量转发到这个Wireguard客户端。 - 需要注意的是,这个网段,不要和客户端本机的其他网卡的网段重复,不然可能会导致其他网段的网卡不能正常生效,全部都走的Wireguard的虚拟网卡。
- 其实就是,对于Wireguard服务端来说,这个Peer需要处理哪些IP网段的报文。比如
- "终结点允许的IP地址"(
Endpoint AllowedIPs
)用于生成客户端的AllowedIPs。- 其中就是,对于Wireguard客户端来说,这个Peer需要处理哪些IP网段的报文。
5. 基于Wireguard实现外部访问家里内网(内网穿透)
需求就是:我在外网有一个设备(比如手机、电脑),想要去访问我家里的10.168.1.0/24
这个内网网段的机器。
整体的网络结构图如下:
Wireguard内网网段:10.0.8.0/24
,家里的网络的网段:10.168.1.0/24
。现在想要实现的是:外网想要访问家里的10.168.1.0/24
网段的网络。
我们来思考一下:既然10.0.8.2
、10.0.8.3
、10.0.8.4
这三个IP,都已经在Wireguard虚拟网络当中可以互相访问,10.0.8.4
这台机器又可以访问家里的10.168.1.0/24
这整个网络,那么理论上我们可以通过转发的方式实现,10.0.8.2
也可以访问10.168.1.0/24
的机器。
我们下面的介绍,都是基于最终生成的配置文件wg0.conf的内容去进行的介绍,如果是使用的WGDashboard进行的介绍,在对应的Web页面参照wg0.conf的配置信息进行对应的配置即可。
5.1 Wireguard服务端配置允许访问家里内网的网段
对于服务端来说,我们需要将10.168.1.0/24
这个内网网段的流量,全部都转发到10.8.0.4
这台机器上,我们找到这台机器的Wireguard的Peer配置,往AllowedIPs
当中新增配置项10.168.1.0/24
,这样服务端的Wireguard虚拟网卡接口wg0
,在接收到流量时,查找路由表时,就可以找到10.0.8.4/32
这台机器能同时处理10.0.8.4/32,10.168.1.0/24
这两个网段的网络。
Wireguard的服务端配置wg0.conf文件:
# Server
[Interface]
PrivateKey = ......
Address = 10.0.8.1/24
ListenPort = 51820
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; iptables -I FORWARD -s 10.0.8.0/24 -i wg0 -d 10.0.8.0/24 -j ACCEPT; iptables -I FORWARD -s 10.0.8.0/24 -i wg0 -d 10.168.1.0/24 -j ACCEPT; iptables -I FORWARD -s 10.168.1.0/24 -i wg0 -d 10.0.8.0/24 -j ACCEPT;
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; iptables -D FORWARD -s 10.0.8.0/24 -i wg0 -d 10.0.8.0/24 -j ACCEPT; iptables -D FORWARD -s 10.0.8.0/24 -i wg0 -d 10.168.1.0/24 -j ACCEPT; iptables -D FORWARD -s 10.168.1.0/24 -i wg0 -d 10.0.8.0/24 -j ACCEPT;
# Client: gmk (640e7741-430f-4ffe-bae0-3320cbbfe14a)
[Peer]
PublicKey = ...
PresharedKey = ...
AllowedIPs = 10.0.8.4/32,10.168.1.0/24
我们还得新增两条iptables规则,iptables -I FORWARD -s 10.0.8.0/24 -i wg0 -d 10.168.1.0/24 -j ACCEPT; iptables -I FORWARD -s 10.168.1.0/24 -i wg0 -d 10.0.8.0/24 -j ACCEPT;
,用于支持两个网段的流量之间进行转发时可以互通。
5.2 内网服务器配置允许访问家里内网
我们来想象一下10.0.8.2
这台机器请求10.168.1.101
这个IP地址流量的流转过程:
- 1.流量在
10.8.0.2
这台机器上,通过之前与Wireguard
服务端的UDP协议51820端口建立的Socket(这中间其实也会涉及到很多层的路由器的跳转,我们这里简化),将数据包发送给10.0.8.1
这台机器(也就是公网IP机器)。 - 2.首先会将流量经过Wireguard服务端,Wireguard服务端会将流量通过
10.0.8.4(10.168.1.100)
这台机器进行转发,并将sourceIp
修改为服务器的公网IP。 - 3.流量达到
10.0.8.4(10.168.1.100)
这台家里的服务器上,此时sourceIp
为Wireguard服务端的公网IP地址,destinationIp
还是10.168.1.102
,在这台机器上会查找路由表,找到另外一个网卡接口有处理10.168.1.0/24
这个网段的能力,将流量从这个网卡接口流出。 - 3.流量到达
10.168.1.101
这台机器,这台机器在处理请求的数据包之后,正常来说接着响应数据包会重新流转到10.0.8.4(10.168.1.100)
这台家里的服务器上。- 但是这个流程存在有一个问题,那就是响应报文的
sourceIp
为10.168.1.101
,destinationIp
为服务器的公网IP,但是**10.168.1.101
这台机器并未和公网机器建立连接,正确的结果是它需要将报文回给10.0.8.4(10.168.1.100)
这台机器**。解决这个问题有两个方案:(1)在上一个阶段,在10.0.8.4(10.168.1.100)
机器将流量转发给10.168.1.101
这台机器之前,我们将sourceIp
修改成为10.168.1.100
,这样在最终处理响应报文时10.168.1.101
就能自动识别到目标地址是10.168.1.100
,从而将报文转发送给10.168.1.100
。(2)在10.168.1.101
这台机器上,使用NAT手动将公网IP的目标地址转换成为10.168.1.100
,但是这意味着每台机器都得修改。因此使用第一种方案,是最为合理的。
- 但是这个流程存在有一个问题,那就是响应报文的
- 5.响应报文到达
10.0.8.4(10.168.1.100)
这台机器,这台机器通过之前和公网服务器建立的Socket,将流量重新转发给公网服务器10.0.8.1
,此时sourceIp
为10.168.1.100
,destinationIp
为公网服务器的IP。 - 6.流量到达Wireguard服务端,也就是公网服务器的50820端口,公网服务器Wireguard服务,通过之前和
10.8.0.2
这台机器建立的Socket(这中间其实也会涉及到很多层的路由器的跳转,我们这里简化),将流量重新返回给10.8.0.2
这台机器。 - 7.最终结果:
10.8.0.2
这台机器收到了来自10.168.1.101
内网机器的响应。
因此我们需要家里内网(gmk)的机器的Wireguard配置:
[Interface]
PrivateKey = .....
Address = 10.0.8.4/24
DNS = 114.114.114.114
PostUp = iptables -t nat -A POSTROUTING -s 10.0.8.0/24 -j SNAT --to-source 10.168.1.100
PostDown = iptables -t nat -D POSTROUTING -s 10.0.8.0/24 -j SNAT --to-source 10.168.1.100;
[Peer]
PublicKey = ...
PresharedKey = .....
AllowedIPs = 10.0.8.0/24
PersistentKeepalive = 25
Endpoint = xxx.yyy.com:51820
主要修改一个地方,就是新增iptables规则,实现在内网之间的流量转发时,将sourceIp
修改成为当前机器的IP,适用于内网的IP地址不会变更的情况。
PostUp = iptables -t nat -A POSTROUTING -s 10.0.8.0/24 -j SNAT --to-source 10.168.1.100
PostDown = iptables -t nat -D POSTROUTING -s 10.0.8.0/24 -j SNAT --to-source 10.168.1.100;
也可以指定如下的内容,通过-j MASQUERADE
直接修改sourceIp
为eth0
网卡的IP,好处是可以如果机器的IP地址发生变更,那么也能动态的探测内网的IP地址,比如使用了动态的DHCP服务的时候。
PostUp = iptables -t nat -A POSTROUTING -s 10.0.8.0/24 -o eth0 -j MASQUERADE;
PostDown = iptables -t nat -D POSTROUTING -s 10.0.8.0/24 -o eth0 -j MASQUERADE;
5.3 外网访问的客户端接入家里内网
外网需要访问家里内网的机器的Wireguard客户端配置,需要配置AllowedIPs
,将10.168.1.0/24
也放入到其中,标识对于10.168.1.0/24
这个网段的流量,也将它转发给Wireguard服务器进行流量的转发,不然凭空产生的10.168.1.0/24
的流量,机器当然是无法处理的。
[Interface]
PrivateKey = ......
Address = 10.0.8.3/24
DNS = 114.114.114.114
[Peer]
PublicKey = ......
PresharedKey = ......
AllowedIPs = 10.0.8.0/24,10.168.1.0/24
PersistentKeepalive = 25
Endpoint = xxx.yyy.com:51820
修改配置之后,需要重新启动Wireguard客户端。
sudo wg-quick down ./wg0.conf
sudo wg-quick up ./wg0.conf
对于移动端(IOS/Android)的Wireguard客户端,可以直接在Wireguard客户端的配置页面去修改AllowedIPs
。
5.4 K8S集群的容器组IP接入Wireguard VPN
如果我们想要在外网访问K8S集群当中的各个容器组IP的话,我们可以创建一个Pod,让这个Pod去接入Wireguard服务端,从而桥接整个K8S集群的容器组IP。
内部的K8S集群的IP地址网段CIDR是10.233.64.0/18
,具体配置可以参考如下的资源清单:
apiVersion: v1
kind: Namespace
metadata:
name: linux
---
kind: ConfigMap
apiVersion: v1
metadata:
name: k8s-cluster-vpn-internet
namespace: linux
annotations:
kubesphere.io/creator: admin
kubesphere.io/description: ''
data:
wg0.conf: >
[Interface]
PrivateKey = {客户端私钥}
Address = 10.0.8.12/32
MTU = 1420
DNS = 114.114.114.114
PostUp = iptables -t nat -A POSTROUTING -s 10.233.64.0/18 -o eth0 -j
MASQUERADE;
PostUp = iptables -t nat -D POSTROUTING -s 10.233.64.0/18 -o eth0 -j
MASQUERADE;
[Peer]
PublicKey = {服务端公钥}
AllowedIPs = 10.0.8.0/24
Endpoint = xxx.yyy.com:51820
PersistentKeepalive = 25
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wg-k8s-vpn-internet
namespace: linux
labels:
app: wg-k8s-vpn-internet
spec:
replicas: 1
selector:
matchLabels:
app: wg-k8s-vpn-internet
template:
metadata:
labels:
app: wg-k8s-vpn-internet
spec:
initContainers:
- name: set-systemctl
image: harbor.wanna1314y.top/library/busybox
command:
- "sh"
- "-c"
- |
sysctl -w net.ipv4.conf.all.src_valid_mark=1
sysctl -w net.ipv4.ip_forward=1
securityContext:
privileged: true
imagePullPolicy: Always
containers:
- name: wireguard
image: harbor.wanna1314y.top/linuxserver/wireguard:latest
command:
- "sh"
- "-c"
- "wg-quick up wg0 && tail -f /dev/null"
volumeMounts:
- name: host-time
mountPath: /etc/localtime
- name: vpn-config
mountPath: /etc/wireguard/wg0.conf
readOnly: true
subPath: wg0.conf # 这里将 wg0.conf 挂载到容器的 /etc/wireguard/wg0.conf
securityContext:
capabilities:
add: ["NET_ADMIN", "SYS_MODULE"]
resources: {}
volumes:
- name: host-time
hostPath:
type: ''
path: /etc/localtime
- name: vpn-config
configMap:
name: k8s-cluster-vpn-internet
我们使用ConfigMap的方式,将Wireguard的客户端wg0配置文件挂载到Pod当中,接着在Pod当中,运行Wireguard客户端服务,去连接Wireguard服务端。
我们仅仅展示桥接的客户端的配置信息(对应5.2
部分),对于Wireguard服务端的配置信息和外放访问K8S集群的客户端信息配置,参照上面我们介绍的5.1
和5.3
这两个部分。
5.5 通过前面提到的多种方式进行接入时的注意点
使用官方原始的方式接入Wireguard注意点
没什么问题,就是无法做到可视化,得自己修改配置文件,无论什么功能都能实现。
通过wg-easy的方式接入Wireguard注意点
说明一下:因为wg-easy实现的是简单,因此没有提供我们改服务端的wg0配置文件的方式,但是我们上面的方式,是需要修改服务端的wg0配置文件进行定制化的,因此通过wg-easy实现不了这个功能。
通过wgdashboard的方式接入Wireguard注意点:
wgdashboard接入时,会有两个问题:
- 1.wgdashboard在进行Peer的生成时,服务端对于客户端的Peer配置
AllowedIPs
和客户端的Address
是同一个值。比如10.0.8.4(10.168.1.100)
这台机器的配置就会是10.0.8.4/32,10.168.1.0/24
。但是如果是同一个值的话,会导致Wireguard客户端处理的网段,和内网的网段冲突,会导致内网中无法访问该设备,只有通过Wireguard连上VPN之后才能访问使用内网IP访问10.168.1.100
。因此方案是:在下载配置文件之后,手动修改Address
,将10.168.1.0/24
这个网段删掉就行。 - 2.无法定义客户端的wg0配置文件的
PostUp
相关的钩子回调,只能在下载配置文件之后,手动新增PostUp
和PostDown
,并添加iptables规则。
主要就上面的两个问题,其实问题不大,都只是10.0.8.4(10.168.1.100)
这一台机器上的配置文件,需要下载后修改一下。