透明代理与 TUN

本页目录

透明代理让应用不必显式使用HTTP或SOCKS代理接口,由主机或网关接管选定流量,再按策略转发。它并不意味着应用绝对无法观察差异,也不等于透明解密:连接时序、地址、协议支持和证书边界仍可能改变。

需要分开回答:包怎样到代理,代理怎样获得原始目标,如何推断域名,以及代理自己的出站怎样避免再次被接管。只配置一个监听端口或一条默认路由,通常不足以完成整个闭环。

需求关键机制
接收本机应用出站本机OUTPUT路径、路由或TUN接管
接收其他机器经网关转发的流量入站PREROUTING及路由交付
保留报文原目的地址TPROXY等透明socket机制
获取应用请求的域名DNS映射、可见握手信息或应用元数据
防止代理递归接管自己出站标记、路由规则或隔离边界
同时覆盖IPv4/IPv6两个地址族的路由、规则和DNS行为

三种接管方式不在同一抽象层

接管流量和识别目标是两项不同工作 REDIRECT:改写目的端点 NAT / conntrack;原目标恢复依赖协议与实现 TPROXY:保留报文目的地址 透明 socket + 匹配规则 + 策略路由共同配合 TUN:把 IP 包送到用户态程序 程序再转发 IP,或终止连接并转换为代理协议 三者都不自动赋予域名信息,也不自动解密 HTTPS;还需独立的域名与信任策略。

REDIRECT:修改目的端点,借助连接跟踪恢复关系

REDIRECT是特殊的目的NAT。IPv4本机OUTPUT场景可重定向到回环地址,PREROUTING场景通常选择入接口本地地址;不能把全部情况写成改到127.0.0.1。iptables REDIRECT

NAT改了当前报文的目的地址,但conntrack可以保留原始方向的元组。TCP代理可通过适用的SO_ORIGINAL_DST查询原目的;这不是只在REDIRECT目标下才有意义的专用接口,也不应该简单写成“原地址永久丢失”。

Linux 7.2源码中,net/netfilter/nf_nat_redirect.c区分LOCAL_OUT与PRE_ROUTING地址选择;nf_conntrack_proto.c的getorigdst查询conntrack原始目的,并限制相应协议。UDP不能把这个TCP接口当作通用恢复方法,数据报原目标获取需单独设计。

REDIRECT让普通目的地址变成本地地址,但代理仍需理解被转发流量的协议和目标。拿TLS SNI代替原目标也不完整:SNI没有目的端口,也不保证每个连接都有可见域名。

TPROXY:包不做目的 NAT,仍要交给本地透明 socket

TPROXY将匹配流量交付到支持透明接收的socket,保留IP/传输层目的端点。其有效工作需要透明监听、规则选择、标记及策略路由共同配合;应用需按要求设置IP_TRANSPARENT等选项。

例如规则把选定包标记为1,策略规则查表100,而表100含local类型路由,就可以把非本机目的地址交给本地协议栈。local ... dev lo在这里表达本地交付,不是把原目的IP改写成127.0.0.1。

只有TPROXY动作没有对应路由,或者只建路由而代理不支持透明socket,都不能组成完整路径。本机生成流量与网关入站走不同hook,也不能认为一条PREROUTING规则同时接管所有本机出站。Linux TPROXY文档

TCP连接的目标可以从适用socket语义获取,UDP通常需按每个数据报处理目的元信息。实际程序仍需正确处理地址族、端口与回包来源,而非假设一条UDP监听socket只有一个永久目标。

TUN:用户态收发 IP 包的设备接口

TUN面向IP包,TAP面向以太网帧。Linux程序通过/dev/net/tun等接口创建或附着设备后,从文件描述符读取内核发向该设备的数据,再把返回数据写入内核接收路径。是否有4 B packet information前缀取决于IFF_NO_PI等设置,不能对所有实现固定跳过同一长度。

TUN本身不进行加密、不解析域名,也不自动提供SOCKS出口。用户态程序可以把IP包封装到VPN,也可以运行协议栈,终止本地TCP并向代理建立另一条连接。后者不等于把原TCP段逐个原样送进CONNECT。Linux TUN/TAP

虚拟网卡不一定都是/dev/net/tun。例如Linux内核WireGuard使用自身网络设备实现,用户态版本才可能通过TUN交付IP包;不能把所有WireGuard和TUN代理画成完全相同的数据路径。

出站绕行:DIRECT 同样会遇到循环

代理自己的出站连接必须有绕开接管路径的出口 应用发包 路由选择 TUN 用户态代理 读取 IP 包,选择转发策略 代理出站 socket 标记 / 接口 / 路由隔离 物理出口 直连目标或连接代理节点 若出站再次走默认 TUN,会形成递归接管;DIRECT 也必须真正绕开 TUN。

应用的目标包被路由到TUN,代理决定DIRECT后仍需创建真实出站连接。若新socket继续命中同一默认路由,连接又回到TUN,形成递归。连接远端代理节点也一样,不能只排除DIRECT目标而忘记节点地址。

可采用策略mark、专用路由表、绑定接口或网络命名空间等方式;具体优先级和地址覆盖必须配套。按进程UID绕过也有作用域与共享用户的问题,不能假设任意平台都具备相同规则语义。

以下只模拟单一IPv4教学策略:代理出站mark优先绕行,明确的本地网段绕行,其余才交给TUN。它不修改真实路由,也不覆盖所有特殊地址或部署规则。

from ipaddress import ip_address, ip_network

BYPASS_MARK = 0x80
local_ranges = [ip_network('127.0.0.0/8'), ip_network('192.168.0.0/16')]

def route(destination, mark=0):
    addr = ip_address(destination)
    if mark & BYPASS_MARK:
        return 'physical'
    if any(addr in net for net in local_ranges):
        return 'local'
    return 'tun'

assert route('203.0.113.10') == 'tun'
assert route('203.0.113.10', BYPASS_MARK) == 'physical'
assert route('192.168.1.10') == 'local'
assert route('198.18.0.42') == 'tun'
# 目标改成远端代理节点也不会自然绕开 TUN,必须有出口策略。
assert route('192.0.2.20') == 'tun'
assert route('192.0.2.20', BYPASS_MARK) == 'physical'
print('本地绕行、代理出口与递归风险模型通过')

“创建TUN不必用iptables”不代表部署不需要防火墙或策略路由。Mihomo的auto-route、auto-redirect、strict-route等选项有平台和实现限制,应阅读当前版本配置,而不是把名字解释成跨平台固定行为。Mihomo TUN配置

原始目的 IP 不等于域名

同一IP可以服务很多域名,同一域名也可对应多个地址。IP归属或地理数据库不能可靠恢复应用想访问的名字;“IP落在某国家”也不是域名分类的等价替代。

信息来源能提供什么缺失或不可靠的情况
原始IP/端口网络目标不含服务域名
明文HTTP Host应用声明的HTTP目标HTTPS下不可直接读取,也可能与连接目标不一致
TLS ClientHello SNI可见的服务名称提示无SNI、ECH或解析材料不全
DNS关联表某次解析与后续连接关联共享地址、缓存、旁路解析及生命周期差异
Fake-IP映射本地合成地址所代表的名称映射丢失、冲突或客户端绕过该DNS

TLS ClientHello不是在固定的“5+32”偏移就能找到SNI:记录、握手长度、session ID、套件和扩展均需按结构解析,还可能跨TCP读取边界。QUIC Initial则还需去除相应保护并重组CRYPTO数据,不能直接对UDP payload做固定偏移查找。

ECH隐藏内层ClientHello,外层公开名字不一定是应用原始目标。嗅探可用于有限的路由提示,不应当作可靠身份认证。RFC 9849:ECH

Fake-IP:分配本地索引,不是把域名编码进 IP

代理控制的DNS可给api.example.com分配198.18.0.42,客户端随后连接该合成地址;接管程序查本地映射,恢复域名并决定实际出口。地址本身通常无法单独解码出域名,关键状态在映射表中。

常见池使用198.18.0.0/15,但该段的IANA用途是网络基准测试,不是全球统一专为代理预留的地址。部署需避免与实际网络使用冲突,也不能让合成地址无映射地流到公网。IANA特殊地址登记

例如客户端缓存合成地址10分钟,代理1分钟后重启并把同一地址重分配给另一域名,就可能误路由。映射持久化、地址回收、DNS TTL和活动连接生命周期需协调;只保证当前表里“一IP一域名”还不够。

若客户端通过自带DoH获得真实IP,连接仍可被TUN接管,但Fake-IP映射可能不存在;这是“流量已接管、域名信息不足”,不一定是“流量完全绕过代理”。相反,只接管UDP53也不覆盖TCP DNS、DoT或DoH。对A与AAAA、直连域名及Fake-IP过滤应有一致策略。Mihomo DNS配置

映射清空后,缓存里的IP还代表原域名吗?

先清空映射再连接,地址本身不能恢复名称。接着误复用该地址,客户端依然想访问api.example,代理却会查出other.example。修复需要协调双方状态,而不是只让当前映射表看起来一一对应。

正在呈现知识画面
映射清空后,缓存里的IP还代表原域名吗?

合成IP是本地映射的索引。客户端副本与代理映射生命周期不同,过早复用地址会把旧意图指向新对象。

MTU、地址族与故障定位

接管后增加封装可能降低有效MTU;小请求成功、大传输停顿时,应检查路径MTU、ICMP错误传播和MSS等证据。用户态TCP栈、UDP中继与IP封装路径的处理方式不同,不能统一用一个“改MTU为固定值”解决所有问题。

IPv4默认路由0.0.0.0/0不覆盖IPv6。若AAAA可用而IPv6路径未接管,应用可能选择IPv6出口;若一律阻断IPv6,又可能改变可达性和地址选择。应根据预期覆盖范围明确配置,而不是把能访问某个IPv4站点当完整验收。

只读检查可以按次序查看当前规则与路由,再与代理日志对应:

ip rule show
ip -6 rule show
ip route show table all
ip -6 route show table all
ip -d link show
观察结果下一步定位
代理没有收到目标流量地址族、路由、hook与匹配条件
收到流量但原目标不对NAT恢复、透明socket、UDP元信息
能识别目标但反复出现相同出站出站绕行与mark/rule优先级
只有某些域名分流错误DNS路径、映射、ECH与嗅探限制
只有UDP失败代理出口能力、关联与MTU
只有大响应或长连接异常分片/PMTUD、背压、状态超时

这些检查用于理解现有部署;配置时需把监听、接管、绕行和返回路径作为完整方案核对。

继续查阅