Showing Posts From
网络
EasyTier 能 ping 通但 HTTP 不通:排查思路和解决方法
EasyTier 组网后能 ping 通对方,但 HTTP 访问失败——这是隧道已建立、应用层流量被拦截的典型现象。 快速定位(3 条命令) ① 客户端测试 TCP 连通性 curl -v http://对方EasyTierIP:端口Connection refused:网络通,服务没监听 Connection timed out:防火墙或路由拦截 返回 HTTP 内容:服务正常,排查客户端代理② 服务端抓包 sudo tcpdump -i tun0 tcp port 80同时客户端访问一次:抓包结果 结论看到 SYN 流量到了服务器,查服务监听或防火墙看不到 SYN 路由或 EasyTier 转发问题SYN 无 SYN-ACK 防火墙或服务未监听③ 查看监听端口 ss -lntp | grep -E ':80|:443|:8080'原因一:服务只监听 127.0.0.1(最常见) ss -lntp 看到 127.0.0.1:80,EasyTier 的 tun0 流量就无法到达服务。 修改方法: # uvicorn uvicorn main:app --host 0.0.0.0 --port 8000# nginx listen 0.0.0.0:80;# Node.js app.listen(80, '0.0.0.0')原因二:防火墙拦截 tun0 ping 用 ICMP,HTTP 用 TCP,如果防火墙只放行了 ICMP 或者对 EasyTier 网卡有限制: # 查看规则 sudo iptables -L -n sudo ufw status# 临时放行 tun0(测试用) sudo iptables -I INPUT -i tun0 -j ACCEPT sudo iptables -I FORWARD -i tun0 -j ACCEPT永久规则(以 ufw 为例): sudo ufw allow in on tun0原因三:Docker 端口绑定到 localhost docker ps如果看到 127.0.0.1:80->80/tcp,改成: docker run -p 80:80 ...原因四:服务绑定到特定网卡 IP 服务器有多个网卡(公网 IP + EasyTier IP),应用可能只绑到了公网 IP: ss -lntp | grep 8080如果看到 103.x.x.x:8080,需要改为 0.0.0.0:8080。 原因五:代理劫持 HTTP 流量 系统装了 Clash、sing-box 等,可能把内网 IP 的 HTTP 也走了代理: curl --noproxy '*' http://10.x.x.x如果这样能通,就是代理规则问题,把 EasyTier 网段加入 bypass 列表。 原因六:MTU 问题 现象:ping 小包正常,HTTP 建连后卡住或大文件下载失败。 ping -M do -s 1472 目标IP失败则把 EasyTier 网卡 MTU 调小: sudo ip link set tun0 mtu 1280EasyTier 配置自定义 IP 段 默认 dhcp = true 由网络中的 DHCP 节点分配 IP。如果要固定 IP: dhcp = false ipv4 = "10.88.0.2/24"如果加入的是他人的 EasyTier 网络,单方面改客户端配置通常不生效,需要整个网络统一规划地址段。 诊断工具速查 # 确认 EasyTier 接口和 IP ip addr show tun0# 确认路由(流量是否走 tun0) ip route | grep tun0# TCP 连通性测试 nc -vz 目标IP 80# 本机自测(服务端执行) curl -v http://127.0.0.1:80 curl -v http://tun0_IP:80
EasyTier 能 ping 通但 HTTP 访问不了:六种原因排查
现象 EasyTier 组网后:ping 10.126.126.x ✅ 成功 curl http://10.126.126.x ❌ 失败说明 EasyTier 隧道本身正常,问题在 TCP/应用层。 排查顺序 1. Web 服务只监听 127.0.0.1(最常见) ss -tlnp如果看到: 127.0.0.1:8080而不是: 0.0.0.0:8080说明服务拒绝来自 EasyTier 地址的连接。修复方式:服务 正确配置Nginx listen 0.0.0.0:80;Node.js app.listen(8080, '0.0.0.0')FastAPI/uvicorn uvicorn main:app --host 0.0.0.0Python http.server python -m http.server --bind 0.0.0.02. iptables 只放行了 ICMP,没放行 TCP sudo iptables -L -n sudo ufw status sudo firewall-cmd --list-all # CentOS临时放行 EasyTier 网卡: sudo iptables -I INPUT -i tun0 -j ACCEPT sudo iptables -I FORWARD -i tun0 -j ACCEPT如果 HTTP 立刻恢复,说明是防火墙问题,再添加永久规则。3. Docker 端口映射绑定到 127.0.0.1 docker ps如果端口映射是: 127.0.0.1:8080->8080/tcpEasyTier 无法访问。需要改为: docker run -p 8080:8080 ... # 正确 # 不要用 docker run -p 127.0.0.1:8080:8080 ...4. HTTP 请求被系统代理拦截 如果本机装了 Clash/V2Ray/sing-box: curl --noproxy '*' http://10.126.126.x如果加了 --noproxy 后能访问,说明代理规则把 EasyTier 内网地址错误地转发了出去。在代理工具里添加 10.0.0.0/8 为直连规则。5. MTU 问题 现象:ping 小包正常,HTTP 建连后卡住,大文件下载失败。 测试: ping -M do -s 1472 目标IP如果失败,降低 MTU: sudo ip link set tun0 mtu 12806. 路由未覆盖目标网段 ip route | grep tun0确认有类似: 10.126.126.0/24 dev tun0 proto kernel scope link src 10.126.126.1如果没有,HTTP 的 TCP 包会走默认网关出公网。 tcpdump 快速定位(推荐) 在服务端抓包: sudo tcpdump -i tun0 tcp port 80客户端发起请求后看结果:抓包结果 结论看到 SYN 包 流量到达服务端,问题是防火墙或服务监听地址看不到任何包 EasyTier 路由问题,或客户端代理拦截SYN 但没有 SYN-ACK 防火墙拦截(iptables DROP)SYN 后立即 RST 服务没在该端口监听配合 curl -v http://目标IP 看是 Connection refused 还是 Timeout,可以快速缩小问题范围。
Citrix Gateway 三种模式:Full VPN / Clientless / ICA Proxy 怎么区分
在公司里用 Citrix Gateway(以前叫 NetScaler Gateway)远程办公,能不能 ping 内网、能不能 mstsc 到某台内网机器——完全取决于管理员开的是哪种模式。三种模式差别很大: 三种模式 1. Full VPN(完整 VPN) 登录 Gateway 后建立 VPN 隧道,你的电脑相当于接进了公司内网。 能做的: ping 10.0.0.1 mstsc 10.0.0.100 \\10.0.0.50\share内网 API、SMB 共享、RDP 都能直接连。最接近传统 VPN。 2. Clientless VPN(无客户端 VPN) 只能通过 Gateway 的网页入口访问管理员发布过的内网网站: https://gateway.company.com/vpn/index.html ├─ OA 系统 ├─ JIRA ├─ GitLab └─ Exchange OWAGateway 帮你反向代理到内网。只有网页可访问——ping / RDP / SMB 全都不行。 3. ICA Proxy(最常见) 登录 Gateway 后看到的不是网页应用列表,而是远程桌面 / 应用: [Windows 桌面] [SAP] [Outlook] [Chrome]点击后:浏览器下载 .ica 文件 Citrix Workspace 客户端打开 连到内网一台服务器上运行你本机没进内网——只是远程操作一台内网机器。所有操作都在那台远端上完成,本机能看到的只是画面像素。 怎么判断自己是哪种 登录 Gateway 之后: 方法 1:看网卡 ipconfigFull VPN 会多出: 以太网适配器 Citrix Secure Access: 以太网适配器 Citrix VPN Adapter:看到就是 Full VPN。 方法 2:看路由 route printFull VPN 里有内网网段被路由到 Citrix 虚拟网卡: 10.0.0.0 255.0.0.0 10.0.0.1 <Citrix 网卡 IP>方法 3:直接 ping 内网 ping 10.x.x.x telnet 10.x.x.x 3389通 = Full VPN,不通 = Clientless 或 ICA Proxy。 方法 4:登录后看到什么 登录 Gateway 网页后:看到 大概率是一堆网页链接 Clientless VPN桌面 / 应用图标(点了下 ica) ICA Proxy什么都没看到,只有 VPN 状态 Full VPNICA Proxy 想访问其它内网资源 基本上不行。ICA Proxy 的设计初衷就是"给远程用户桌面 / 应用",不给完整内网访问权限。安全模型里这是"最小暴露面"的选择。 想访问需要管理员:开启 Full VPN 策略(Citrix Secure Access Client) 或者把你需要的服务发布成 Clientless 应用 或者给你专门开一个 RDP 会话,你从那台机器上操作传大文件的坑 ICA Proxy 模式下想把本机文件传到远端会话:Citrix Workspace 支持"客户端驱动映射",可以把本地磁盘挂进远端会话,管理员可能禁用 剪贴板:可能双向、可能单向、可能禁用,也是策略控制的 上传下载按钮:Citrix Workspace 本身有,但公司常常禁真被卡住只能靠在远端邮箱收发、通过 WebOA 上传中转等等。或者直接找 IT 开权限。 Full VPN 的坑 即使有 Full VPN 也不等于什么都能访问:分段路由:管理员可能只给部分内网网段路由,比如只给你 10.10.0.0/16,其它照样不通 应用层过滤:Gateway 可以按端口 / 协议限制,明明有路由但 RDP 不通 DNS:需要用公司内 DNS,否则内网域名解析不了route print 看到给了哪些网段,就只能访问那些网段。 一句话总结 Citrix Gateway 三种模式—— Full VPN 拿到内网路由、Clientless 只发布网页、ICA Proxy 只是远端桌面像素。看 ipconfig 有没有 Citrix 网卡最快分辨。想要更多权限,只能找管理员改策略。
CIDR 表示法:/24、/32 的含义和只匹配单个 IP 的写法
CIDR(无类别域间路由)用"IP地址/前缀长度"表示一段 IP 范围,前缀长度决定有多少个 IP 被包含在内。 /24 不等于单个 IP 43.255.122.56/24 表示前 24 位固定,等价于: 43.255.122.0 - 43.255.122.255共 256 个地址,包含 43.255.122.56,但也包含同网段的所有其他地址。 只匹配单个 IP:使用 /32 IPv4 地址是 32 位,/32 表示所有 32 位都固定: 43.255.122.56/32 → 仅匹配 43.255.122.56配置防火墙、ACL、路由规则、Cloudflare IP 规则时,允许或封禁单个 IP 都应使用 /32: # iptables iptables -A INPUT -s 43.255.122.56/32 -j ACCEPT# Nginx allow 43.255.122.56/32;如果不要求 CIDR 格式,直接写 IP 不加后缀也等同于 /32: 43.255.122.56常用前缀长度对照写法 匹配范围 地址数x.x.x.x/32 仅这一个 IP 1x.x.x.x/31 2 个(常用于点对点链路) 2x.x.x.x/30 4 个 4x.x.x.x/29 8 个 8x.x.x.x/28 16 个 16x.x.x.0/24 256 个(常见局域网段) 256x.x.0.0/16 65536 个 65536x.0.0.0/8 16777216 个(A 类网络) 167772160.0.0.0/0 所有 IPv4 地址 全部前缀长度与地址数的计算 地址数 = 2^(32 - 前缀长度)/32 → 2^0 = 1 /24 → 2^8 = 256 /16 → 2^16 = 65536网络地址与广播地址 在标准 IPv4 子网中,最小地址(如 43.255.122.0)是网络地址,最大地址(43.255.122.255)是广播地址,可用主机地址是中间的 254 个。但在防火墙规则和 CIDR 匹配中,这个区分通常不重要,/24 就是匹配全部 256 个地址。
CIDR 记法要写对:43.255.122.56/24 匹配的是一整段
配置防火墙、白名单、路由规则的时候,经常见到有人这样写: 43.255.122.56/24本意是想匹配单个 IP,结果匹配了 256 个。CIDR 的斜杠语法是"前面多少位固定",不是"IP 加上标签"。 /24 到底表示什么 43.255.122.56/24 意思是:IP 的前 24 位固定。IPv4 一共 32 位,24 位固定就是前三段(43.255.122)不变,最后一段(.56)被忽略、整段 0-255 都算命中。 等价于: 43.255.122.0 – 43.255.122.255 (256 个地址)所以: 43.255.122.0 ✅ 命中 43.255.122.56 ✅ 命中 43.255.122.100 ✅ 命中 43.255.122.255 ✅ 命中 43.255.123.56 ❌ 未命中想匹配单个 IP:/32 要精确匹配一个 IP,写 /32——32 位全都固定,就是它自己: 43.255.122.56/32如果配置格式不要求必须有 CIDR 后缀,直接写 IP 也行: 43.255.122.56/32 是最严格的等价形式。 常见 CIDR 对照写法 匹配范围 地址数43.255.122.56/32 只 43.255.122.56 143.255.122.56/31 43.255.122.56 – 57 243.255.122.56/30 43.255.122.56 – 59 443.255.122.56/29 43.255.122.56 – 63 843.255.122.56/28 43.255.122.48 – 63 1643.255.122.56/27 43.255.122.32 – 63 3243.255.122.56/26 43.255.122.0 – 63 6443.255.122.56/25 43.255.122.0 – 127 12843.255.122.56/24 43.255.122.0 – 255 25643.255.122.56/16 43.255.0.0 – 43.255.255.255 65,53643.255.122.56/8 43.0.0.0 – 43.255.255.255 16M+43.255.122.56/0 整个 IPv4 空间 全部注意:CIDR 里"起点 IP"随便写,实际会向下对齐。43.255.122.56/24 和 43.255.122.99/24 等价,都指 43.255.122.0/24 这个网段。 计算方法 给一个 CIDR A.B.C.D/N,怎么算范围? 掩码位数 → 主机位数 = 32 - N 主机位数决定了段长:主机位 = 0:1 个 IP(/32) 主机位 = 1:2 个(/31) 主机位 = 8:256 个(/24) 主机位 = 16:65536 个(/16) 主机位 = N:2^N 个起点:把 A.B.C.D 的后 (32-N) 位置零。 例:10.0.5.37/28N=28,主机位=4 段长 = 2^4 = 16 后 4 位置零:37 二进制 00100101 → 后 4 位置零 00100000 = 32 范围:10.0.5.32 – 10.0.5.47Linux 命令算 ipcalc 是最快的: $ ipcalc 43.255.122.56/24 Address: 43.255.122.56 Netmask: 255.255.255.0 = 24 Network: 43.255.122.0/24 HostMin: 43.255.122.1 HostMax: 43.255.122.254 Broadcast: 43.255.122.255 Hosts/Net: 254或者 Python 一行: import ipaddress net = ipaddress.ip_network("43.255.122.56/24", strict=False) print(net.network_address, net.broadcast_address, net.num_addresses)常见误区 误区 1:以为 /24 加个数字更保险不是。任何斜杠后缀都是"网段掩码",你越加越大。 误区 2:/32 单独 IP 时可以省掉大多数系统能省,但**某些严格配置格式(RBAC 白名单、AWS SG)**要求必须写 /32,别偷懒。 误区 3:以为 /0 是"无匹配"反过来——0.0.0.0/0 匹配整个 IPv4 空间,路由表里就是默认路由。 IPv6 版 IPv6 也用 CIDR: 2001:db8::1/128 单个 IP 2001:db8::/32 一整段 ::/0 整个 IPv6 空间规则一样,只是位数从 32 变成 128。 一句话总结 IP 加 /N 是网段,前 N 位固定其余任意。单个 IP 要写 /32(IPv6 是 /128)。别偷懒不加 CIDR 后缀,也别把 /24 当成"某个 IP 的编号"。
Python 通过 Shadowsocks SOCKS5 代理发送请求
核心原理 Python 不能直接解析 Shadowsocks 协议。标准做法是: Python ↓ SOCKS5 sslocal (本地 1080 端口) ↓ Shadowsocks 协议 远端 SS 服务器 ↓ 明文 目标网站先用 Shadowsocks 客户端在本地起一个 SOCKS5 代理,Python 通过这个代理访问外网。 方法一:requests[socks](推荐) 安装: pip install requests[socks]使用: import requestsproxies = { "http": "socks5://127.0.0.1:1080", "https": "socks5://127.0.0.1:1080" }r = requests.get( "https://httpbin.org/ip", proxies=proxies, timeout=10 )print(r.json())socks5:// 使用远端 DNS 解析;如果想让本地 DNS 解析,换成 socks5h://。 方法二:PySocks 全局劫持 安装: pip install pysocks设置全局默认代理,之后所有 socket 连接都走 SOCKS5: import socket import sockssocks.set_default_proxy(socks.SOCKS5, "127.0.0.1", 1080) socket.socket = socks.socksocketimport requestsr = requests.get("https://httpbin.org/ip") print(r.json())适合需要代理所有网络调用(requests、httpx、urllib 等)的场景,但会影响进程内所有 socket,慎用。 方法三:环境变量 export ALL_PROXY=socks5://127.0.0.1:1080 export HTTPS_PROXY=socks5://127.0.0.1:1080 export HTTP_PROXY=socks5://127.0.0.1:1080或者在 Python 代码里设置: import osos.environ["ALL_PROXY"] = "socks5://127.0.0.1:1080"import requestsr = requests.get("https://httpbin.org/ip") print(r.json())requests 会自动读取 HTTP_PROXY / HTTPS_PROXY / ALL_PROXY 环境变量。 在代码里启动 sslocal 如果想让 Python 程序自己管理 Shadowsocks 子进程: import subprocess import time import requestsproc = subprocess.Popen([ "sslocal", "-s", "服务器IP", "-p", "8388", "-k", "密码", "-m", "aes-256-gcm", "-l", "1080" ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)time.sleep(1) # 等待 sslocal 就绪proxies = { "http": "socks5://127.0.0.1:1080", "https": "socks5://127.0.0.1:1080" }try: r = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10) print(r.json()) finally: proc.terminate()sslocal 来自 shadowsocks-libev 或 shadowsocks-rust 包,需要提前安装。 验证代理是否生效 import requestsproxies = { "http": "socks5://127.0.0.1:1080", "https": "socks5://127.0.0.1:1080" }r = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10) print(r.json()) # 返回的 origin IP 应为代理服务器 IP,不是本机 IP
Cloudflare 525 SSL Handshake Failed:原因排查与修复方法
Cloudflare 525 错误发生在 Cloudflare 与源站之间,不是用户与 Cloudflare 之间。含义:Cloudflare 已经接收到请求,但无法与源站完成 TLS 握手。 快速检测源站 TLS openssl s_client -connect 源站IP:443 -servername 你的域名正常握手会看到: New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384如果出现 handshake failure,问题在 Nginx 或证书配置。 原因一:Nginx 未使用 fullchain 证书(最常见) Let's Encrypt 证书必须用 fullchain.pem,不能只用叶证书: # 正确 ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;检查: nginx -T | grep ssl_certificate如果配置的是 cert.pem 而非 fullchain.pem,Cloudflare 会因为证书链不完整而报 525。 openssl 验证时看到这行也是同一问题: verify error:num=20:unable to get local issuer certificate原因二:TLS 版本过低 Cloudflare 要求至少 TLS 1.2。Nginx 配置: ssl_protocols TLSv1.2 TLSv1.3;原因三:Cipher Suite 过于严格 ssl_ciphers HIGH:!aNULL:!MD5;检查源站支持的 cipher: nmap --script ssl-enum-ciphers -p 443 源站IP原因四:开启了客户端证书验证 ssl_verify_client on;Cloudflare 无法提供匹配的客户端证书,直接导致 525。 检查: nginx -T | grep verify_client原因五:443 端口未监听 telnet 源站IP 443或: curl -vk https://源站IP如果 Connection refused 说明 Nginx 没有监听 443。 原因六:IPv6 配置问题 Cloudflare 可能优先走 IPv6: dig AAAA 你的域名如果有 AAAA 记录,但 Nginx 的 IPv6 监听或防火墙有问题: openssl s_client -connect [IPv6地址]:443 -servername 你的域名测试是否成功。 Cloudflare SSL 模式 Cloudflare Dashboard → SSL/TLS → Overview 查看当前模式:模式 要求Flexible 源站无需 HTTPSFull 源站需要 HTTPS,证书可自签Full (strict) 源站需要有效证书,证书链完整使用 Full (strict) 时,源站必须配置 fullchain.pem。 完整排查步骤 # 1. 检查证书配置 nginx -T | grep ssl_certificate# 2. 测试握手(替换为实际源站 IP) openssl s_client -connect 源站IP:443 -servername 你的域名# 3. 检查 IPv6 dig AAAA 你的域名# 4. 检查客户端验证 nginx -T | grep verify_client# 5. 直接用 curl 测试 curl -Iv https://你的域名
Clash Verge Rev 规则覆写:prepend/append/delete 与 PROCESS-PATH-REGEX 详解
Clash Verge Rev 的规则覆写配置片段: prepend: - 'PROCESS-PATH-REGEX,/Applications/ToDesk.app.*,DIRECT' - 'PROCESS-PATH-REGEX,/Applications/UURemote.app.*,DIRECT' - 'PROCESS-PATH-REGEX,/Applications/WeChat.app.*,DIRECT' append: [] delete: []prepend / append / delete 的含义字段 作用prepend 在规则列表最前面插入,优先匹配append 在规则列表最后面追加,最后匹配delete 从现有规则中删除匹配项prepend 里的规则优先级最高,命中后直接执行,不再往下匹配。 PROCESS-PATH-REGEX 语法 PROCESS-PATH-REGEX,<正则表达式>,<策略>macOS 示例: PROCESS-PATH-REGEX,/Applications/WeChat.app.*,DIRECT匹配进程路径: /Applications/WeChat.app/Contents/MacOS/WeChatWindows 示例: PROCESS-PATH-REGEX,.*\\WeChat\\WeChat.exe,DIRECT注意 Windows 路径需要用 \\ 转义反斜杠。 为什么远程控制软件要设为 DIRECT ToDesk、TeamViewer、向日葵、AnyDesk 等远程桌面软件通过自有中转服务器建立连接,走代理后常见问题:连接黑屏 延迟显著增加 无法建立 P2P 通道 连接频繁中断把这类软件设置为 DIRECT 可以绕过代理,走最短路径连接中转服务器。 常用直连配置 prepend: # 远程桌面 - 'PROCESS-PATH-REGEX,/Applications/ToDesk.app.*,DIRECT' - 'PROCESS-PATH-REGEX,/Applications/UURemote.app.*,DIRECT' - 'PROCESS-PATH-REGEX,/Applications/SunloginClient.app.*,DIRECT' - 'PROCESS-PATH-REGEX,/Applications/TeamViewer.app.*,DIRECT' - 'PROCESS-PATH-REGEX,/Applications/AnyDesk.app.*,DIRECT' # 国内即时通讯 - 'PROCESS-PATH-REGEX,/Applications/WeChat.app.*,DIRECT' - 'PROCESS-PATH-REGEX,/Applications/DingTalk.app.*,DIRECT'查找 macOS 进程真实路径 # 通过进程名查找 ps aux | grep ToDesk# 通过 PID 查看文件 lsof -p <PID> | head -5输出示例: /Applications/ToDesk.app/Contents/MacOS/ToDesk然后写规则: PROCESS-PATH-REGEX,/Applications/ToDesk.app.*,DIRECT让应用走代理 如果需要某个应用强制走代理(和默认行为相反),改策略组名称: prepend: - 'PROCESS-PATH-REGEX,/Applications/SomeApp.app.*,节点选择'前提是策略组 节点选择 在你的 proxy-groups 中存在。
Clash 用 PROCESS-PATH-REGEX 让特定 App 直连
日常科学上网走 Clash 系客户端,大多数应用没问题。但有些特殊 App 不适合走代理:远程控制类(ToDesk、向日葵、AnyDesk、TeamViewer):走代理会连不上或黑屏 微信 / QQ:中转导致延迟大或者协议对不上 游戏:延迟敏感、部分反作弊系统会怀疑 IP 一些内网 App:需要局域网直连Clash Meta(含 Verge Rev、ClashX Meta、OpenClash)提供 PROCESS-PATH-REGEX 规则,按进程路径匹配后强制直连。 规则语法 - 'PROCESS-PATH-REGEX,<正则>,<策略>'例: - 'PROCESS-PATH-REGEX,/Applications/WeChat.app.*,DIRECT'匹配路径包含 /Applications/WeChat.app 的进程,直连。 常用配置(macOS) prepend: # 远程控制软件 - 'PROCESS-PATH-REGEX,/Applications/ToDesk.app.*,DIRECT' - 'PROCESS-PATH-REGEX,/Applications/UURemote.app.*,DIRECT' - 'PROCESS-PATH-REGEX,/Applications/SunloginClient.app.*,DIRECT' - 'PROCESS-PATH-REGEX,/Applications/TeamViewer.app.*,DIRECT' - 'PROCESS-PATH-REGEX,/Applications/AnyDesk.app.*,DIRECT' # 通讯软件 - 'PROCESS-PATH-REGEX,/Applications/WeChat.app.*,DIRECT' - 'PROCESS-PATH-REGEX,/Applications/QQ.app.*,DIRECT' # 开发工具(可选) - 'PROCESS-PATH-REGEX,/Applications/Docker.app.*,DIRECT'append: [] delete: []prepend 表示加到规则最前面(优先级最高,第一个匹配到就返回)。 Windows 上路径 Windows 版路径不同: - 'PROCESS-PATH-REGEX,.*\\WeChat\.exe$,DIRECT' - 'PROCESS-PATH-REGEX,.*\\ToDesk\.exe$,DIRECT' - 'PROCESS-PATH-REGEX,.*\\SunloginClient\.exe$,DIRECT'或者用 PROCESS-NAME(更省心): - 'PROCESS-NAME,WeChat.exe,DIRECT' - 'PROCESS-NAME,ToDesk.exe,DIRECT'PROCESS-NAME 只匹配文件名,不用管路径,跨平台更方便。 放哪儿 Clash Verge Rev: 配置界面 → 配置 → 脚本 / Rules 覆写。规则放在 prepend 数组里。 ClashX / ClashX Meta: ~/.config/clash/config.yaml 的 rules: 数组开头(或用 mixin/override)。 Clash for Windows(已停止更新): Parsers → 编辑 YAML 覆写规则。 想让某 App 走特定节点 改成策略组名字: - 'PROCESS-PATH-REGEX,/Applications/Chrome.app.*,🚀 节点选择'前提是策略组里有这个名字: proxy-groups: - name: 🚀 节点选择 type: select proxies: - 🇺🇸 美国 - 🇯🇵 日本 - 🇭🇰 香港查真实进程路径 不确定 App 的路径,先查一下(macOS): # 按名字找 PID pgrep -x WeChat# 用 PID 查完整路径 lsof -p <PID> | head -5# 或者一句话 ps -ax -o command | grep WeChat | grep -v grepWindows: Get-Process | Where-Object Name -like "*WeChat*" | Select-Object Name, Path规则匹配优先级 Clash 从上往下匹配,第一个命中就返回。所以 prepend 里的规则会覆盖后面的常规规则。 要精细控制某类流量的策略: rules: # 高优先级:某个 App 强制直连 - PROCESS-NAME,WeChat.exe,DIRECT # 中优先级:国内 IP 直连 - GEOIP,CN,DIRECT # 中优先级:广告拦截 - DOMAIN-SUFFIX,doubleclick.net,REJECT # 兜底:所有其它走代理 - MATCH,🚀 节点选择顺序错了效果就变了。 常见问题 1. 规则没生效Clash Meta 才支持 PROCESS-* 系列规则,老版 Clash 不支持。看客户端版本,升级到 Meta 内核。 2. macOS 上要给 Clash 权限系统偏好 → 安全性与隐私 → 隐私 → 允许 Clash 访问其他应用的进程信息。 3. 路径匹配正则要转义. 在正则里是任意字符,严格匹配要写 \.: - 'PROCESS-PATH-REGEX,/Applications/WeChat\.app.*,DIRECT'4. 微信直连了还是登不上微信是走多个域名的,有的不在国内。除了 App 直连,可能还要加域名规则: - DOMAIN-SUFFIX,weixin.qq.com,DIRECT - DOMAIN-SUFFIX,wechat.com,DIRECT一句话总结 远程控制、微信、游戏这类"不适合走代理"的应用,用 PROCESS-PATH-REGEX 或 PROCESS-NAME 走直连。规则要放在 prepend(最高优先级),只有 Clash Meta 内核才支持这类规则。
MITM 抓包代理的实现方案:从用户态代理到驱动层
六种实现层次对比方案 典型工具 特点用户态代理 mitmproxy, Charles, Burp Suite 配置简单,需安装根证书TUN 虚拟网卡 Clash Meta, sing-box, Surge 不依赖系统代理,可抓 UDP/游戏WinDivert WinDivert + 自定义程序 Windows 驱动层,游戏加速器常用WFP 企业安全/DLP 产品 微软官方,性能高,不易检测NDIS Filter Wireshark/Npcap 二层帧,可抓所有流量Socket Hook Frida, Xposed 拦截加密前明文,绕过证书绑定用户态代理(最常见) 流量路径: App → 系统代理 → MITM Proxy → 目标服务器HTTPS 需要:生成 CA 根证书,安装到系统信任 收到 CONNECT hostname:443 后,动态签发 hostname 的域名证书 与客户端建立一条 TLS,与服务端建立另一条 TLS,在中间解密/重新加密TUN 虚拟网卡方案 App → 虚拟网卡(TUN) → 用户态协议栈 → 真实网卡Linux 创建 TUN: int tun_fd = open("/dev/net/tun", O_RDWR); // 配置后:所有流量走 TUN,用户态读 IP 包 read(tun_fd, buf, sizeof(buf));优点:不需要配系统代理,UDP 也能抓,Clash/sing-box 都用这套。 Python 实现最小 HTTPS MITM 第一步:生成 CA openssl genrsa -out ca.key 2048 openssl req -x509 -new -key ca.key -out ca.crt -days 3650安装 ca.crt 到系统根证书信任。 第二步:动态签发域名证书 from cryptography import x509 from cryptography.x509.oid import NameOID from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa import datetimedef gen_cert(hostname: str, ca_key, ca_cert): key = rsa.generate_private_key(public_exponent=65537, key_size=2048) cert = ( x509.CertificateBuilder() .subject_name(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, hostname)])) .issuer_name(ca_cert.subject) .public_key(key.public_key()) .serial_number(x509.random_serial_number()) .not_valid_before(datetime.datetime.utcnow()) .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=365)) .add_extension(x509.SubjectAlternativeName([x509.DNSName(hostname)]), critical=False) .sign(ca_key, hashes.SHA256()) ) return key, cert第三步:TLS 劫持 import ssl# 客户端侧:用伪造证书与客户端握手 def mitm_tls_client(client_socket, hostname, certfile, keyfile): ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) ctx.load_cert_chain(certfile, keyfile) return ctx.wrap_socket(client_socket, server_side=True)# 服务端侧:正常连接真实服务器 def connect_server(hostname, port): ctx = ssl.create_default_context() raw = socket.create_connection((hostname, port)) return ctx.wrap_socket(raw, server_hostname=hostname)两端建立后,中间就能读到明文 HTTP 请求: req = client_tls.recv(8192) print(req.decode()) # POST /v1/chat/completions ...HTTP 解析推荐 不要手动解析 HTTP,使用成熟库: pip install h11 # 轻量,推荐 # 或 pip install httptools证书缓存 对同一域名动态生成一次后缓存,避免每次重复生成: cert_cache = {}def get_cert(hostname): if hostname not in cert_cache: key, cert = gen_cert(hostname, ca_key, ca_cert) cert_cache[hostname] = (key, cert) return cert_cache[hostname]最小模块划分 自己实现一个基础 HTTPS MITM 代理,核心代码约 500~1000 行: proxy/ ├── listener.py # 监听连接 ├── connect.py # 处理 CONNECT 方法 ├── tls.py # TLS 劫持 ├── cert.py # 动态证书生成/缓存 ├── http.py # HTTP 解析 └── logger.py # 请求日志局限性 用户态 HTTP Proxy 在以下场景会遇到问题:QUIC/HTTP3(基于 UDP,不走 CONNECT) App 硬编码不走系统代理 Certificate Pinning(固定证书哈希) gRPC over HTTP2对于需要捕获所有流量的场景(如 AI Agent 全局监听),TUN + gVisor Netstack 扩展性更好。
进程联网监控工具:WFN / Sniffnet / Picosnitch / LuLu
想知道"哪个进程偷偷联网"并实时弹通知,不同系统有不同的成熟开源方案。 Windows:Windows Firewall Notifier(WFN) 最接近需求的工具:进程发起网络连接时弹窗,可以选择允许或拒绝。 特点:基于 Windows 自带防火墙(WFP) 实时弹窗通知:显示进程名、目标 IP/域名、端口 允许/拒绝并记住规则 免费开源(GlassWire 的免费替代)安装后运行即可,不需要修改系统设置。 Windows / Linux / macOS:Sniffnet(流量可视化) Rust 编写,UI 现代,侧重流量监控而非拦截:显示哪个程序在联网、发送/接收了多少流量 支持桌面通知 可过滤特定协议或国家 跨平台(Windows/Linux/macOS)# Windows/macOS 下载安装包 # Linux cargo install sniffnet适合:想了解整体网络情况,而不是逐个审批连接请求。 Linux:Picosnitch eBPF 实现,低开销,功能最强: pip install picosnitch sudo picosnitch start功能:新进程联网时立即通知(systemd/D-Bus 通知) 按可执行文件统计流量 关联父进程(识别子进程发起的连接) 可选接入 VirusTotal 检查新进程 hash Web UI 查看历史记录配置文件(~/.config/picosnitch/config.json): { "Desktop notifications": true, "VT API key": "your_virustotal_api_key" }适合:需要安全审计,或在服务器上追踪异常网络行为。 macOS:LuLu macOS 下最受欢迎的免费联网防火墙:新连接弹窗(允许/拒绝/记住) 显示进程路径和目标地址 支持规则管理 Objective-See 出品(macOS 安全工具知名团队)下载安装包后启用,效果类似 Little Snitch(商业软件)的免费替代。 对比选择工具 系统 特点 是否可拦截WFN Windows 弹窗审批,集成防火墙 ✅Sniffnet 跨平台 流量可视化,UI 好看 ❌Picosnitch Linux eBPF,安全审计,VirusTotal ❌(仅监控)LuLu macOS 弹窗审批,免费 ✅根据场景选择 想控制哪些程序能联网:WFN(Windows)、LuLu(macOS) 想看流量统计和来源:Sniffnet(跨平台) 服务器安全审计/发现异常进程:Picosnitch(Linux) 排查可疑进程(如挖矿木马):先用 Picosnitch/WFN 发现网络连接,再配合 ss -antp | grep <PID> 查目标 IP
跨域 CORS 完整解决方案:后端、Nginx、前端代理全覆盖
跨域的本质 浏览器的同源策略阻止了跨源请求:协议、域名、端口任一不同即为跨域。服务端不受此限制,同源策略是浏览器行为,后端直接请求后端不存在跨域问题。 浏览器在发送非简单请求前会先发一个 OPTIONS 预检请求(preflight),服务端必须正确响应: Access-Control-Allow-Origin: https://example.com Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization后端配置(根本解法) Node.js / Express 最简单的做法是手动加响应头,或使用 cors 中间件: // 手动写 app.use((req, res, next) => { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS"); res.header("Access-Control-Allow-Headers", "Content-Type,Authorization"); if (req.method === "OPTIONS") return res.sendStatus(200); next(); });// 或使用 cors 包 const cors = require("cors"); app.use(cors({ origin: "https://your-frontend.com", methods: ["GET", "POST"], allowedHeaders: ["Content-Type", "Authorization"] }));生产环境不要用 *,写具体允许的 origin。带 credentials 时更不能用 *: app.use(cors({ origin: "https://your-frontend.com", credentials: true // 允许 Cookie }));Spring Boot 单个接口: @CrossOrigin(origins = "*", methods = {RequestMethod.GET, RequestMethod.POST}) @RestController public class MyController { @GetMapping("/api/data") public String getData() { ... } }全局配置(推荐统一管理): @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("https://your-frontend.com") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }Nginx 反向代理配置 适合把前端和后端统一在同一域名下,或给第三方接口套一层代理: location /api/ { proxy_pass http://backend:8080; add_header Access-Control-Allow-Origin $http_origin always; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always; add_header Access-Control-Allow-Headers "Content-Type, Authorization" always; add_header Access-Control-Allow-Credentials true always; if ($request_method = OPTIONS) { return 204; } }always 参数确保在非 200 响应(如 4xx、5xx)时也带上跨域头。 前端开发环境代理 开发阶段用本地代理把请求转发到后端,不产生跨域: Vite: // vite.config.js export default defineConfig({ server: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } } } });Vue CLI / webpack-dev-server: // vue.config.js module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } };Create React App: // package.json { "proxy": "http://localhost:8080" }JSONP(仅 GET,老项目兼容) 利用 <script> 标签不受同源限制: <script src="https://api.example.com/data?callback=onData"></script> <script> function onData(data) { console.log(data); // { "name": "test" } } </script>服务端返回: onData({"name":"test"})JSONP 只支持 GET,现代项目不推荐,但对接老旧第三方接口时可能还会遇到。 后端转发(BFF 模式) 前端请求自己的服务端,由服务端再去请求第三方接口: 浏览器 → 自己的后端(同源)→ 第三方 API这样浏览器始终只看到同源请求,完全绕开 CORS 限制。适合第三方接口不支持配置 CORS 的情况。 方案速查场景 推荐方案生产环境,自己控制后端 后端配置 CORS 响应头生产环境,用 Nginx Nginx 加 Access-Control-* 头开发环境调试 本地代理(Vite/Vue CLI/CRA)第三方接口不支持 CORS 后端转发(BFF)老项目只有 GET 需求 JSONP临时调试(不上线) 浏览器 CORS 插件
TUN/TAP 虚拟网卡与 VPN 代理:TUN0 路由拦截与 TAP0901 驱动
TUN vs TAP特性 TUN TAP模拟对象 IP 网络接口 以太网卡工作层 第三层(IP 层) 第二层(数据链路层)处理单元 IP 数据包 以太网帧支持协议 仅 IP IP、ARP、广播等典型用途 全局路由代理 局域网桥接CPU 开销 较小 较大TUN 适合"全部流量走 VPN",TAP 适合"需要访问远程 LAN 内设备"的桥接场景。 TUN 模式的流量拦截 VPN 客户端启用 TUN 模式的流程:创建虚拟网卡 tun0 修改系统路由表,将默认路由(0.0.0.0/0)指向 tun0 所有出站 IP 包经过 tun0 → VPN 客户端加密封装 → 通过真实网卡发送到 VPN 服务器[本地应用] | (IP 包) v [tun0 虚拟网卡] | (加密封装) v [VPN 客户端程序] | v [VPN 服务器 -> 目标网络]TUN 全局模式会代理 localhost 开启全局代理时,若未排除环回地址,127.0.0.1 的流量也会被 tun0 拦截,导致本地服务访问失败。 排除 localhost(OpenVPN 配置) route 127.0.0.1 255.0.0.0 net_gateway或在客户端设置中勾选"分流模式",只代理外网 IP。 TAP0901 驱动(Windows) TAP-Windows Adapter V9(tap0901)是 OpenVPN 在 Windows 上使用的虚拟以太网卡驱动,安装后在网络适配器列表中以普通网卡形式出现。 驱动通过设备文件暴露接口: \\.\Global\{TAP-Adapter-GUID}.tapJava 通过 JNA 读写数据包 // 打开设备文件 HANDLE h = Kernel32.INSTANCE.CreateFile( "\\\\.\\Global\\{GUID}.tap", WinNT.GENERIC_READ | WinNT.GENERIC_WRITE, 0, null, WinNT.OPEN_EXISTING, WinNT.FILE_ATTRIBUTE_NORMAL, null );// 读取以太网帧 byte[] buf = new byte[2048]; IntByReference bytesRead = new IntByReference(); Kernel32.INSTANCE.ReadFile(h, buf, buf.length, bytesRead, null);// 写入以太网帧 Kernel32.INSTANCE.WriteFile(h, buf, bytesRead.getValue(), bytesRead, null);操作虚拟网卡需要管理员权限。TAP0901 只在 Windows 上可用;Linux 和 macOS 使用系统内置的 TUN/TAP 接口。 实现 VPN 客户端的选型目标 方案 难度仅代理指定应用流量 纯 Socket / SOCKS5 代理 低全局系统流量拦截 TUN/TAP 驱动 + JNI/JNA 高Java 只能做用户空间的协议与加密逻辑;全局流量拦截必须借助内核级虚拟网卡驱动(TUN/TAP)。
