Debian使用nftables进行端口转发

Debian使用nftables进行端口转发

mohuangNPC
2026-01-06 / 0 评论 / 2 阅读 / 正在检测是否收录...

最近搭建MC服务器,虽然有公网IP但是不想使用frp,最后采用了通过了Wireguard组网,nftables进行转口转发的方式,这里记录一下端口转发的配置以及相关介绍

先上一些基础名词配置

# 标准 NAT 钩子点有约定名称:
#   - prerouting    (入站数据包处理)
#   - input         (目标为本机的数据包)
#   - output        (本机产生的数据包)  
#   - postrouting   (出站数据包处理)
#   - forward       (转发的数据包)

# nftables          iptables
# ---------------------------------
# prerouting   ↔   PREROUTING
# input        ↔   INPUT
# output       ↔   OUTPUT
# postrouting  ↔   POSTROUTING
# forward      ↔   FORWARD

# 如果你改名,熟悉 iptables 的人会困惑

直接上配置,咱们逐行解析

table ip nat {
    chain prerouting {
        type nat hook prerouting priority -100; policy accept;
        # 外部访问转发
        tcp dport 80 redirect to :8080
    }
    
    chain output {
        type nat hook output priority -100; policy accept;
        # 本地访问转发
        ip daddr 127.0.0.1 tcp dport 80 redirect to :8080
        # 或者更通用(包括本机IP)
        # tcp dport 80 redirect to :8080
    }
    
    chain postrouting {
        type nat hook postrouting priority 100; policy accept;
    }
}

表定义

table ip nat

  • table:定义一个 nftables 表
  • ip:仅处理 IPv4 数据包(IPv6 需要 ip6 或 inet)
  • nat:表名称,专门用于网络地址转换(NAT)

PREROUTING 链定义

    chain prerouting {
        type nat hook prerouting priority -100; policy accept;
  • chain prerouting:创建名为 "prerouting" 的链
  • type nat:这是一个 NAT 类型的链
  • hook prerouting:挂接到内核的 PREROUTING 钩子点
  • priority -100:优先级,数字越小优先级越高
  • policy accept:默认策略是接受(如果规则不匹配)

工作流程:

外部访问: 客户端 → 服务器IP:80 → PREROUTING → 目标端口改为8080 → 本地进程

PREROUTING 规则

# 外部访问转发
 tcp dport 80 redirect to :8080
}
  • tcp dport 80:匹配 TCP 协议且目标端口为 80 的数据包
  • redirect to :8080:将目标端口重定向到 8080

工作流程:

外部访问: 客户端 → 服务器IP:80 → PREROUTING → 目标端口改为8080 → 本地进程

OUTPUT 链定义

    chain output {
        type nat hook output priority -100; policy accept;
  • chain output:创建名为 "output" 的链
  • hook output:挂接到 OUTPUT 钩子点
  • 优先级相同:-100,与 PREROUTING 并行处理

OUTPUT 钩子位置:

本地进程发送数据 → OUTPUT → POSTROUTING → 发送到网络

OUTPUT 链规则

# 本地访问转发
ip daddr 127.0.0.1 tcp dport 80 redirect to :8080
  • ip daddr 127.0.0.1:匹配目标 IP 地址为 127.0.0.1(localhost)
  • tcp dport 80:匹配目标端口 80
  • redirect to :8080:重定向到 8080 端口

为什么需要这个规则

本地访问: curl 127.0.0.1:80 → OUTPUT链 → 端口改为8080 → 回环接口 → 本地进程

POSTROUTING 链定义

    chain postrouting {
        type nat hook postrouting priority 100; policy accept;
    }
}
  • hook postrouting:挂接到 POSTROUTING 钩子点
  • priority 100:较低优先级,在路由决策后执行

POSTROUTING 钩子位置:

数据包发送前 → POSTROUTING → 网络接口发送

完整数据包流向

场景1:外部客户端访问

mk1xlqgk.png

场景2:本地 curl 访问

mk1xlqgk.png

场景3:本地 curl 访问服务器公网IP

mk1xlqgk.png

问题

当咱们有这个配置

 chain output {
        type nat hook output priority -100; policy accept;
        # 本地访问转发
        ip daddr 127.0.0.1 tcp dport 80 redirect to :8080
        # 或者更通用(包括本机IP)
        # tcp dport 80 redirect to :8080
    }

咱们访问curl 127.0.0.1的时候会转发到80,但是curl 192.x.x.x则不会,因为咱们只配置了127.0.0.1

解决方案1:更通用的 OUTPUT 规则

chain output {
        type nat hook output priority -100; policy accept;
        # 匹配所有目标端口80的TCP流量
        tcp dport 80 redirect to :8080
    }

但是这样有大问题,本机所有目标端口的80都会到8080,甚至是curl http://www.baidu.com
所以慎用

解决方案2:指定所有本地IP

  chain output {
        type nat hook output priority -100; policy accept;
        # 匹配多个本地地址
        ip daddr { 127.0.0.1, 192.168.1.100, 10.0.0.1 } tcp dport 80 redirect to :8080
    }

进阶

代理到其他内网机器

大部分时候MC服务器不一定是在本机,所以有时候我们需要转发到别的内网机或者外网机

先上配置:

table ip nat {

    chain prerouting {
        type nat hook prerouting priority 0; policy accept;
        #ip daddr 公网ip tcp dport 80 dnat to 172.32.100.11:25565
        iifname "eth0" tcp dport 80 dnat to 172.32.100.11:25565
    }

    chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;
        #ip daddr 172.32.100.11 masquerade
        oifname "test" masquerade
    }
}

prerouting部分

咱们先看第一部分

ip daddr 公网ip tcp dport 80 dnat to 172.32.100.11:25565
iifname "eth0" tcp dport 80 dnat to 172.32.100.11:25565

为什么我把第一段注释了呢,这段意思是把ip端口为公网:80的流量转发给172xxxx:25565,那么正常应该是没问题,但是咱们一般都是公网云服务器,一般也都是弹性IP,也就是说,如果正常网络下访问公网:80, 服务器的确能收到流量,但是目标ip可能被云服务器厂商转换了,而转换的ip大概率就是本机eth0(本机网卡)的IP

mk1xlqgk.png

如图这个10.0.16.2,当然我没试过,所以咱们直接最暴力的就是本机eth所有的80端口流量都到172.32.100.11:25565就行

postrouting部分

咱们再看第二部分,首先我们回顾一下场景

① 客户端访问公网服务器

客户端 → 82.x.x.x:80

② PREROUTING

iifname "eth0" tcp dport 80 dnat to 172.32.100.11:25565

数据包变为了

源 IP: 客户端公网 IP
目标 IP: 172.32.100.11
目标端口: 25565

③ 路由判断

系统发现:172.32.100.11 在 wg/本机局域网 网段 → 走 test/eth0 网卡

④ 到达 POSTROUTING(当前部分)

oifname "test" masquerade

那这里发生了什么呢?

内核准备把包从 test 网卡发出去,于是 nftables 检查这个包是不是要从 test 网卡出去

✔ 是 → 命中规则

⑤ masquerade 做了什么(核心)

原始包(DNAT 后):
源 IP: 客户端公网 IP
目标 IP: 172.32.100.11
经过 masquerade 后:
源 IP: 172.32.100.1   ←(本机 test 网卡 IP)
目标 IP: 172.32.100.11

源地址被“伪装”成 WG 网卡自己

为什么必须 masquerade?

如果不 masquerade(常见错误)

MC 服务器收到的是:

源 IP: 客户端公网 IP
目标 IP: 172.32.100.11

然后 MC 服务器回包时会:

目的 IP = 客户端公网 IP

But 这就是问题:

  • MC 服务器根本不知道怎么去公网
  • 默认路由可能不在 WG
  • 回包直接丢失
有 masquerade 的情况

MC 服务器看到的是:
源 IP: 172.32.100.1(你的公网服务器)

回包:
172.32.100.11 → 172.32.100.1

  • 走 WG 回到公网服务器
  • 内核根据 NAT 表反查
  • 自动还原成:82.x.x.x → 客户端

连接成功

0

评论 (0)

取消

Warning: file_put_contents(/var/www/html/rss.xml): failed to open stream: Permission denied in /var/www/html/usr/plugins/CustomRSS/Plugin.php on line 149