前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >云原生家庭网络(十七):使用 nftables 透明拦截流量

云原生家庭网络(十七):使用 nftables 透明拦截流量

原创
作者头像
imroc
发布2024-05-30 09:57:13
1850
发布2024-05-30 09:57:13
举报

概述

iptables 和 nftables 都能用于透明代理的流量拦截,区别就是 nftables 是后起之秀,未来取代 iptables。可读性更高、性能更好。

本文介绍如何在路由器利用 nftables 透明拦截流量。

思路

可以利用 Pod 的 postStart 和 preStop 两个生命周期的 hook 来设置和清理 nftables 相关规则:

代码语言:yaml
复制
        lifecycle:
          postStart:
            exec:
              command:
              - /scripts/set-rules.sh
          preStop:
            exec:
              command:
              - /scripts/clean.sh

脚本和 nftables 通过 configmap 挂载。

volumeMounts 部分:

代码语言:yaml
复制
        volumeMounts:
        - mountPath: /scripts
          name: script
        - mountPath: /etc/nftables
          name: nftables-config

volumes 部分:

代码语言:yaml
复制
      volumes:
      - configMap:
          defaultMode: 420
          name: nftables-config
        name: nftables-config
      - configMap:
          defaultMode: 511
          name: script
        name: script

另外,保险起见,可以加个 initContainer,保证在启动的时候也执行下清理规则的脚本(避免代理 Pod 异常挂掉导致 preStop 里的清理规则脚本没执行到):

代码语言:yaml
复制
      initContainers:
      - command:
        - /scripts/clean.sh
        image: your-proxy-image
        imagePullPolicy: IfNotPresent
        name: clean
        securityContext:
          privileged: true
        volumeMounts:
        - mountPath: /scripts
          name: script

nftables 规则

nftables.conf:

代码语言:conf
复制
#!/usr/sbin/nft -f

table inet proxy
delete table inet proxy

table inet proxy {

    # 保留网段,参考:https://zh.wikipedia.org/zh-sg/%E4%BF%9D%E7%95%99IP%E5%9C%B0%E5%9D%80
    set byp4 {
        typeof ip daddr
        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.0.0.0/24,
            192.0.2.0/24,
            192.88.99.0/24,
            192.168.0.0/16,
            198.18.0.0/15,
            198.51.100.0/24,
            203.0.113.0/24,
            224.0.0.0/4,
            240.0.0.0-255.255.255.255
        }
    }
    set byp6 {
        typeof ip6 daddr
        flags interval
        elements = {
            ::,
            ::1,
            ::ffff:0:0:0/96,
            100::/64,
            64:ff9b::/96,
            2001::/32,
            2001:10::/28,
            2001:20::/28,
            2001:db8::/32,
            2002::/16,
            fc00::/7,
            fe80::/10,
            ff00::/8
        }
    }

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

        meta l4proto != { tcp, udp } counter return

        # 避免拦截公网回包(如果没用主路由方案,可以删除)
        iifname ppp0 \
          ct state new \
          ct mark set 11 \
          log prefix "Public Inbound:" \
          counter \
          return \
          comment "Mark conntrack for connection from internet, avoid intercept the return packets"

        # 不拦截从公网主动进来的包(如果没用主路由方案,可以删除)
        iifname ppp0 counter return

        # 避免源和目的都是本机公网 IP 时造成代理 UDP 无限回环
        fib daddr type local counter return

        # 对保留 IP(包括内网) 放行。
        ip daddr @byp4 counter return
        ip6 daddr @byp6 counter return
        goto proxy
    }
    chain proxy {
        # 对其它数据包(其它内网机器访问公网的包)进行拦截,全部转发给代理。
        # 踩坑:一定 tproxy 到回环 ip (127.0.0.1和::1),如果 tproxy 到 0.0.0.0和::,运行一段时间后,可能会导致 UDP 疯狂不停回环打满 CPU(本机外部IP到本机外部IP一直在lo网卡打转)
        meta l4proto { tcp, udp } tproxy ip to 127.0.0.1:12345 meta mark set 1 counter
        meta l4proto { tcp, udp } tproxy ip6 to [::1]:12345 meta mark set 1 counter
    }
    chain output {
        type route hook output priority filter; policy accept;

        # 不拦截代理自身发出的包。
        meta skgid eq 23333 counter return

        # 不拦截公网回包
        ct mark 11 counter return

        # 对保留 IP(包括内网) 放行。
        ip daddr @byp4 counter return
        ip6 daddr @byp6 counter return

        # 避免源和目的都是本机公网 IP 时造成代理 UDP 无限回环
        fib daddr type local counter return

        # 主机内进程发出的包打上 mark,让报文走策略路由重新进入 PREROUTING,以便被代理。
        meta l4proto { tcp, udp } meta mark set 1 counter
    }
}

设置规则的脚本

set-rules.sh:

代码语言:bash
复制
#!/bin/bash

set -x

while :; do
	export https_proxy=http://127.0.0.1:10809                                           # 假设代理会启动 10809 端口的 HTTP 代理
	code=$(curl -I -o /dev/null -m 5 -s -w %{http_code} https://www.google.com | xargs) # 通过代理探测目标地址是否正常响应
	if [[ "${code}" == "200" ]]; then                                                   # 探测成功就退出探测循环,准备设置策略路由
		break
	fi
	echo "bad code:${code};" >/tmp/error.log
	sleep 5s
done

echo "proxy is ready, set up rules"
ip rule list | grep "from all fwmark 0x1 lookup 100"
if [ $? -ne 0 ]; then
	echo "add rule and route"
	ip rule add fwmark 0x1 table 100
	ip route add local 0.0.0.0/0 dev lo table 100
	ip -6 rule add fwmark 0x1 table 100
	ip -6 route add local ::/0 dev lo table 100
fi

nft -f /etc/nftables/nftables.conf # 设置 nftables 规则
exit 0

清理规则的脚本

clean.sh:

代码语言:bash
复制
#!/bin/bash

set -x

nft delete table inet proxy # 关闭时清理 nftables,避免拉不了镜像
ip rule delete fwmark 0x1 table 100
ip route delete local 0.0.0.0/0 dev lo table 100
ip -6 rule delete fwmark 0x1 table 100
ip -6 route delete local ::/0 dev lo table 100

exit 0

定向拦截

如果家里的设备特别多,全局拦截流量的话,对代理的压力可能较大,有时可能会影响网速。

这种情况可以考虑只拦截特定设备的流量,比如只拦截 10.10.10.2~10.10.10.10 这个小网段的流量,该网段的 IP 可以静态分配,在 dnsmasq 的 dhcp 里配一下,示例:

代码语言:conf
复制
log-queries=extra
no-resolv
no-poll
server=61.139.2.69
strict-order
log-dhcp
cache-size=2000
dhcp-range=10.10.10.11,10.10.10.254,255.255.255.0,12h
dhcp-authoritative
dhcp-leasefile=/var/lib/dnsmasq/dnsmasq.leases
dhcp-option=option:router,10.10.10.2
dhcp-option=option:dns-server,61.139.2.69,218.6.200.139
dhcp-host=a0:78:17:87:0e:51,10.10.10.5 # M1
dhcp-host=fc:18:3c:37:6a:1c,10.10.10.6 # iPhone - roc
  • dhcp-host 配置项里填入设备的 MAC 地址及其对应固定分配的 IP 地址。

需要注意的是,有些设备在内网通信时可能不会使用真实的 MAC 地址,比如 iPhone,可以在无线局域网里点进连上的 WiFi 设置下,关掉 私有无线局域网地址

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 思路
  • nftables 规则
  • 设置规则的脚本
  • 清理规则的脚本
  • 定向拦截
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档