Skip to content

About

中国大陆 GitHub 访问恢复:本机 127.0.0.1:443 上的透明 TLS 反代。转发时不发送 SNI,绕过按域名精确匹配的阻断。并发竞速选路 + 上游连接池 + 故障分级,零第三方依赖,147 项离线自测 + 真 git push 端到端。

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

8 Commits

Folders and files

Repository files navigation

github-no-sni-proxy

中国大陆网络环境下让 GitHub 恢复可用:跑在本机 127.0.0.1:443 上的透明 TLS 反向代理。

它凭什么能通

GFW 对 GitHub 的阻断按 TLS 握手里的 SNI 字段精确匹配 —— 这解释了两件事:

  • 换 IP、改 hosts、换 DNS 全都无效(阻断不认 IP);
  • 同一 IP、同一时刻,发 SNI 就超时,不发 SNI 就能拿到完整内容。

所以思路很直接:浏览器照常访问 https://github.com,由 hosts 把域名指到本机, 反代完成 TLS 中间人,在转发时不发送 SNI,阻断就失效了。

与 FastGithub / Watt Toolkit 同源思路,区别是这里纯 Python 标准库实现: 不需要内核驱动,不改系统代理,不与其他代理软件争夺流量。

特性

  • 零第三方依赖 —— 只用 Python 标准库,Python 3.11+ 直接跑
  • 并发竞速选路 —— 4 个 IP × (正常 SNI / 无 SNI) × 2 轮,谁先握手成功用谁
  • 连接池复用 —— 避免每个请求都重吃一遍「握手成功但请求被吞」的十几秒
  • 故障分级处理
    • 通道级(体传输中断 / 刚建连接等不到响应头)→ 惩罚该 SNI 模式 180s,换另一条
    • 虚拟主机级(IP 真实但不属于该域名)→ 拉黑该 IP 600s
  • 流式转发 —— 响应体不落内存,几百 MB 的仓库打包也不会撑爆
  • 分帧严格按类型收尾 —— chunked 必须读到收尾块、length 必须读满、 只有 eof 才是「上游关连接 = 正常结束」
  • 白名单收口 —— 只有 GitHub 相关域名会转发,其余一律 403
  • 自带 CRL 分发 —— 证书声明 CRL 分发点、反代顺手把它喂出去, 于是 curl / git 在 Windows 上不需要任何开关(见下)
  • 内置自测 —— 147+ 项离线单测 + 真 git push 端到端,不联网约 12s 跑完

目录结构

gh-proxy.py      反代主体(唯一的网络核心)
deploy.py        一键部署:备份 hosts → 装 CA → 写映射 → 起反代
undeploy.py      一键卸载:摘映射 → 删 CA → 删自启(可逆,带快照)
status_gui.py    状态监控窗口(纯 tkinter)
restart_proxy.py 重启逻辑(停旧起新)
stop_proxy.py    停进程逻辑(带防误杀核对)
gen_certs.py     生成私有 CA + 服务器证书
selftest.py      离线自测(A 段单测 + C 段真 git push 端到端)
certs/           openssl 配置 + 运行时生成的证书(证书本体不入库)
使用说明.txt      详细中文文档

快速开始

前置条件

  • Windows 10 / 11
  • Python 3.11 或更高(下载,安装时勾选 Add python.exe to PATH)
  • 管理员权限(写 hosts、监听 443、装根证书都需要)
  • openssl(Git for Windows 自带,装 Git 即可)

三步

1. 生成证书(每人必须自建,仓库内不含任何私钥)

python gen_certs.py

2. 部署

双击 一键部署.bat(会自动申请管理员权限)。 它会:备份 hosts → 把私有 CA 装进系统信任区 → 写入 36 条域名映射 → 启动反代。

3. 验证

双击 状态监控.bat,点「立即全链路测试」。 六行全部变绿 = 通了。

安全模型

项 做法
CA 私钥 每人自建,仓库永不包含
上游证书链 一律验证;「正常 SNI」模式连主机名一起核
「无 SNI」模式 没有主机名可核 → 用根证书 SHA-256 钉扎,链根不在白名单就拒
转发范围 域名白名单,非 GitHub 域名返回 403
alt-svc 响应头 剥掉,否则浏览器记住 HTTP/3 地址后会绕过本代理

⚠️ 这是 TLS 中间人。私有 CA 私钥等同于你所有 HTTPS 流量的伪造能力, 请勿外泄,也不要把 certs/*.key 提交到任何仓库(.gitignore 已排除)。

设计取舍与风险边界

这个工具的存在理由只有一条:到 GitHub 的链路存在概率性、时变的干扰。 从这条事实出发,每个机制都对应一条环境约束——没有为炫技而生的部件:

环境约束 必然需求 对应机制
失效是概率性的(同时刻部分 IP 通、部分不通) 静态白名单必然失效 → 探索 + 记忆 4 IP × 2 模式竞速、GOOD_PEER 直连、模式提升/惩罚
干扰会适应(今天通的通道明天被盯上) 单一机制会被磨掉 → 在线轮换 模式惩罚、IP 拉黑、预算过期回收
效率瓶颈在握手(每页几十次请求) 复用是唯一根本杠杆 连接池 keep-alive + 流式搬运(内存与文件大小无关)
无 SNI 是根本反制,但丢名字=丢认证 认证缺口只能补偿 链验证 + 根证书钉扎(预共享秘密)

三支柱现状

  • 专注(满分):白名单 + 纯标准库 + 单文件 + 不碰内核。每个「没有」 (不做通用代理、无第三方依赖、无驱动)都是主动的范围收缩—— 专注换来了一个人就能完成全量审计的可维护性。
  • 高效(热路径达标):记忆命中时延迟由链路主导(API/静态资源亚秒级); 记忆失效时的重新探索以秒计,这是对抗环境的必然成本,设计只在必要时支付 (热路径直达、付过就记住、同一条税不重复交)。
  • 稳定(逻辑层达标):所有失败被分类并映射到恰好一个纠正动作; 预算体系保证「重试在结构上永远可能」;全部超时锚定实测分布并留 15 倍以上裕度。

两处理论边界(实现无法再优化的部分)

  1. 「无 SNI」模式的认证缺口是信息论层面的。「不透露名字」与「用名字做 TLS 认证」不可兼得;在没有预分发秘密的现实中,根证书钉扎就是那个秘密。 残余风险:持有任意受信 CA 证书的在路者可在此模式下伪造响应——这是 该选择的下界,不是瑕疵。缓解靠 SNI 优先 + 钉扎 + 错误虚拟主机检测三层。
  2. MITM 架构意味着永远握着信任根。所有效率与稳定收益,都以「永久维护 CA 私钥卫生」为对价。私钥一旦泄露等同于全部 HTTPS 流量可被伪造—— 这是架构内生负债,需要永久警觉(文件权限 + 不入库 + 每人自建)。

运行层短板(当前已知)

代码有预算与自愈,但进程本身没有守护:无看门狗、无崩溃自启。 若系统策略阻止注册自启任务,可用手动方式创建(见 手动添加开机自启-GUI教程), 或退回「双击 启动反代.bat」的最低保障。

验证状态

190+ 条离线断言(含真 git push 端到端)+ 27 项沙盒对抗矩阵 (慢握手/慢滴灌/请求夹带/上游投毒/并发洪水,全部按预算精确收敛)。 对抗性验证与静态审计的报告随仓库演进持续更新。

为什么 Windows 上的 curl 会「无缘无故」打不开

这是本项目踩过的最隐蔽的一个坑,值得单独说明。

Windows 上 curl / git 走的是 schannel(系统原生 TLS),它会强制做 证书吊销检查(CRL / OCSP)。而本方案出示的证书由你自己的私有 CA 签发 —— 如果那张证书没声明任何吊销源,schannel 就找不到东西可查,直接报:

curl: (35) schannel: next InitializeSecurityContext failed:
CRYPT_E_NO_REVOCATION_CHECK (0x80092012) - 吊销功能无法检查证书是否吊销。

注意错误信息的误导性:它读起来像「CRL 服务器连不上」,实际是 「证书里压根没写 CRL 地址」。两者修法完全不同。

而且只有 curl 会中招 —— Git for Windows 对吊销失败的处理宽松得多, git clone 默认就能过。所以排查时务必把 git 和 curl 分开实测, 不要假设「两个都坏了」。

本项目的做法(gen_certs.py + gh-proxy.py 配合):

  1. 给服务器证书加 crlDistributionPoints = http://127.0.0.1:<端口>/ca.crl
  2. 生成对应的 CRL 文件
  3. 反代在同端口起一个只服务该 CRL 的极小 HTTP 监听

schannel 取到 CRL → 吊销检查真正成功 → 放行。 于是 curl / git / 任何 schannel 工具都不需要任何开关。

实测(2026-09-21,ctypes 直调 crypt32 读链的 TrustStatus): file:// 形式的 CRL 分发点Windows 链引擎不读,吊销状态仍是 UNKNOWN, 只有 http:// 才真正被取回。所以别图省事改成 file://。

若该监听因故未起来,反代会在启动横幅里明确报警,此时回退方案是给 curl 加 --ssl-revoke-best-effort(仍尝试检查,只是不硬失败), 写进 %USERPROFILE%\.curlrc 即全局生效。

它到底改了什么

只有三处,且都可逆:

  1. hosts 的标记块 —— 36 条 127.0.0.1 <域名>,夹在 # === gh-proxy BEGIN === / # === gh-proxy END === 之间。 卸载时只摘这一段,其余条目原样保留。
  2. 系统 Root 存储里的私有 CA —— 卸载时移除。
  3. 127.0.0.1:443 监听 —— 关掉进程即释放。

不改:系统代理设置(ProxyEnable / ProxyServer 原样)、 不碰 v2rayN / Watt Toolkit / Clash 等其它代理软件、不注册常驻服务(除非你主动运行 开机自启.bat)。

日常操作

想做什么 双击
启动反代(前台,能看输出) 启动反代.bat
改完代码后让它生效 重启反代.bat
体检 状态监控.bat
开机自动跑 开机自启.bat
恢复原样 一键卸载.bat

⚠️ 改完 gh-proxy.py 必须重启才生效 —— 反代是常驻进程, 加载的是它启动那一刻的代码。

自测

python selftest.py -A

-A 跑 A 段(离线单测)+ C 段(真 git push 端到端),不联网,约 12 秒。 改代码后建议先跑这个 —— 联机试验会被网络波动掩盖真正的 bug。

跑全量(含联网链路):

python selftest.py

故障速查

症状 原因 处理
浏览器报「连接被拒绝」 hosts 还在拦截,但 443 没进程监听 双击 启动反代.bat
反代在跑,但某些域名 502 链路抖动,或某个 SNI 模式被针对 等 3 分钟(模式惩罚自动过期),看监控窗口是哪条
面板显示「pid 文件过期」但端口在听 面板判活异常 新版已改用端口属主核对,不会再误报
重启反代.bat 报「拒绝访问」 旧进程在更高完整性等级下运行 注销重登后再试

已知限制

  • 只支持 Windows —— hosts 路径、certutil、schtasks 都绑定了 Windows
  • 只代理 GitHub 域名 —— 白名单机制,不做通用代理
  • 不支持 WebSocket(收到 Upgrade 请求会按 RFC 剥掉 Connection: Upgrade 等头再转发)
  • 反代进程本身不监听公网,仅 127.0.0.1

免责声明

本项目用于恢复对 GitHub 的正常访问,不提供任何绕过付费/授权的手段。 请遵守当地法律法规与 GitHub 服务条款。使用者自行承担风险。

许可

MIT,见 LICENSE。

About

中国大陆 GitHub 访问恢复:本机 127.0.0.1:443 上的透明 TLS 反代。转发时不发送 SNI,绕过按域名精确匹配的阻断。并发竞速选路 + 上游连接池 + 故障分级,零第三方依赖,147 项离线自测 + 真 git push 端到端。

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages