[{"data":1,"prerenderedAt":761},["ShallowReactive",2],{"post-zh-trans-proxy-transparent-gateway":3},{"id":4,"title":5,"body":6,"cover":752,"date":753,"description":754,"extension":755,"meta":756,"navigation":215,"path":757,"seo":758,"stem":759,"__hash__":760},"posts/posts/trans-proxy-transparent-gateway.md","使用 trans_proxy 搭建透明代理网关",{"type":7,"value":8,"toc":732},"minimark",[9,13,17,20,24,34,41,45,48,51,77,81,88,109,113,116,142,145,148,151,165,168,246,249,252,263,312,315,325,376,379,503,506,509,528,531,534,537,557,576,579,593,596,599,622,625,667,671,679,687,690,693,696,728],[10,11,12],"h2",{"id":12},"什么是透明代理",[14,15,16],"p",{},"传统代理需要在每台设备上手动配置代理地址，透明代理则工作在网关这一层：局域网设备只要把默认网关和 DNS 指向代理机器，所有 TCP 流量就会被自动拦截并转发到上游代理，设备本身不用做任何代理配置。",[14,18,19],{},"这对不方便设置代理的设备（智能电视、IoT 设备、游戏主机等）尤其有用。",[10,21,23],{"id":22},"trans_proxy-的工作原理","trans_proxy 的工作原理",[14,25,26,33],{},[27,28,32],"a",{"href":29,"rel":30},"https://github.com/madeye/trans_proxy",[31],"nofollow","trans_proxy"," 是一个用 Rust 写的透明代理工具，支持 macOS 和 Linux，基于 tokio 异步运行时。它的整体架构如下：",[14,35,36],{},[37,38],"img",{"alt":39,"src":40},"trans_proxy 架构示意图","/assets/2026/trans-proxy-arch.svg",[42,43,44],"h3",{"id":44},"获取原始目的地址",[14,46,47],{},"透明代理要解决的第一个问题是：流量被 NAT 重定向到 trans_proxy 的监听端口后，怎么知道客户端原本想访问的地址？",[14,49,50],{},"trans_proxy 在两个平台上用了不同的办法：",[52,53,54,67],"ul",{},[55,56,57,61,62,66],"li",{},[58,59,60],"strong",{},"macOS (pf)","：通过 ",[63,64,65],"code",{},"DIOCNATLOOK"," ioctl 查询 pf 的 NAT 状态表，拿到连接被重定向前的原始目的地址",[55,68,69,72,73,76],{},[58,70,71],{},"Linux (nftables)","：在 accept 出来的 socket 上通过 ",[63,74,75],{},"SO_ORIGINAL_DST"," getsockopt 读取原始目的地址",[42,78,80],{"id":79},"解析出-hostname","解析出 hostname",[14,82,83,84,87],{},"拿到原始 IP 和端口后，还要把 IP 还原成域名，因为上游 HTTP CONNECT 代理需要 ",[63,85,86],{},"CONNECT hostname:port"," 格式的请求。trans_proxy 按下面的优先级确定 hostname：",[89,90,91,97,103],"ol",{},[55,92,93,96],{},[58,94,95],{},"SNI 提取","：对 TLS 连接（端口 443），解析 ClientHello 里的 SNI 扩展字段，直接拿到客户端要访问的域名，不需要解密 TLS 流量",[55,98,99,102],{},[58,100,101],{},"DNS 反查","：如果启用了内置 DNS 转发器，trans_proxy 会记录 DNS 查询结果，可以从里面反查 IP 对应的域名",[55,104,105,108],{},[58,106,107],{},"IP fallback","：两种办法都拿不到域名时，就直接用 IP",[42,110,112],{"id":111},"内置-dns-转发器","内置 DNS 转发器",[14,114,115],{},"trans_proxy 内置了一个 DNS 转发器，监听网关接口的 53 端口，功能包括：",[52,117,118,124,130,136],{},[55,119,120,123],{},[58,121,122],{},"DNS-over-HTTPS (DoH)","：把 DNS 查询通过 HTTPS 发给上游 DNS 服务器（默认 Cloudflare），避免 DNS 污染",[55,125,126,129],{},[58,127,128],{},"HTTP/2 连接池","：复用 HTTP/2 连接，降低 DoH 查询延迟",[55,131,132,135],{},[58,133,134],{},"TTL 缓存","：按 DNS 记录的 TTL 缓存查询结果",[55,137,138,141],{},[58,139,140],{},"查询合并（coalescing）","：合并对同一域名的并发查询，避免重复请求",[14,143,144],{},"除了提供 DNS 服务，它记录的查询结果也给上面的 hostname 解析提供了 IP 到域名的反查依据。",[42,146,147],{"id":147},"防火墙集成",[14,149,150],{},"trans_proxy 通过脚本管理防火墙规则：",[52,152,153,159],{},[55,154,155,158],{},[58,156,157],{},"macOS","：用 pf 的 anchor 机制，把 NAT 重定向规则挂在独立的 anchor 下，不影响系统已有的 pf 规则",[55,160,161,164],{},[58,162,163],{},"Linux","：创建独立的 nftables table，同样不干扰现有的防火墙配置",[10,166,167],{"id":167},"安装与编译",[169,170,175],"pre",{"className":171,"code":172,"language":173,"meta":174,"style":174},"language-bash shiki shiki-themes github-light github-dark","# 克隆仓库\ngit clone https://github.com/madeye/trans_proxy.git\ncd trans_proxy\n\n# 编译（需要 Rust 1.70+）\ncargo build --release\n\n# 编译产物在 target/release/trans_proxy\n","bash","",[63,176,177,186,200,210,217,223,235,240],{"__ignoreMap":174},[178,179,182],"span",{"class":180,"line":181},"line",1,[178,183,185],{"class":184},"sJ8bj","# 克隆仓库\n",[178,187,189,193,197],{"class":180,"line":188},2,[178,190,192],{"class":191},"sScJk","git",[178,194,196],{"class":195},"sZZnC"," clone",[178,198,199],{"class":195}," https://github.com/madeye/trans_proxy.git\n",[178,201,203,207],{"class":180,"line":202},3,[178,204,206],{"class":205},"sj4cs","cd",[178,208,209],{"class":195}," trans_proxy\n",[178,211,213],{"class":180,"line":212},4,[178,214,216],{"emptyLinePlaceholder":215},true,"\n",[178,218,220],{"class":180,"line":219},5,[178,221,222],{"class":184},"# 编译（需要 Rust 1.70+）\n",[178,224,226,229,232],{"class":180,"line":225},6,[178,227,228],{"class":191},"cargo",[178,230,231],{"class":195}," build",[178,233,234],{"class":205}," --release\n",[178,236,238],{"class":180,"line":237},7,[178,239,216],{"emptyLinePlaceholder":215},[178,241,243],{"class":180,"line":242},8,[178,244,245],{"class":184},"# 编译产物在 target/release/trans_proxy\n",[10,247,248],{"id":248},"使用方法",[42,250,157],{"id":251},"macos",[14,253,254,255,258,259,262],{},"假设网关机器的网络接口是 ",[63,256,257],{},"en0","，上游 HTTP CONNECT 代理跑在本地 ",[63,260,261],{},"127.0.0.1:1082","：",[169,264,266],{"className":171,"code":265,"language":173,"meta":174,"style":174},"# 启动 trans_proxy（启用 DNS 转发器）\nsudo ./trans_proxy --upstream-proxy 127.0.0.1:1082 --dns\n\n# 设置 pf NAT 重定向规则\nsudo scripts/pf_setup.sh en0 8443\n",[63,267,268,273,290,294,299],{"__ignoreMap":174},[178,269,270],{"class":180,"line":181},[178,271,272],{"class":184},"# 启动 trans_proxy（启用 DNS 转发器）\n",[178,274,275,278,281,284,287],{"class":180,"line":188},[178,276,277],{"class":191},"sudo",[178,279,280],{"class":195}," ./trans_proxy",[178,282,283],{"class":205}," --upstream-proxy",[178,285,286],{"class":195}," 127.0.0.1:1082",[178,288,289],{"class":205}," --dns\n",[178,291,292],{"class":180,"line":202},[178,293,216],{"emptyLinePlaceholder":215},[178,295,296],{"class":180,"line":212},[178,297,298],{"class":184},"# 设置 pf NAT 重定向规则\n",[178,300,301,303,306,309],{"class":180,"line":219},[178,302,277],{"class":191},[178,304,305],{"class":195}," scripts/pf_setup.sh",[178,307,308],{"class":195}," en0",[178,310,311],{"class":205}," 8443\n",[42,313,163],{"id":314},"linux",[14,316,317,318,321,322,262],{},"假设网关接口是 ",[63,319,320],{},"eth0","，上游代理在 ",[63,323,324],{},"127.0.0.1:7890",[169,326,328],{"className":171,"code":327,"language":173,"meta":174,"style":174},"# 启动 trans_proxy\nsudo ./trans_proxy --upstream-proxy 127.0.0.1:7890 --dns --interface eth0\n\n# 设置 nftables 规则\nsudo scripts/nftables_setup.sh eth0 8443\n",[63,329,330,335,355,359,364],{"__ignoreMap":174},[178,331,332],{"class":180,"line":181},[178,333,334],{"class":184},"# 启动 trans_proxy\n",[178,336,337,339,341,343,346,349,352],{"class":180,"line":188},[178,338,277],{"class":191},[178,340,280],{"class":195},[178,342,283],{"class":205},[178,344,345],{"class":195}," 127.0.0.1:7890",[178,347,348],{"class":205}," --dns",[178,350,351],{"class":205}," --interface",[178,353,354],{"class":195}," eth0\n",[178,356,357],{"class":180,"line":202},[178,358,216],{"emptyLinePlaceholder":215},[178,360,361],{"class":180,"line":212},[178,362,363],{"class":184},"# 设置 nftables 规则\n",[178,365,366,368,371,374],{"class":180,"line":219},[178,367,277],{"class":191},[178,369,370],{"class":195}," scripts/nftables_setup.sh",[178,372,373],{"class":195}," eth0",[178,375,311],{"class":205},[42,377,378],{"id":378},"主要参数",[380,381,382,398],"table",{},[383,384,385],"thead",{},[386,387,388,392,395],"tr",{},[389,390,391],"th",{},"参数",[389,393,394],{},"默认值",[389,396,397],{},"说明",[399,400,401,417,430,443,460,473,488],"tbody",{},[386,402,403,409,414],{},[404,405,406],"td",{},[63,407,408],{},"--listen-addr",[404,410,411],{},[63,412,413],{},"0.0.0.0:8443",[404,415,416],{},"trans_proxy 监听地址",[386,418,419,424,427],{},[404,420,421],{},[63,422,423],{},"--upstream-proxy",[404,425,426],{},"（必填）",[404,428,429],{},"上游 HTTP CONNECT 代理地址",[386,431,432,437,440],{},[404,433,434],{},[63,435,436],{},"--dns",[404,438,439],{},"关闭",[404,441,442],{},"启用内置 DNS 转发器",[386,444,445,450,457],{},[404,446,447],{},[63,448,449],{},"--interface",[404,451,452,454,455],{},[63,453,257],{}," / ",[63,456,320],{},[404,458,459],{},"网关网络接口",[386,461,462,467,470],{},[404,463,464],{},[63,465,466],{},"--dns-upstream",[404,468,469],{},"Cloudflare DoH",[404,471,472],{},"上游 DNS 服务器（支持 UDP 或 DoH）",[386,474,475,480,485],{},[404,476,477],{},[63,478,479],{},"--log-level",[404,481,482],{},[63,483,484],{},"info",[404,486,487],{},"日志级别（trace/debug/info/warn/error）",[386,489,490,498,500],{},[404,491,492,454,495],{},[63,493,494],{},"-d",[63,496,497],{},"--daemon",[404,499,439],{},[404,501,502],{},"以 daemon 方式运行",[42,504,505],{"id":505},"客户端配置",[14,507,508],{},"局域网内的设备只需要两步：",[89,510,511,518],{},[55,512,513,514,517],{},"把",[58,515,516],{},"默认网关","设为运行 trans_proxy 的机器 IP",[55,519,520,521,524,525,527],{},"把 ",[58,522,523],{},"DNS 服务器","设为同一 IP（如果启用了 ",[63,526,436],{},"）",[14,529,530],{},"macOS、iOS、Windows、Linux、Android 等主流平台都支持，设备端不用装任何软件。",[10,532,533],{"id":533},"安装为系统服务",[14,535,536],{},"trans_proxy 支持一键安装为系统服务：",[169,538,540],{"className":171,"code":539,"language":173,"meta":174,"style":174},"sudo ./trans_proxy --upstream-proxy 127.0.0.1:1082 --dns --install\n",[63,541,542],{"__ignoreMap":174},[178,543,544,546,548,550,552,554],{"class":180,"line":181},[178,545,277],{"class":191},[178,547,280],{"class":195},[178,549,283],{"class":205},[178,551,286],{"class":195},[178,553,348],{"class":205},[178,555,556],{"class":205}," --install\n",[52,558,559,564],{},[55,560,561,563],{},[58,562,157],{},"：创建 LaunchDaemon，开机自启",[55,565,566,568,569,454,572,575],{},[58,567,163],{},"：创建 systemd unit，并通过 ",[63,570,571],{},"ExecStartPre",[63,573,574],{},"ExecStopPost"," 自动管理 nftables 规则的加载和清除",[14,577,578],{},"卸载服务：",[169,580,582],{"className":171,"code":581,"language":173,"meta":174,"style":174},"sudo ./trans_proxy --uninstall\n",[63,583,584],{"__ignoreMap":174},[178,585,586,588,590],{"class":180,"line":181},[178,587,277],{"class":191},[178,589,280],{"class":195},[178,591,592],{"class":205}," --uninstall\n",[10,594,595],{"id":595},"排查问题",[14,597,598],{},"遇到问题时，先把日志级别调高观察：",[169,600,602],{"className":171,"code":601,"language":173,"meta":174,"style":174},"sudo ./trans_proxy --upstream-proxy 127.0.0.1:1082 --dns --log-level debug\n",[63,603,604],{"__ignoreMap":174},[178,605,606,608,610,612,614,616,619],{"class":180,"line":181},[178,607,277],{"class":191},[178,609,280],{"class":195},[178,611,283],{"class":205},[178,613,286],{"class":195},[178,615,348],{"class":205},[178,617,618],{"class":205}," --log-level",[178,620,621],{"class":195}," debug\n",[14,623,624],{},"常见问题：",[52,626,627,636,649,658],{},[55,628,629,635],{},[58,630,631,634],{},[63,632,633],{},"/dev/pf"," 权限错误","（macOS）：确认以 root 权限运行",[55,637,638,641,642,645,646,527],{},[58,639,640],{},"NAT 查询失败","：检查防火墙规则是否正确加载（macOS 用 ",[63,643,644],{},"pfctl -sa","，Linux 用 ",[63,647,648],{},"nft list ruleset",[55,650,651,654,655,527],{},[58,652,653],{},"连接超时","：确认上游代理运行正常，检查网关机器是否开启了 IP 转发（Linux 需要 ",[63,656,657],{},"sysctl net.ipv4.ip_forward=1",[55,659,660,663,664,527],{},[58,661,662],{},"DNS 解析失败","：确认 53 端口没有被其他进程占用（",[63,665,666],{},"lsof -i :53",[10,668,670],{"id":669},"与-caddy-正向代理配合","与 Caddy 正向代理配合",[14,672,673,674,678],{},"trans_proxy 需要一个上游 HTTP CONNECT 代理。没有现成的话，可以参考",[27,675,677],{"href":676},"/zh/blog/caddy-tls-http-proxy-with-acme","之前的文章","用 Caddy 搭一个。典型的组合是：",[169,680,685],{"className":681,"code":683,"language":684},[682],"language-text","局域网设备 → trans_proxy（网关） → Caddy HTTPS 正向代理（远程服务器） → 目标网站\n","text",[63,686,683],{"__ignoreMap":174},[14,688,689],{},"这样局域网设备不用任何配置，所有流量自动通过加密隧道转发。",[10,691,692],{"id":692},"小结",[14,694,695],{},"trans_proxy 解决的核心问题是：让局域网内所有设备不做任何配置就能走代理上网。它的好处在于：",[89,697,698,704,710,716,722],{},[55,699,700,703],{},[58,701,702],{},"零配置客户端","：设备只需改网关和 DNS，不用装软件也不用配代理",[55,705,706,709],{},[58,707,708],{},"跨平台","：同时支持 macOS (pf) 和 Linux (nftables) 作为网关",[55,711,712,715],{},[58,713,714],{},"DNS 防污染","：内置 DoH 转发器，从源头解决 DNS 污染",[55,717,718,721],{},[58,719,720],{},"不侵入现有防火墙","：anchor / 独立 table 机制不干扰已有规则",[55,723,724,727],{},[58,725,726],{},"异步高性能","：基于 tokio，每个连接独立调度，适合网关的高并发场景",[729,730,731],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":174,"searchDepth":188,"depth":188,"links":733},[734,735,741,742,748,749,750,751],{"id":12,"depth":188,"text":12},{"id":22,"depth":188,"text":23,"children":736},[737,738,739,740],{"id":44,"depth":202,"text":44},{"id":79,"depth":202,"text":80},{"id":111,"depth":202,"text":112},{"id":147,"depth":202,"text":147},{"id":167,"depth":188,"text":167},{"id":248,"depth":188,"text":248,"children":743},[744,745,746,747],{"id":251,"depth":202,"text":157},{"id":314,"depth":202,"text":163},{"id":378,"depth":202,"text":378},{"id":505,"depth":202,"text":505},{"id":533,"depth":188,"text":533},{"id":595,"depth":188,"text":595},{"id":669,"depth":188,"text":670},{"id":692,"depth":188,"text":692},"/assets/covers/trans-proxy-transparent-gateway.jpg","2026-03-18T00:00:00.000Z","介绍如何用 trans_proxy 在 macOS 或 Linux 上搭建透明代理网关，让局域网内所有设备无需任何配置即可通过上游 HTTP CONNECT 代理访问网络。","md",{},"/posts/trans-proxy-transparent-gateway",{"title":5,"description":754},"posts/trans-proxy-transparent-gateway","PPiLJm2e0GJ_OtuipYVR4v_yl-r3iLAaNVtSZyhT_E8",1788681981101]