安装
配置解析
WireGuard的配置示例
[Interface] # 路由器(家里)
PrivateKey = 2FRm2lH1JAePxk9/fYOgv622y/W/2KYq4atwihOQom4=
Address = 192.168.100.1/32
ListenPort = 51820
MTU = 1280
DNS = 1.1.1.1, 8.8.8.8
PreUp = ip rule add ipproto tcp dport 22 table 1234
PostUp = wg set %i private-key /etc/wireguard/wg0.key <(some command here)
PreDown = echo "$(date +%s) WireGuard Going Down" >> /var/log/wireguard.log
PostDown = echo "$(date +%s) WireGuard Going Down" >> /var/log/wireguard.log
[Peer]
PublicKey = t4sXRq2YT4gRyr1s2TKndFuP3hYyZ1AuU01zAUiuSw0=
Endpoint = home.example.com:51820
AllowedIPs = 192.168.5.0/24, 192.168.6.0/24, 192.168.100.0/2
PersistentKeepalive = 25
[Peer]
PublicKey = M1coQS07FKAOzoq3i07ZrFQZJhbHCPurGuXDKewMk10=
AllowedIPs = 192.168.100.12/32接口
接口IP
[Interface]
Address = 192.168.100.1/32就是自身在虚拟局域网中的IP。应该包含网络范围。一般地,IPv4使用的子网前缀是32。
节点
对端
[Peer] # 对方在互联网
Endpoint = home.example.com:51820[Peer] # 对方在局域网
Endpoint = 192.168.5.1:51820指对方的网络地址,值为IP + 端口。可以使用IP或域名。
INFO
只需在对方能直接连接时使用。适用于在内网(物理)或有公网IP的对端。
允许的IP
[Peer]
AllowedIPs = 192.168.5.1/32, 192.168.6.0/24, 192.168.100.0/24功能:引导流量发送到VPN,限制VPN流量进入。
- 对于从本地发送到网络的流量,如果目标IP在 “允许的IP”中,则会引导流量进入VPN。
- 对于接收到的数据包,会检查来源IP,如果在“允许的IP”中,就接受,否则丢弃(忽视)。
对各种类型的对端应使用的值
- 终端:它的 VPN IP
- 路由器(无公网 IP):它的 VPN IP;如果要其内网设备能被远程访问,应该包含它的物理局域网IP段。
- 路由器(有公网 IP):所有IP段
INFO
同一个设备的配置中,不同对端的AllowedIPs不应该重叠。如果重叠,冲突的网段仅对顺序向下的第一个对端生效。
WARNING
如果在某个运行了WG的自己的路由器下,不应该将其网段包含在任何对端的AllowedIPs中。否则会造成本地流量的绕路、不必要的加、解密。
总的来说,只要让这项的值为,对方会使用的IP即可。
连接保活间隔
[Peer]
PersistentKeepalive = 25关:如果没有数据需要传输,就没有任何网络活动。
开:间隔一段时间,在隧道中传输一些数据。让外界看起来一直有网络活动
WG默认的行为:启动后,没有数据传输就不建立连接;不保活。
这可能产生一些问题:
- 隧道刚启动时不可用(我也不清楚)
- 长时间的沉默后需要重连,这导致首批数据包的延迟。
- 在调试时,在启动后,不能立刻知道能否连通。
PreUp, PostUp, PreDown, PostDown
功能:在指定的时机执行指定的命令。可以指定多个相同的键,按顺序执行。
PreUp: 在建立接口前执行命令PostUp: 在建立接口后执行命令PreDown: 在移除接口前执行命令PostDown: 在移除接口后执行命令
创建配置
INFO
假设: 你会在家里和宿舍间活动,并且有一些设备要接入VPN。
网络范围
- 家里:
192.168.5.X/24 - 宿舍:
192.168.6.X/24 - VPN:
192.168.100.X/24
设备 VPN IP
- 路由器(家里):
192.168.100.1 - 路由器(宿舍):
192.168.100.2 - 笔记本:
192.168.100.10 - 手机:
192.168.100.11 - 电脑:
192.168.100.12
创建密钥对
WireGuard for Windows
WireGuard > 新建隧道 > 新建空隧道,然后就可以复制弹窗中的公私钥。
路由器
[Interface] # 路由器(家里)
PrivateKey = 2FRm2lH1JAePxk9/fYOgv622y/W/2KYq4atwihOQom4=
Address = 192.168.100.1/32
ListenPort = 51820
[Peer] # 路由器(宿舍)
PublicKey = MfGgsYp++kB43L8SQ3db6K19xwCckJz8YQdqvvqJFjI=
AllowedIPs = 192.168.100.2/32, 192.168.6.0/24
[Peer] # 笔记本
PublicKey = Aqo/uxzO2HN0HFLO8mB67bBU9C47P1H5e7P7SWvPLB8=
AllowedIPs = 192.168.100.10/32
[Peer] # 手机
PublicKey = +4AoJPnaouLk1Sbubc2Mo6WQKCtm7wDefU0Fw11FFQI=
AllowedIPs = 192.168.100.11/32
[Peer] # 电脑
PublicKey = M1coQS07FKAOzoq3i07ZrFQZJhbHCPurGuXDKewMk10=
AllowedIPs = 192.168.100.12/32解析
为了客户端连接方便,应该采用固定的端口。
因为它有公网环境,所以它应该是所有设备交换的中枢。因为每个设备都会主动连接它,所以它的配置文件应该包含对端的信息。
对于笔记本、手机、电脑,因为它们没有公网IP,就不用设置它们的对端地址。等它们主动连接即可。
允许的IP(路由的IP):此设备的VPN IP。
为了立刻建立VPN连接、持续保持连接,应该启用保活。即设置保活间隔。
手机
[Interface] # 手机
PrivateKey = ILaRZ4y8+ztDGZXWVFpuFUcK8Fmh1C7q4KAmJXFisXM=
Address = 192.168.100.11/32
[Peer] # 路由器(家里)
PublicKey = t4sXRq2YT4gRyr1s2TKndFuP3hYyZ1AuU01zAUiuSw0=
Endpoint = home.example.com:51820
AllowedIPs = 192.168.5.0/24, 192.168.6.0/24, 192.168.100.0/24
PersistentKeepalive = 25因为它没有公网IP,不会有其他设备主动连接它。所以它的配置文件只需包含路由器(家里)
允许的IP(路由的IP):应为家里的网段、宿舍的网段、VPN 网段。 即发往这3个区域的流量都有家里的路由器处理。家里的路由器会转发数据到正确的地方。
为了立刻建立VPN连接、持续保持连接,应该启用保活。即设置保活间隔。
路由器(宿舍)
[Interface] # 路由器(宿舍)
PrivateKey = iCZM3AjBuCnWpB6H1uG6ICzrKYfFq+lBVMem1ZObL2g=
Address = 192.168.100.2/32
[Peer] # 路由器(家里)
PublicKey = t4sXRq2YT4gRyr1s2TKndFuP3hYyZ1AuU01zAUiuSw0=
Endpoint = home.example.com:51820
AllowedIPs = 192.168.5.0/24, 192.168.6.0/24, 192.168.100.0/24
PersistentKeepalive = 25笔记本
[Interface] # 笔记本
PrivateKey = ILaRZ4y8+ztDGZXWVFpuFUcK8Fmh1C7q4KAmJXFisXM=
Address = 192.168.100.10/32
[Peer] # 路由器(家里)
PublicKey = t4sXRq2YT4gRyr1s2TKndFuP3hYyZ1AuU01zAUiuSw0=
Endpoint = home.example.com:51820
AllowedIPs = 192.168.5.0/24, 192.168.6.0/24, 192.168.100.0/24
PersistentKeepalive = 25电脑
[Interface] # 电脑
PrivateKey = uHxEh+4NZK1MY6VXckHDuI0XItwsWGUp8fMTYxercXg=
Address = 192.168.100.12/32
[Peer] # 路由器(家里)
PublicKey = t4sXRq2YT4gRyr1s2TKndFuP3hYyZ1AuU01zAUiuSw0=
Endpoint = home.example.com:51820
AllowedIPs = 192.168.5.0/24, 192.168.6.0/24, 192.168.100.0/24
PersistentKeepalive = 25导入配置
Windows
主界面 > 新建隧道
OpenWRT
WebUI > 网络 > 接口 > wg > 编辑 > 对端 > 添加对端
手动输入信息。
Android
打开软件(WireGuard),点击右下角的 +
通过打洞连接无公网 IP v4 的对端
前情提要:如果你很不幸,没有公网IPv4;但又幸运地获得了较好的 NAT 环境:完全锥型。那么可以试试本方案。
INFO
原理:通过STUN打开一个上层(公网)到本机的通道;通过DDNS和对象存储获得最新的通道入口。
在OpenWRT安装、启动插件Lucky。在其添加一个STUN规则
类型:UDP;目标地址:127.0.0.1;目标端口:OP上 WireGuard 的监听端口;
自动化更新 通道信息
完成上述操作,已经可以通过IP v4 连通对端了。只是手动获取信息再修改有点麻烦。
如果能什么都不用做就好了。
方案选择
- DNS 法:用TXT记录保存"IP:端口"。通过解析指定的TXT记录获得端口。
- 文件法: 保存端口信息到文件中。使得此文件能被从互联网下载。通过读它的内容来读端口。
DNS 法
Lucky > 动态域名 > 添加任务: 完成"DNS服务商设置", 添加同步记录,
- 记录名"wg.example.com"
- 记录类型:"TXT",
- 记录内容:
{ipv4Addr}:{STUN_name}# Lucky可以引用其模块STUN获得的端口, 变量名: "STUN_" + STUN规则名
文件法
需要通过某种手段将当前端口保存在某个文件、能在IP v4 通过某个URL下载它。内容示例:
721设置脚本
这里提供一套我编写的脚本,功能是推送最新STUN信息。
放置这些脚本到Linux的某个位置(比如/etc/script):STUN 脚本, 邮件通知脚本, 邮件账号
使用说明:
account.yml:填写邮箱信息stun_update.sh:填写 Alist 账号密码。如果需要设置Clash配置文件,还可以设置STUN规则名称与节点名称的映射关系。case的选项是STUN规则名称,Edit的参数是节点名。
调用脚本
Lucky > STUN > 添加规则: - 穿透通道本地端口: 0 # 随机端口可以使能手动触发更新 - 目标地址: WireGuard 服务器的 IP. 如果WG和插件都在OpenWRT,则是 "127.0.0.1" - 自定义脚本触发:开; - 自定义脚本:bash /etc/script/stun_update.sh ${ruleName} ${time} ${ip} ${port} ${ipAddr}
Wireguard for Windows
用PowerShell执行以下命令:允许 WireGuard 执行脚本;允许 PowerShell 执行本地脚本。
reg add HKLM\Software\WireGuard /v DangerousScriptExecution /t REG_DWORD /d 1 /f # 允许 WireGuard 执行脚本
set-executionpolicy remotesigned # 允许 PowerShell 执行本地脚本WARNING
请注意,此执行是以本地系统用户身份完成的,该用户以操作系统上的最高权限运行,因此是恶意软件的真正目标。因此,您应该以最大的恐惧启用此选项。
保存以下内容为PowerShell脚本(后缀为ps1)。至于位置,可以自己设定。修改其中的关键部分,比如URL,接口信息。
更新 EndPoint 的端口的脚本
# $Log_Path = "$env:TEMP/WireGuard_Helper.log"
# Start-Transcript -Path $Log_Path
function Test-PublicIPv6Address {
<#
.SYNOPSIS
检查设备自身是否拥有公网 IPv6 地址。
.DESCRIPTION
此函数通过查询本地网络适配器的 IPv6 地址,并排除私有地址范围(Unique Local Addresses 和 Link-Local Addresses),
来判断设备是否配置了公网 IPv6 地址。
.EXAMPLE
Test-PublicIPv6Address
# 输出: True (如果找到公网 IPv6 地址)
# 输出: False (如果没有找到公网 IPv6 地址)
.EXAMPLE
if (Test-PublicIPv6Address) {
Write-Host "设备拥有公网 IPv6 地址。"
} else {
Write-Host "设备没有公网 IPv6 地址。"
}
#>
[CmdletBinding()]
param (
# No parameters needed for this function as it checks the local machine.
)
try {
# 获取所有 IPv6 地址
$IPv6Addresses = Get-NetIPAddress -AddressFamily IPv6 -ErrorAction Stop
if ($IPv6Addresses) {
foreach ($IPAddressObject in $IPv6Addresses) {
$ip = $IPAddressObject.IPAddress
# 排除私有 IPv6 地址范围
if (
$ip -ne "::1" -and # 排除 Loopback 地址 (虽然 fe8* 也排除了 loopback,但显式排除更清晰)
$ip -ne "::" -and # 排除 未指定地址 (虽然 fe8* 也排除了 unspecified,但显式排除更清晰)
$ip -notlike "fe8*" -and # 排除 Link-Local 地址 (fe80::/10)
$ip -notlike "fc*" -and # 排除 Unique Local Addresses (fc00::/7)
$ip -notlike "fd*" # 排除 Unique Local Addresses (fc00::/7)
) {
# 找到非私有 IPv6 地址,认为是公网 IPv6 地址
return $true
}
}
}
# 没有找到公网 IPv6 地址
return $false
}
catch {
# Get-NetIPAddress 可能出错,例如没有网络适配器或者权限问题
Write-Warning "获取 IPv6 地址时出错: $_"
return $false # 出错时也返回 False,表示无法确定是否有公网 IPv6
}
}
function Resolve-EndpointFromTxtRecord {
param(
[Parameter(Mandatory = $true)]
[string]$Domain
)
Write-Host "正在查询域名 '$Domain' 的 TXT 记录..."
try {
# 查询TXT记录
$txtRecord = Resolve-DnsName -Name $Domain -Type TXT -ErrorAction Stop
# 提取TXT记录字符串 (假设只有一个TXT记录且是我们需要的)
$ipAndPortString = $txtRecord.Strings -join '' # 将字符串数组合并成一个字符串
# 解析IP和端口
if ($ipAndPortString -match "^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)$") {
$ip = $Matches[1]
$port = $Matches[2]
$endpoint = "${ip}:$port"
Write-Host "成功从TXT记录获取到IP: $ip, 端口: $port"
return $endpoint # 返回解析出的endpoint字符串
}
else {
Write-Error "无法解析TXT记录字符串 '$ipAndPortString' 格式为 'IP:端口' 。"
return $null # 解析失败返回 null
}
}
catch {
Write-Error "查询TXT记录失败或域名不存在: $($_.Exception.Message)"
return $null # 查询失败返回 null
}
}
function Get-EndpointFromFileUrl {
$url = "https://ddns-0721.cos.ap-chengdu.myqcloud.com/WireGuard.txt"
$port = Invoke-RestMethod -Uri $url
return "home.example.xyz:$port"
}
function Get-EndpointFromTxtRecord {
$domain = "wg.example.xyz"
$endpoint = Resolve-EndpointFromTxtRecord -Domain $domain
if (-not $endpoint) { Write-Error "获取 endpoint 失败。" ; exit 1 }
return $endpoint
}
function Set-Endpoint {
$Endpoint = Get-EndpointFromTxtRecord
# wg set <接口名称> peer <对端公钥> <属性> <值>
wg set "test2" peer "dz0cYzp5TuQ3IniYmDHLLBYDY64bRtKkBsqEvLnJzB0=" endpoint $Endpoint
}
if (Test-PublicIPv6Address) {
Write-Host "设备拥有公网 IPv6 地址。"
}
else {
Write-Output "设备没有公网 IPv6 地址。"
Set-Endpoint
}
# Stop-Transcript
# Pause在 WireGuard 的配置的节 Interface 下添加一个 接口建立后要执行的命令,内容是执行此脚本。
[Interface]
PostUp = powershell "C:\Users\用户名\script\WG_Update.ps1"Wireguard for Android
使用此分支以支持TXT记录:zhcode-fun/wireguard-android
完成
试试让 WireGuard 启动连接,看看是否生效吧。
解释
NAT
- 全称:网络地址转换
- 功能:在公网IPv4用尽的情况下,让设备得以连接互联网。
- 原理:共用同一个IP,分配端口。用局域网IP区分下面的设备。
NAT 的分类
如果外部主机要使用ISP已为某个内部主机分配的公网端口和其通信,
- 完全锥型(Full Cone):指允许不同的IP和NAT下的设备通信。
- 地址限制锥型(Restricted Cone):来源IP必须相同。
- 端口限制锥型(Port Restricted Cone):源IP和端口必须相同。
STUN
- 功能:在NAT下开辟一(或多)条公网到本地的通道。有点像占用公物。
- 原理:NAT下的设备向服务器发送数据包后,电信的路由器会为你分配一个入口(在公网中)。在管理宽松的情况下,互联网上的其他的设备也能通过这个入口,实现互相通信。
打洞
使用协议STUN的过程。
DDNS
全称:动态域名解析
作用:将某个设备的,动态变化的IP,绑定到固定的域名
对象存储
云服务商为开发者提供的文件存储服务。类似网盘。特点是贵,服务好(不限速、容量)。