# GoEdge HTTPDNS 技术需求文档v2.5 ## 1. 文档概览 ### 1.1 背景 目前 GoEdge 已经具备了强大的边缘缓存、WAF 和基础 DNS 功能。但在移动端应用中,传统的 UDP DNS 容易受到运营商劫持。此外,即便使用了 HTTPDNS,HTTPS 握手阶段明文传输的 **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 3(ECH)。 - **公共 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 的全链路隐私保护体系,确保业务在复杂网络环境下兼顾安全与稳定。