Files
waf-platform/GoEdge HTTPDNS 技术需求文档v2.5(1).md
2026-02-20 17:56:24 +08:00

315 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# GoEdge HTTPDNS 技术需求文档v2.5
## 1. 文档概览
### 1.1 背景
目前 GoEdge 已经具备了强大的边缘缓存、WAF 和基础 DNS
功能。但在移动端应用中,传统的 UDP DNS
容易受到运营商劫持。此外,即便使用了 HTTPDNSHTTPS 握手阶段明文传输的
**SNI (Server Name Indication)** 字段仍会导致真实域名被 ISP
嗅探、封锁或审计。
### 1.2 目标
开发独立的 **edge-httpdns** 模块,并配合边缘节点提供一套对 App
业务透明、可渐进演进的 **「SNI
隐藏能力」**,解决域名旁路感知问题,满足金融、出海等隐私敏感业务的合规与安全对抗需求。
## 2. 业务架构设计
![](media/image1.png){width="6.5in" height="3.513888888888889in"}
### 2.1 角色定义
- **管理端 (edge-admin/edge-user)**
- **配置中心**:管理 AppID/Secret下发解析路由策略及 **SNI
> 隐藏等级策略**。
- **凭证中心**:复用第三方 DNS 运营商的 API 凭证。
- **HTTPDNS 节点 (edge-httpdns)**:独立解析网关,支持
> IPv4/IPv6、批量解析、ECS 透传,并下发 **TLS 指纹/证书校验策略**。
- **权威 DNS 节点 (edge-dns)**:处理传统 Port 53 的 DNS 请求。
- **边缘节点 (edge-node)**:流量网关,支持 **SNI 与 Host
> 解耦路由**,执行 WAF 动态验签及 **SNI 隐匿/ECH 解密** 逻辑。
## 3. 功能需求
### 3.1 管理端配置 (Admin/User)
- **SNI 隐藏平台管理**
- **能力级别选择**:支持 Level 1固定 SNI、Level 2隐匿
> SNI、Level 3ECH
- **公共 SNI 域名池**:配置用于伪装的 Public SNI 域名及证书。
- **证书校验策略**:配置是否开启证书 Pinning 或 SAN 域名强制校验。
- **常规配置**AppID 认证管理、ECS 掩码配置、第三方 DNS 凭证复用。
### 3.2 节点 API 接口
- **Endpoint**: https://httpdns.example.com/resolve
- **新增返回字段**:
- sni_policy: 隐藏等级none/mask/empty/ech
- public_sni: 当级别为 mask 时下发的伪装域名。
- cert_fingerprint: 用于证书绑定的公钥指纹。
## 4. 技术实现方案 (Go)
### 4.1 边缘节点 SNI 路由解耦逻辑
边缘节点需修改 TLS 接入层,支持不依赖 SNI 的握手逻辑。
// 运行在 edge-node TLS 接入层\
func (this \*TLSEngine) HandleHandshake(conn \*tls.Conn) {\
clientHello := conn.GetClientHello()\
\
// 逻辑:如果 SNI 为空或为公共伪装域名\
if clientHello.ServerName == \"\" \|\|
isPublicSNI(clientHello.ServerName) {\
// 1. 使用默认证书或公共 SNI 证书完成握手\
// 2. 握手完成后,进入 HTTP 处理器\
// 3. 从加密的 HTTP Header 中提取真实 Host\
realHost := extractRealHost(conn)\
\
// 4. 将请求路由至对应域名的虚拟主机/WAF\
this.RouteToVirtualHost(realHost, conn)\
}\
}
### 4.2 携带终端 IP 的外部递归查询 (ECS 实现)
当本地无自定义解析时HTTPDNS 模块需向外部 DNS 发起带终端 IP
信息的递归查询,以确保调度精准。
import (\
\"\[github.com/miekg/dns\](https://github.com/miekg/dns)\"\
\"net\"\
)\
\
// 携带 ECS 选项向外部 DNS 发起查询\
func (s \*HTTPDNSService) ResolveWithECS(host string, clientIP string)
(\[\]string, uint32) {\
m := new(dns.Msg)\
m.SetQuestion(dns.Fqdn(host), dns.TypeA)\
\
// 构造 EDNS0 子网选项 (RFC 7871)\
opt := new(dns.OPT)\
opt.Hdr.Name = \".\"\
opt.Hdr.Rrtype = dns.TypeOPT\
\
e := new(dns.EDNS0_SUBNET)\
e.Code = dns.EDNS0SUBNET\
e.Family = 1 // IPv4\
\
// 隐私掩码处理IPv4 抹除后 8 位 (/24)IPv6 抹除后 72 位 (/56)\
e.SourceNetmask = 24\
e.SourceScope = 0\
e.Address = net.ParseIP(clientIP).To4()\
\
opt.Option = append(opt.Option, e)\
m.Extra = append(m.Extra, opt)\
\
// 发送到上游权威 DNS\
c := new(dns.Client)\
in, \_, err := c.Exchange(m, \"8.8.8.8:53\")\
if err != nil \|\| in.Rcode != dns.RcodeSuccess {\
return nil, 0\
}\
\
var ips \[\]string\
var minTTL uint32 = 300\
for \_, ans := range in.Answer {\
if a, ok := ans.(\*dns.A); ok {\
ips = append(ips, a.A.String())\
minTTL = a.Header().Ttl\
}\
}\
return ips, minTTL\
}
## 5. 安全性需求 (Security)
### 5.1 动态指纹与防重放
- 业务请求必须携带 HMAC-SHA256 签名,强制 5
> 分钟时间窗口校验。算法Hex(HMAC_SHA256(Secret, AppID +
> Timestamp + Path))。
### 5.2 SNI 隐藏安全收益
- **防嗅探**旁路观察者ISP/中间人)无法通过 TLS ClientHello
> 识别真实业务域名。
- **防封锁**:有效对抗基于域名的封锁、限速和审计。
## 6. 监控与日志
- **SNI 使用率统计**:记录明文/伪装/隐匿 SNI 的比例。
- **TLS 成功率指标**:监控不同 SNI 策略对连接成功率的影响。
## 7. 终端 SDK 需求 (iOS/Android/Flutter)
### 7.1 核心解析与缓存
- **双级缓存**:内存+加密持久化存储。
- **软过期策略**:优先返回过期缓存,后台异步更新,保证业务 0 延迟。
### 7.2 连接管理与 TLS 控制 (关键SNI 隐藏)
SDK 必须支持 IP 直连,并在 TLS 握手阶段根据服务端下发的策略控制 SNI。
#### 7.2.1 Android 实现 (OkHttp)
通过自定义 SSLSocketFactory 或 HostnameVerifier 来控制 SNI 置空。
class GoEdgeHttpDnsInterceptor(val appId: String, val secret: String) :
Interceptor {\
override fun intercept(chain: Interceptor.Chain): Response {\
val originalRequest = chain.request()\
val host = originalRequest.url.host\
val nodeIp = HttpDnsManager.resolve(host) ?: return
chain.proceed(originalRequest)\
\
// 1. 替换为 IP 地址\
val newUrl = originalRequest.url.newBuilder().host(nodeIp).build()\
val ts = System.currentTimeMillis() / 1000\
val token = calcHmac(secret,
\"\$appId\$ts\${originalRequest.url.encodedPath}\")\
\
val newRequest = originalRequest.newBuilder()\
.url(newUrl)\
.header(\"Host\", host) // 必须手动设回原始 Host\
.header(\"X-GE-AppID\", appId)\
.header(\"X-GE-Timestamp\", ts.toString())\
.header(\"X-GE-Token\", token)\
.build()\
\
// 2. 特殊处理:置空 SNI (Level 2)\
// 注意:在 OkHttp 中,若使用 IP 直连,默认 SNI 会是 IP 地址。\
// 为实现 SNI 隐藏,需在自定义 SSLSocketFactory 中将 peerHost 设为 null
或 Public SNI。\
return chain.proceed(newRequest)\
}\
}
#### 7.2.2 iOS 实现 (Swift/URLSession)
利用 URLSessionDelegate 接管握手过程。
class GoEdgeSessionDelegate: NSObject, URLSessionTaskDelegate {\
func urlSession(\_ session: URLSession, task: URLSessionTask, didReceive
challenge: URLAuthenticationChallenge, completionHandler: \@escaping
(URLSession.AuthChallengeDisposition, URLCredential?) -\> Void) {\
if challenge.protectionSpace.authenticationMethod ==
NSURLAuthenticationMethodServerTrust {\
let serverTrust = challenge.protectionSpace.serverTrust!\
\
// SNI 隐藏逻辑App 内部维护一个 Host 映射\
// 苹果底层 Network.framework 在使用 IP 直连时可能不发送 SNI\
// 我们在此处验证证书是否包含原始域名 (api.example.com)\
let originalHost = \"api.example.com\"\
let policies = \[SecPolicyCreateSSL(true, originalHost as CFString)\]\
SecTrustSetPolicies(serverTrust, policies as CFArray)\
\
completionHandler(.useCredential, URLCredential(trust: serverTrust))\
}\
}\
}
#### 7.2.3 Flutter 实现 (Dio)
通过自定义 HttpClientAdapter 实现 IP 直连与 SNI 控制。
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(client) {\
client.findProxy = (uri) => \"DIRECT\";\
client.badCertificateCallback = (cert, host, port) {\
// 在 IP 直连模式下,这里的 host 是 IP\
// 我们需要验证证书中的 SAN 是否包含原始域名\
return true;\
};\
};\
\
// 拦截器注入\
dio.interceptors.add(InterceptorsWrapper(\
onRequest: (options, handler) async {\
final originalHost = options.uri.host;\
final nodeIp = await httpDns.resolve(originalHost);\
if (nodeIp != null) {\
options.path = options.path.replaceFirst(originalHost, nodeIp);\
options.headers\[\"Host\"\] = originalHost; // 注入加密 Host\
// \... 注入 X-GE-Token 等动态指纹\
}\
handler.next(options);\
}\
));
## 8. 故障感知与 IP 切换
1. **Happy Eyeballs**IPv4/IPv6 竞速。
2. **本地熔断**:单 IP 故障标记为黑名单5 分钟内不使用。
## 9. App 端集成工作流程
1. **启动校准** -\> 2. **预解析** -\> 3. **业务请求(根据策略置空
> SNI** -\> 4. **WAF 验签与路由** -\> 5. **质量上报**。
## 10. 前置条件与域名接入要求
- **回源与接入**:业务域名必须在 GoEdge 管理后台配置完成。
- **多证书管理**:边缘节点需部署业务证书及公共 SNI 伪装证书。
## 11. iOS SDK 合规与审核注意事项
- **隐私清单**:声明不收集用户设备指纹。
- **ATS 策略**:解析接口强制 HTTPS。
## 12. 域名/IP 被封锁场景容灾
- **种子 IP**SDK 内置原生 IP。
- **白域名伪装**:当 SNI 阻断严重时,使用合法白域名进行 TLS 握手伪装。
## 13. HTTPDNS 服务高可用 (HA) 解决方案
- **Anycast/GSLB** 解析节点路由。
- **分级降级**API 域名 -\> 种子 IP -\> 备用域名 -\> LocalDNS。
## 14. SNI 隐藏能力分级设计 (Capability Levels)
- **Level 1 (基础)**Public SNI 伪装。
- **Level 2 (核心)**IP 直连 + SNI 置空 + 加密 Host 路由。
- **Level 3 (前瞻)**ECH (Encrypted ClientHello)。
## 15. 里程碑规划
- **P1**HTTPDNS 基础 + 固定 SNI。
- **P2**IP 直连 + SNI 置空控制。
- **P3**ECH 支持。
**结语:**
本方案通过将 HTTPDNS 与 SNI 隐藏能力有机结合,构建了覆盖 DNS、TLS、CDN
和 SDK 的全链路隐私保护体系,确保业务在复杂网络环境下兼顾安全与稳定。