Linux Wireguard + nftable 实现内网穿透并保留源IP (DNAT)
使用Wireguard进行端口转发, 能保留TCP连接的源IP
Zapic
2025-08-16 0

两台机器, 一台内网机器N, 一台有公网G.
仅能保留TCP的连接的IP, UDP连接配置过于繁琐, 故直接使用动态伪装以简化操作, 此时看到的IP为公网机器G的IP.

一些配置中的可变量, 可以按需更改:

变量名用途
wg0Wireguard的接口名称, 文章同时用作了Nftable转发规则的表名称
172.16.0.1/24Wireguard接口IP, 组网后网络中的IP, 可以任意修改为任何你喜欢的IP段
Table = 1Wireguard使用的路由表, 在内网机器N配置时的PostUp和PostDown中有引用, 路由表ID范围为0~32768, 0为系统默认占用, 任意修改

首先开启公网机器G的转发:

echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p

在两台机器上都安装Wireguard, 并都生成一对公私钥.

cd /etc/wireguard
wg genkey > wg0.key
cat wg0.key | wg pubkey > wg0.pub

输出:

# wg genkey > wg0.key
Warning: writing to world accessible file.
Consider setting the umask to 077 and trying again.
# cat wg0.key | wg pubkey > wg0.pub
# cat wg0.pub
LTzVnao892Iifn835NNdi398jNNIO39dj53MczezRH0=
# cat wg0.key
GHjJFZ7K8diUdk23MidPc991VOll+ZsiVdnMa8aiWlI=

该操作需要在两台机器上都完成一次.

配置公网机器G:
/etc/wireguard/wg0.conf

[Interface]
# 监听端口, 任何你想要的端口都行
ListenPort = 1145
# 公网机器G在Wireguard中的IP, 建议设置成内网IP段
Address = 172.16.0.1/24
# 生成密钥时, 在公网机器G中/etc/wireguard/wg0.key的内容
PrivateKey = GHjJFZ7K8diUdk23MidPc991VOll+ZsiVdnMa8aiWlI=
# 启动时加载转发规则
PostUp = nft -f /etc/wireguard/wg0.nft
# 关闭时清除转发规则
PostDown = nft delete table wg0

[Peer]
# 生成密钥时, 在内网机器N中/etc/wireguard/wg0.pub的内容
PublicKey = LTzVnao892Iifn835NNdi398jNNIO39dj53MczezRH0=
# 需要设置成同一网段
AllowedIPs = 172.16.0.0/24

配置内网机器N:
/etc/wireguard/wg0.conf:

[Interface]
# 内网机器N在Wireguard中的IP, 需要与公网机器同一网段
Address = 172.16.0.2/24
# 生成密钥时, 在内网机器N中/etc/wireguard/wg0.key的内容
PrivateKey = QE6m/gMDgJ2LQA7lj3+5kz7fouX3awNzQy2ruXV/KX0=
# 使用另外的路由表处理转发, 可以是任何小于32768的数字
Table = 1
# 配置路由使流量发回到Wireguard中
PostUp = ip rule add from 172.16.0.2 table 1;
# 关闭时清除路由
PostDown = ip rule del from 172.16.0.2 table 1;

[Peer]
# 生成密钥时, 在公网机器G中/etc/wireguard/wg0.pub的内容
PublicKey = LTzVnao892Iifn835NNdi398jNNIO39dj53MczezRH0=
# 公网机器G的IP与你设置的端口
Endpoint = 1.2.3.4:1145
# 特别的, 需要设置成接受所有流量, 以处理转发
AllowedIPs = 0.0.0.0/0
# 保持隧道开启, 否则公网机器在一段时间后无法访问内网机器
PersistentKeepalive = 20

配置转发, 只需要在公网机器G上操作:
/etc/wireguard/wg0.nft:

table ip wg0 {
        # 内网机器N在Wireguard中的IP
        define dest = 172.16.0.2
        # 需要转发的TCP端口
        define tcp_ports = { 22, 25565 }
        # 需要转发的UDP端口
        # define udp_ports = { 53 }
        # 公网机器的出口接口, 可以用ip addr命令查看
        define infc = eth0
        chain prerouting {
                type nat hook prerouting priority dstnat; policy accept;
                # DNAT转发TCP
                iif $infc tcp dport $tcp_ports dnat to $dest
                # DNAT转发UDP, 有转发需求可去掉井号注释
                # iif $infc udp dport $udp_ports dnat to $dest
        }

        chain postrouting {
                type nat hook postrouting priority srcnat; policy accept;
                # 对UDP使用动态伪装, 能大幅简化配置, 但会丢失源IP
                iif $infc meta l4proto udp masquerade
        }
}

在两台机器上都启动Wireguard并配置开机自启:

wg-quick up wg0
systemctl enable wg-quick@wg0

References:

  1. 妖月. (2023, January 17). 通过Wireguard实现内网穿透. 妖の森. https://fjwr.xyz/archives/174/
评论 0
没有评论
评论已关闭
发表评论
评论 取消回复
Copyright © 2025 Zapic's Blog