BobMaster's Blog

生活的点滴-是热爱呀

使用debian13做路由网关

前言

以前都是使用openwrt发行版,这次尝试直接使用debian13实现类似的效果,由于机器性能过剩,使用debian可玩性会高很多。

目标:debian13 PPPoE拨号,ipv4+ipv6 透明代理

组件:

ppp / pppoeconf: PPPoE 拨号
nftables: 防火墙 + IPv4 NAT,配合tproxy实现局域网设备透明代理
dnsmasq: DHCPv4 + DNS + IPv6 RA
wide-dhcpv6-client: DHCPv6-PD 获取 LAN 侧 IPv6 前缀

由于我的迷你主机(零刻ser6-pro)只有一个千兆网口,我额外买了个Type-C转RJ45的转换器用来扩展。

目标拓扑:
WAN 物理网卡:enx00e04c680da5 (可以使用ip a找到作为WAN口的设备),接桥接光猫
LAN 网卡: enp2s0,接交换机 / AP
PPPoE 接口: ppp0
LAN IPv4: 192.168.10.1/24

等debian配置好了,原先的路由器改成 AP 模式,关闭 DHCP,LAN 口接 Debian 的 LAN 网络。

debian配置

为了方便,直接使用root用户配置

安装软件

apt update
apt install ppp pppoe pppoeconf nftables dnsmasq wide-dhcpv6-client

安装 wide-dhcpv6-client 的时候会问你要用于哪个网络设备,这里直接填ppp0。

安装过程部分服务起不来是正常的。

软件安装好后,我们就可以把目前的网络断开了,将原先插在路由器WAN口的网线插到debian的WAN口上。我们先备份一下默认的网络接口,

cp /etc/network/interfaces /etc/network/interfaces.bak

再写入下面的内容

# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback

allow-hotplug enx00e04c680da5
iface enx00e04c680da5 inet manual

auto enp2s0
iface enp2s0 inet static
    address 192.168.10.1/24

LAN 口使用前面规划的 192.168.10.1/24 。IPv6 前缀由 DHCPv6-PD 客户端动态分配。

PPPoE 拨号

运行:pppoeconf enx00e04c680da5

注意将 enx00e04c680da5 改为你的实际作为WAN口的网卡

根据提示操作即可,允许启动自动连接、输入账号密码,最后选择马上连接以后,可以看到多出了一个ppp0网络接口

ip addr show ppp0
5: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1492 qdisc fq_codel state UNKNOWN group default qlen 3
    link/ppp 
    inet 111.xxx.xxx.xxx peer 123.xxx.xxx.1/32 scope global ppp0
       valid_lft forever preferred_lft forever
    inet6 fe80::f84f:xxxx:xxxx:xxxx peer fe80::8e90:d3ff:xxxx:xxxx/128 scope link nodad 
       valid_lft forever preferred_lft forever

/etc/ppp/peers/dsl-provider 中建议加下面的选项

defaultroute
usepeerdns
persist
maxfail 0
holdoff 5
+ipv6
defaultroute6
mtu 1492
mru 1492

再加两个拨号成功后的脚本,用于设置ipv6默认路由(上面的defaultroute6我遇到过不起作用的情况),重启wide-dhcpv6-client服务

cat >/etc/ppp/ipv6-up.d/10-ipv6-default-route <<'EOF'
#!/bin/sh

IFACE="$1"

[ "$IFACE" = "ppp0" ] || exit 0

PEER_LL="$(ip -6 addr show dev "$IFACE" scope link | sed -n 's/.* peer \([^/ ]*\).*/\1/p' | head -n1)"

if [ -n "$PEER_LL" ]; then
    ip -6 route replace default via "$PEER_LL" dev "$IFACE" metric 1024
fi

exit 0
EOF

cat >/etc/ppp/ipv6-up.d/10-wide-dhcpv6-client <<'EOF'
#!/bin/sh
[ "$1" = "ppp0" ] && systemctl restart wide-dhcpv6-client.service
exit 0
EOF

开启转发

cat >/etc/sysctl.d/99-router.conf <<'EOF'
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
net.ipv6.conf.all.accept_ra=2
net.ipv6.conf.default.forwarding=1
net.ipv6.conf.default.accept_ra=2
EOF

sysctl --system

配置 DHCPv6-PD

cat >/etc/wide-dhcpv6/dhcp6c.conf <<'EOF'
interface ppp0 {
    send ia-na 0;
    send ia-pd 0;
    request domain-name-servers;
    script "/etc/wide-dhcpv6/dhcp6c-script";
};

id-assoc na 0 {
};

id-assoc pd 0 {
    prefix-interface enp2s0 {
        sla-id 0;
        sla-len 4;
        ifid 1;
    };
};
EOF

这里的意思是:在 ppp0 上向运营商请求 IPv6 地址和 IPv6 前缀,把拿到的前缀分配给LAN口 enp2s0,让 enp2s0 使用 ::1 作为 LAN 侧 IPv6 地址。运行商下发的前缀是/60,家庭 LAN 侧通过 RA/SLAAC 发给客户端时,通常是 /64。所以 sla-len = 64-60=4。

检查下 /etc/default/wide-dhcpv6-client ,确保里面的接口写的是 INTERFACES=”ppp0″

配置 dnsmasq:DHCPv4 + DNS + IPv6 RA

编辑 /etc/dnsmasq.d/router.conf

cat >/etc/dnsmasq.d/router.conf <<'EOF'
interface=enp2s0
bind-interfaces

# DNS
domain-needed
bogus-priv
no-resolv

server=119.29.29.29

# DHCPv4
dhcp-authoritative
dhcp-range=192.168.10.100,192.168.10.250,255.255.255.0,12h
dhcp-option=option:router,192.168.10.1
dhcp-option=option:dns-server,192.168.10.1

# ipv6 RA / SLAAC
enable-ra
dhcp-range=::,constructor:enp2s0,ra-stateless,ra-names,64,12h
EOF

nftables 防火墙/NAT

编辑 /etc/nftables.conf

cat >/etc/nftables.conf <<'EOF'
flush ruleset

define WAN = "ppp0"
define LAN = "enp2s0"
define LAN4 = 192.168.10.0/24

table inet filter {
    chain input {
        type filter hook input priority filter; policy drop;

        ct state established,related accept
        iif lo accept

        # LAN 访问路由器:DNS / DHCP / SSH
        iifname $LAN udp dport { 53, 67, 547 } accept
        iifname $LAN tcp dport { 22, 53 } accept

        # ICMP / ICMPv6,IPv6 必须保留 ICMPv6
        ip protocol icmp accept
        ip6 nexthdr icmpv6 accept

        # WAN 侧 DHCPv6 回复
        iifname $WAN udp sport 547 udp dport 546 accept
    }

    chain forward {
        type filter hook forward priority filter; policy drop;

        ct state established,related accept

        # LAN 出口
        iifname $LAN oifname $WAN accept

        # ICMPv6
        ip6 nexthdr icmpv6 accept
    }
}

table ip nat {
    chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;
        oifname $WAN ip saddr $LAN4 masquerade
    }
}
EOF

IPv4 需要 NAT;IPv6 不建议做 NAT66,而是用运营商给你的公网 LAN 前缀直接路由,然后靠防火墙阻止外部主动访问。

https://wiki.nftables.org/wiki-nftables/index.php/Simple_ruleset_for_a_home_router

将相关服务配置为自启动

systemctl enable wide-dhcpv6-client dnsmasq nftables

重启

重启下系统,然后验证下

ip addr show ppp0
ip addr show enp2s0
ip route
ip -6 route

ppp0:
  IPv4  正常拿到运营商分配的IP
  IPv6  正常拿到运营商分配的IP

enp2s0:
  192.168.10.1/24
  2408:8207:....::1/64 这一类 LAN 侧公网 IPv6

IPv4 默认路由:
  default dev ppp0

IPv6 默认路由:
  default via fe80::... dev ppp0

curl -4 ip.qqs.tw
curl -6 ip.qqs.tw
能够正常请求并拿到公网出口地址

使用tproxy配置透明代理

这边使用sing-box实现

添加policy routing

TProxy 需要把被标记的包送到本地 loopback,由 sing-box 接收。

cat >/etc/systemd/system/singbox-policy-routing.service <<'EOF'
[Unit]
Description=Policy routing for sing-box TProxy
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/sh -c '\
ip -4 rule show | grep -q "fwmark 0x1.*lookup 100" || ip -4 rule add fwmark 0x1/0x1 table 100 priority 100; \
ip -6 rule show | grep -q "fwmark 0x1.*lookup 100" || ip -6 rule add fwmark 0x1/0x1 table 100 priority 100; \
ip -4 route replace local 0.0.0.0/0 dev lo table 100; \
ip -6 route replace local ::/0 dev lo table 100'
ExecStop=/bin/sh -c '\
ip -4 rule del fwmark 0x1/0x1 table 100 priority 100 2>/dev/null || true; \
ip -6 rule del fwmark 0x1/0x1 table 100 priority 100 2>/dev/null || true; \
ip -4 route flush table 100; \
ip -6 route flush table 100'

[Install]
WantedBy=multi-user.target
EOF

立即启用一下:systemctl enable --now singbox-policy-routing.service

检查,应该能够看到类似下面的

ip rule
0:      from all lookup local
100:    from all fwmark 0x1/0x1 lookup singbox
32766:  from all lookup main
32767:  from all lookup default

ip -6 rule
0:      from all lookup local
100:    from all fwmark 0x1/0x1 lookup singbox
32766:  from all lookup main

ip route show table singbox
local default dev lo scope host

ip -6 route show table singbox
local default dev lo metric 1024 pref medium

sing-box tproxy配置可以参考,这里将tproxy端口监听在7893

{
  "inbounds": [
    {
      "type": "tproxy",
      "tag": "tproxy-in",
      "listen": "::",
      "listen_port": 7893,
      "network": "tcp"
    },
    {
      "type": "tproxy",
      "tag": "tproxy-in-udp",
      "listen": "::",
      "listen_port": 7893,
      "network": "udp"
    },
    {
      "type": "direct",
      "tag": "dns-in",
      "listen": "127.0.0.1",
      "listen_port": 1053,
      "override_address": "1.1.1.1",
      "override_port": 53
    }
  ],
  "outbounds": [
    {
      "type": "direct",
      "tag": "direct"
    }
  ],
  "route": {
    "rules": [
      {
        "action": "sniff"
      },
      {
        "type": "logical",
        "mode": "or",
        "rules": [
          {
            "protocol": "dns"
          },
          {
            "port": 53
          }
        ],
        "action": "hijack-dns"
      },
      {
        "ip_is_private": true,
        "outbound": "direct"
      }
    ],
    "auto_detect_interface": true,
    "default_domain_resolver": "local"
  }
}

direct inbound 可以监听本地端口并覆盖目标地址/端口;这里用它做一个本地 DNS 入口,然后通过路由规则 hijack-dns 交给 sing-box 内部 DNS 模块处理。

修改 dnsmasq,让客户端 DNS 进入 sing-box

interface=enp2s0
bind-interfaces

# DNS
domain-needed
bogus-priv
no-resolv

server=127.0.0.1#1053

# DHCPv4
dhcp-authoritative
dhcp-range=192.168.10.100,192.168.10.250,255.255.255.0,12h
dhcp-option=option:router,192.168.10.1
dhcp-option=option:dns-server,192.168.10.1

# ipv6 RA / SLAAC
enable-ra
dhcp-range=::,constructor:enp2s0,ra-stateless,ra-names,64,12h

客户端 DNS → dnsmasq:53 → sing-box dns-in:1053 → sing-box DNS 分流

重启dnsmasq: systemctl restart dnsmasq

更新nftables规则允许TProxy 包

cat >/etc/nftables.conf <<'EOF'
#!/usr/sbin/nft -f

flush ruleset

define WAN = "ppp0"
define LAN = "enp2s0"
define LAN4 = 192.168.10.0/24
define TPROXY_MARK = 0x1
define TPROXY_PORT = 7893

table inet singbox {
    set bypass4 {
        type ipv4_addr
        flags interval
        elements = {
            0.0.0.0/8,
            10.0.0.0/8,
            100.64.0.0/10,
            127.0.0.0/8,
            169.254.0.0/16,
            172.16.0.0/12,
            192.168.0.0/16,
            224.0.0.0/4,
            240.0.0.0/4,
        }
    }

    set bypass6 {
        type ipv6_addr
        flags interval
        elements = {
            ::/128,
            ::1/128,
            fc00::/7,
            fe80::/10,
            ff00::/8
        }
    }

    chain prerouting {
        type filter hook prerouting priority -150; policy accept;

        # 只处理 LAN 进来的客户端流量,不处理 ppp0/WAN,也不处理 Debian 本机 output
        iifname != $LAN return

        # 已经属于透明 socket 的 TCP 后续包,继续交给本地 socket
        meta l4proto tcp socket transparent 1 meta mark set $TPROXY_MARK accept

        # 访问 Debian 本机地址,例如 SSH、dnsmasq、网关管理页面,不透明代理
        fib daddr type local return

        # 私网、链路本地、组播、广播不透明代理
        ip daddr @bypass4 return
        ip6 daddr @bypass6 return

        # 让 Tailscale UDP 绕过 sing-box TProxy
        meta l4proto udp udp sport 41641 counter return
        meta l4proto udp udp dport { 41641, 3478 } counter return

        # DHCP / DHCPv6 不处理
        udp dport { 67, 68, 546, 547 } return

        # 透明代理 TCP / UDP
        meta l4proto tcp tproxy to :$TPROXY_PORT meta mark set $TPROXY_MARK accept
        meta l4proto udp tproxy to :$TPROXY_PORT meta mark set $TPROXY_MARK accept
    }
}

table inet filter {
        chain input {
                type filter hook input priority filter; policy drop;
                ct state established,related accept
                iif lo accept
                # TProxy 标记流量允许进入本机 sing-box
                meta mark $TPROXY_MARK accept

                iifname $LAN udp dport { 53, 67, 547 } accept
                iifname $LAN tcp dport { 22, 53 } accept
                ip protocol icmp accept
                ip6 nexthdr icmpv6 accept
                iifname $WAN udp sport 547 udp dport 546 accept
        }
        chain forward {
                type filter hook forward priority filter; policy drop;
                ct state established,related accept

                # 放行 IPv6 入站 UDP 41641 到 LAN 客户端
                iifname $WAN oifname $LAN meta l4proto udp udp dport 41641 counter accept

                # 未被 TProxy 接管或被旁路的流量仍允许正常转发
                iifname $LAN oifname $WAN accept
                ip6 nexthdr icmpv6 accept
        }
        chain output {
                type filter hook output priority filter;
        }
}

table ip nat {
        chain postrouting {
                type nat hook postrouting priority srcnat; policy accept;
                oifname $WAN ip saddr $LAN4 masquerade
        }
}
EOF

加载测试

nft -c -f /etc/nftables.conf && nft -f /etc/nftables.conf

到这里正常启动sing-box服务,网关下的设备应该都能无感享受到透明代理的效果。


评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注


©BobMaster 2018~2025