家里云计划:基于有状态策略路由的完美内网穿透

概述

前文的提出的基于源IP的策略路由方案存在未解决的问题,即,无法访问docker映射出的端口,因为docker会再次进行NAT操作,导致回包源IP变为docker容器内部ip而非10.0.0.2,不能被策略路由匹配;而基于连接标记(CONNMARK)的策略路由存在同样缺陷

前文还提到了基于arp proxy的二层方案,而随着云服务商大量使用VPC网络,可行性较低

因此,本文提出了基于 动态 IPSet 的有状态策略路由 方案:

  1. 动态感知:利用 iptables 捕获从 WireGuard 入站的请求,将访问者的真实公网 IP 记录至带有 TTL 的 ipset 集合中。
  2. 目标打标:当家中主机或 Docker 容器回复数据包时,只要目标 IP 属于 ipset,即打上策略标记并强制送回 WireGuard 管道原路返回。
  3. 环路防御:脚本自动提取并豁免 VPS Endpoint 节点 IP,防止 WireGuard 建立隧道的流量被误路由至隧道内部而导致断连。

最终实现了无需修改 Docker 网络完美透传客户端真实 IP纯内核态高吞吐且支持全端口转发的完美内网穿透方案。

至此,我们获得了同时具有以下优点的完美内网穿透!

  • 高性能,wireguard+iptables纯内核态处理 (私、高性能ですから!)
  • 完全开源私有化部署,不依赖任何商业软件
  • 完美的tcp/udp真实ip透传,无需服务端额外支持
  • 端口范围转发,或者全端口转发
  • 强兼容性,完美支持docker,经过masquerade等复杂网络环境后仍能正常工作
  • 部署要求宽松,配置简单,可以参考前文配置

部署

首先当然要把wireguard弄通了)

配置文件

post-up:

#!/bin/bash
set -e

WG_IFACE="wg0"
ROUTING_TABLE="4242"
MARK_VAL="0x42"
IPSET_NAME="wg_peers"
LAN_NET="192.168.1.0/24"
TTL=3600
WG_CONF="/etc/wireguard/${WG_IFACE}.conf"

echo "[$(date)] Setting up dynamic WG peer routing..."

#########################################
# 0. 自动提取 WG Endpoint 外网 IP (关键防御!)
#########################################
# 从 wg0.conf 中提取 Endpoint 的 IP 地址
ENDPOINT_IP=$(grep -iE '^\s*Endpoint\s*=' "${WG_CONF}" | awk -F'[: ]+' '{print $3}' | head -n 1 || true)

if [ -n "${ENDPOINT_IP}" ]; then
    echo "[$(date)] Detected WG Endpoint IP: ${ENDPOINT_IP}, it will be EXCLUDED from ipset."
fi

#########################################
# 1. 创建 ipset
#########################################
ipset create ${IPSET_NAME} hash:ip timeout ${TTL} -exist

# 如果 Endpoint IP 在 ipset 中(可能是残留脏数据),立刻删除它解套!
if [ -n "${ENDPOINT_IP}" ]; then
    ipset del ${IPSET_NAME} ${ENDPOINT_IP} 2>/dev/null || true
fi

#########################################
# 2. WireGuard 路由表配置
#########################################
ip route replace default dev ${WG_IFACE} table ${ROUTING_TABLE}

#########################################
# 3. Policy Routing (策略路由)
#########################################
ip rule del pref 10 2>/dev/null || true
ip rule del pref 100 2>/dev/null || true

# LAN 流量永远走 main 表
ip rule add to ${LAN_NET} lookup main pref 10

# 被标记的 peer 流量走 WG 路由表 (4242)
ip rule add fwmark ${MARK_VAL} lookup ${ROUTING_TABLE} pref 100

#########################################
# 4. iptables 规则配置 (自定义链)
#########################################
iptables -t mangle -N WG_PEER_IN 2>/dev/null || true
iptables -t mangle -N WG_PEER_OUT 2>/dev/null || true

iptables -t mangle -F WG_PEER_IN
iptables -t mangle -F WG_PEER_OUT

iptables -t mangle -C PREROUTING -j WG_PEER_IN 2>/dev/null || iptables -t mangle -A PREROUTING -j WG_PEER_IN
iptables -t mangle -C OUTPUT -j WG_PEER_OUT 2>/dev/null || iptables -t mangle -A OUTPUT -j WG_PEER_OUT

#########################################
# 5. 记录 WG Peer (入口记录与 TTL 刷新)
#########################################
# 5.1 忽略 LAN 内部流量
iptables -t mangle -A WG_PEER_IN -i ${WG_IFACE} -s ${LAN_NET} -j RETURN

# 5.2 【核心修复】:绝对禁止将 WG Endpoint 节点的公网 IP 写入 ipset!
if [ -n "${ENDPOINT_IP}" ]; then
    iptables -t mangle -A WG_PEER_IN -s ${ENDPOINT_IP} -j RETURN
fi

# 5.3 记录 WireGuard 入站流量 IP 并更新 TTL
iptables -t mangle -A WG_PEER_IN -i ${WG_IFACE} -j SET --add-set ${IPSET_NAME} src --exist --timeout ${TTL}

#########################################
# 6. 对目标 Peer 打 Mark (支持转发与本机流量)
#########################################
# 6.1 本机发出的流量 -> 打 Mark
iptables -t mangle -A WG_PEER_OUT -m set --match-set ${IPSET_NAME} dst -j MARK --set-mark ${MARK_VAL}

# 6.2 Docker 等容器/转发发出的流量 -> 打 Mark
iptables -t mangle -A WG_PEER_IN ! -i ${WG_IFACE} -m set --match-set ${IPSET_NAME} dst -j MARK --set-mark ${MARK_VAL}

#########################################
# 7. 对 WG 出口流量做 MASQUERADE
#########################################
iptables -t nat -C POSTROUTING -o ${WG_IFACE} -j MASQUERADE 2>/dev/null || iptables -t nat -A POSTROUTING -o ${WG_IFACE} -j MASQUERADE

#########################################
# 8. 关闭 rp_filter (反向路径过滤)
#########################################
sysctl -q -w net.ipv4.conf.all.rp_filter=0
sysctl -q -w net.ipv4.conf.default.rp_filter=0
sysctl -q -w net.ipv4.conf.${WG_IFACE}.rp_filter=0

#########################################
# 9. 刷新路由缓存
#########################################
ip route flush cache

echo "[$(date)] Dynamic WG peer routing successfully loaded with Endpoint immunity!"

post-down:

#!/bin/bash
set -e

WG_IFACE="wg0"
ROUTING_TABLE="4242"
MARK_VAL="0x42"
IPSET_NAME="wg_peers"
LAN_NET="192.168.1.0/24"

echo "[$(date)] Cleaning up dynamic WG peer routing..."

#########################################
# 1. 解挂并删除 iptables 自定义链
#########################################
# 先从主链中解挂(忽略解挂失败的报错)
iptables -t mangle -D PREROUTING -j WG_PEER_IN 2>/dev/null || true
iptables -t mangle -D OUTPUT -j WG_PEER_OUT 2>/dev/null || true

# 清空自定义链中的规则
iptables -t mangle -F WG_PEER_IN 2>/dev/null || true
iptables -t mangle -F WG_PEER_OUT 2>/dev/null || true

# 删除自定义链
iptables -t mangle -X WG_PEER_IN 2>/dev/null || true
iptables -t mangle -X WG_PEER_OUT 2>/dev/null || true

#########################################
# 2. 删除 Policy Routing (策略路由)
#########################################
# 按 pref 彻底删除添加的路由规则
ip rule del pref 10 2>/dev/null || true
ip rule del pref 100 2>/dev/null || true

#########################################
# 3. 清理自定义路由表 (4242)
#########################################
ip route flush table ${ROUTING_TABLE} 2>/dev/null || true

#########################################
# 4. 销毁 ipset
#########################################
# -F 清空,-X 销毁(-exist 避免不存在时报错)
ipset flush ${IPSET_NAME} 2>/dev/null || true
ipset destroy ${IPSET_NAME} 2>/dev/null || true

#########################################
# 5. 恢复 rp_filter (可选)
#########################################
# 恢复为 Linux 默认的严格/松散模式 (通常系统默认为 1 或 2)
sysctl -q -w net.ipv4.conf.all.rp_filter=2 2>/dev/null || true
sysctl -q -w net.ipv4.conf.default.rp_filter=2 2>/dev/null || true

#########################################
# 6. 刷新路由缓存
#########################################
ip route flush cache

echo "[$(date)] Dynamic WG peer routing cleaned up successfully."

(可选)辅助watchdog脚本

#!/bin/bash

# 配置部分:请确保 WG_UP_SCRIPT 指向你最新的 PostUp 配置脚本路径
WG_UP_SCRIPT="/etc/wireguard/ip_post_up.sh"

WG_IFACE="wg0"
ROUTING_TABLE="4242"
MARK_VAL="0x42"
IPSET_NAME="wg_peers"

# 1. 检查网卡是否存在 (防止 WireGuard 处于 Down 状态时误触发)
if ! ip link show "$WG_IFACE" > /dev/null 2>&1; then
    # WG 接口未启动,静默退出
    exit 0
fi

NEED_RECOVER=0

# 2. 检查策略路由是否存在 (fwmark 0x42 -> lookup 4242)
if ! ip rule list | grep -q "fwmark ${MARK_VAL}.*lookup ${ROUTING_TABLE}"; then
    echo "[$(date)] Watchdog: Missing ip rule for fwmark ${MARK_VAL}."
    NEED_RECOVER=1
fi

# 3. 检查 ipset 容器是否存在
if ! ipset list "${IPSET_NAME}" > /dev/null 2>&1; then
    echo "[$(date)] Watchdog: Missing ipset ${IPSET_NAME}."
    NEED_RECOVER=1
fi

# 4. 检查 iptables 自定义链挂载点是否存在
if ! iptables -t mangle -C PREROUTING -j WG_PEER_IN > /dev/null 2>&1 || \
   ! iptables -t mangle -C OUTPUT -j WG_PEER_OUT > /dev/null 2>&1; then
    echo "[$(date)] Watchdog: Missing iptables mangle hooks."
    NEED_RECOVER=1
fi

# 6. 如果发现任何一项缺失,重新执行修复
if [ "${NEED_RECOVER}" -eq 1 ]; then
    echo "[$(date)] Watchdog: Inconsistency detected! Restoring WG peer routing rules..."
    
    if [ -f "${WG_UP_SCRIPT}" ]; then
        bash "${WG_UP_SCRIPT}"
        echo "[$(date)] Watchdog: Restored successfully using ${WG_UP_SCRIPT}."
    else
        echo "[$(date)] Watchdog ERROR: Setup script ${WG_UP_SCRIPT} not found!"
        exit 1
    fi
fi

# 规则全部完好,静默退出
exit 0

检查运行状态

$ sudo ipset list wg_peers| grep -i "Number of entries"
Number of entries: 3568

至此艺术已成

结尾

贴别感谢turbothorsten的回帖的启发:https://superuser.com/questions/1777082/using-vps-to-give-public-ip-to-on-home-network/1777106

文章内容遵循 CC BY 4.0,代码/脚本遵循 MIT License

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇