增加DNSSEC验证支持
This commit is contained in:
101
dns/server.go
101
dns/server.go
@@ -141,6 +141,7 @@ func NewServer(config *config.DNSConfig, shieldConfig *config.ShieldConfig, shie
|
||||
resolver: &dns.Client{
|
||||
Net: "udp",
|
||||
Timeout: time.Duration(config.Timeout) * time.Millisecond,
|
||||
UDPSize: 4096, // 增加UDP缓冲区大小,支持更大的DNSSEC响应
|
||||
},
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
@@ -544,19 +545,103 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
|
||||
// 尝试所有上游DNS服务器
|
||||
for _, upstream := range s.config.UpstreamDNS {
|
||||
response, rtt, err := s.resolver.Exchange(r, upstream)
|
||||
if err == nil && response != nil && response.Rcode == dns.RcodeSuccess {
|
||||
if err == nil && response != nil {
|
||||
// 设置递归可用标志
|
||||
response.RecursionAvailable = true
|
||||
|
||||
logger.Debug("DNS查询成功", "domain", domain, "rtt", rtt, "server", upstream)
|
||||
// 完整DNSSEC支持:验证DNSSEC签名
|
||||
if s.config.EnableDNSSEC {
|
||||
// 提取DNSKEY和RRSIG记录
|
||||
dnskeys := make(map[uint16]*dns.DNSKEY) // KeyTag -> DNSKEY
|
||||
rrsigs := make([]*dns.RRSIG, 0)
|
||||
|
||||
// 记录解析域名统计
|
||||
s.updateResolvedDomainStats(domain)
|
||||
// 从响应中提取所有DNSKEY和RRSIG记录
|
||||
for _, rr := range response.Answer {
|
||||
if dnskey, ok := rr.(*dns.DNSKEY); ok {
|
||||
tag := dnskey.KeyTag()
|
||||
dnskeys[tag] = dnskey
|
||||
} else if rrsig, ok := rr.(*dns.RRSIG); ok {
|
||||
rrsigs = append(rrsigs, rrsig)
|
||||
}
|
||||
}
|
||||
for _, rr := range response.Ns {
|
||||
if dnskey, ok := rr.(*dns.DNSKEY); ok {
|
||||
tag := dnskey.KeyTag()
|
||||
dnskeys[tag] = dnskey
|
||||
} else if rrsig, ok := rr.(*dns.RRSIG); ok {
|
||||
rrsigs = append(rrsigs, rrsig)
|
||||
}
|
||||
}
|
||||
for _, rr := range response.Extra {
|
||||
if dnskey, ok := rr.(*dns.DNSKEY); ok {
|
||||
tag := dnskey.KeyTag()
|
||||
dnskeys[tag] = dnskey
|
||||
} else if rrsig, ok := rr.(*dns.RRSIG); ok {
|
||||
rrsigs = append(rrsigs, rrsig)
|
||||
}
|
||||
}
|
||||
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.Allowed++
|
||||
})
|
||||
return response, rtt
|
||||
// 如果有RRSIG记录,尝试验证签名
|
||||
if len(rrsigs) > 0 {
|
||||
logger.Debug("DNS响应包含DNSSEC记录", "domain", domain, "server", upstream, "rrsig_count", len(rrsigs), "dnskey_count", len(dnskeys))
|
||||
|
||||
// 验证签名
|
||||
signatureValid := true
|
||||
for _, rrsig := range rrsigs {
|
||||
// 查找对应的DNSKEY
|
||||
dnskey, exists := dnskeys[rrsig.KeyTag]
|
||||
if !exists {
|
||||
logger.Warn("DNSSEC验证失败:找不到对应的DNSKEY", "domain", domain, "server", upstream, "key_tag", rrsig.KeyTag)
|
||||
signatureValid = false
|
||||
continue
|
||||
}
|
||||
|
||||
// 收集需要验证的记录集
|
||||
rrset := make([]dns.RR, 0)
|
||||
for _, rr := range response.Answer {
|
||||
if rr.Header().Name == rrsig.Header().Name && rr.Header().Rrtype == rrsig.TypeCovered {
|
||||
rrset = append(rrset, rr)
|
||||
}
|
||||
}
|
||||
for _, rr := range response.Ns {
|
||||
if rr.Header().Name == rrsig.Header().Name && rr.Header().Rrtype == rrsig.TypeCovered {
|
||||
rrset = append(rrset, rr)
|
||||
}
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
if len(rrset) > 0 {
|
||||
err := rrsig.Verify(dnskey, rrset)
|
||||
if err != nil {
|
||||
logger.Warn("DNSSEC签名验证失败", "domain", domain, "server", upstream, "error", err, "key_tag", rrsig.KeyTag)
|
||||
signatureValid = false
|
||||
} else {
|
||||
logger.Debug("DNSSEC签名验证成功", "domain", domain, "server", upstream, "key_tag", rrsig.KeyTag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置AD标志(Authenticated Data)
|
||||
response.AuthenticatedData = signatureValid
|
||||
if !signatureValid {
|
||||
logger.Warn("DNSSEC验证失败:至少一个签名无效", "domain", domain, "server", upstream)
|
||||
}
|
||||
} else {
|
||||
logger.Debug("DNS响应不包含DNSSEC记录", "domain", domain, "server", upstream)
|
||||
}
|
||||
}
|
||||
|
||||
if response.Rcode == dns.RcodeSuccess {
|
||||
logger.Debug("DNS查询成功", "domain", domain, "rtt", rtt, "server", upstream)
|
||||
|
||||
// 记录解析域名统计
|
||||
s.updateResolvedDomainStats(domain)
|
||||
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.Allowed++
|
||||
})
|
||||
return response, rtt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user