前言
以前都是使用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服务,网关下的设备应该都能无感享受到透明代理的效果。


发表回复