315 lines
9.7 KiB
Markdown
315 lines
9.7 KiB
Markdown
# 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. 业务架构设计
|
||
|
||
{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 的全链路隐私保护体系,确保业务在复杂网络环境下兼顾安全与稳定。
|