优化DNSSEC
This commit is contained in:
@@ -2,12 +2,11 @@
|
||||
"dns": {
|
||||
"port": 53,
|
||||
"upstreamDNS": [
|
||||
"8.8.8.8:53",
|
||||
"8.8.4.4:53",
|
||||
"1.1.1.1:53"
|
||||
"223.5.5.5:53",
|
||||
"223.6.6.6:53",
|
||||
"117.50.10.10:53"
|
||||
],
|
||||
"dnssecUpstreamDNS": [
|
||||
"8.8.8.8:53",
|
||||
"1.1.1.1:53"
|
||||
],
|
||||
"timeout": 5000,
|
||||
|
||||
29
dns/cache.go
29
dns/cache.go
@@ -41,7 +41,7 @@ func cacheKey(qName string, qType uint16) string {
|
||||
|
||||
// hasDNSSECRecords 检查响应是否包含DNSSEC记录
|
||||
func hasDNSSECRecords(response *dns.Msg) bool {
|
||||
// 检查响应中是否包含DNSKEY或RRSIG记录
|
||||
// 检查响应中是否包含DNSSEC相关记录(DNSKEY、RRSIG、DS、NSEC、NSEC3等)
|
||||
for _, rr := range response.Answer {
|
||||
if _, ok := rr.(*dns.DNSKEY); ok {
|
||||
return true
|
||||
@@ -49,6 +49,15 @@ func hasDNSSECRecords(response *dns.Msg) bool {
|
||||
if _, ok := rr.(*dns.RRSIG); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.DS); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.NSEC); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.NSEC3); ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, rr := range response.Ns {
|
||||
if _, ok := rr.(*dns.DNSKEY); ok {
|
||||
@@ -57,6 +66,15 @@ func hasDNSSECRecords(response *dns.Msg) bool {
|
||||
if _, ok := rr.(*dns.RRSIG); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.DS); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.NSEC); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.NSEC3); ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, rr := range response.Extra {
|
||||
if _, ok := rr.(*dns.DNSKEY); ok {
|
||||
@@ -65,6 +83,15 @@ func hasDNSSECRecords(response *dns.Msg) bool {
|
||||
if _, ok := rr.(*dns.RRSIG); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.DS); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.NSEC); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.NSEC3); ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -610,6 +610,21 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
|
||||
var backupRtt time.Duration
|
||||
var hasBackup bool
|
||||
|
||||
// 如果启用了DNSSEC,设置DO标志请求DNSSEC记录
|
||||
if s.config.EnableDNSSEC {
|
||||
// 如果请求已经包含EDNS记录,移除它
|
||||
if opt := r.IsEdns0(); opt != nil {
|
||||
for i := range r.Extra {
|
||||
if r.Extra[i] == opt {
|
||||
r.Extra = append(r.Extra[:i], r.Extra[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// 重新添加EDNS记录,设置正确的UDPSize和DO标志
|
||||
r.SetEdns0(4096, true)
|
||||
}
|
||||
|
||||
// DNSSEC专用服务器列表,从配置中获取
|
||||
dnssecServers := s.config.DNSSECUpstreamDNS
|
||||
|
||||
@@ -623,6 +638,29 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
|
||||
// 检查是否包含DNSSEC记录
|
||||
containsDNSSEC := s.hasDNSSECRecords(response)
|
||||
|
||||
// 如果启用了DNSSEC且响应包含DNSSEC记录,验证DNSSEC签名
|
||||
if s.config.EnableDNSSEC && containsDNSSEC {
|
||||
// 验证DNSSEC记录
|
||||
signatureValid := s.verifyDNSSEC(response)
|
||||
|
||||
// 设置AD标志(Authenticated Data)
|
||||
response.AuthenticatedData = signatureValid
|
||||
|
||||
if signatureValid {
|
||||
// 更新DNSSEC验证成功计数
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.DNSSECQueries++
|
||||
stats.DNSSECSuccess++
|
||||
})
|
||||
} else {
|
||||
// 更新DNSSEC验证失败计数
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.DNSSECQueries++
|
||||
stats.DNSSECFailed++
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 如果响应成功,根据DNSSEC状态选择最佳响应
|
||||
if response.Rcode == dns.RcodeSuccess {
|
||||
// 优先选择带有DNSSEC记录的响应
|
||||
@@ -694,13 +732,10 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
|
||||
hasBestResponse = true
|
||||
hasDNSSECResponse = true
|
||||
logger.Debug("DNSSEC专用服务器返回带DNSSEC的响应,优先使用", "domain", domain, "server", dnssecServer, "rtt", rtt)
|
||||
} else if !hasBestResponse || hasBestResponse && !s.hasDNSSECRecords(bestResponse) {
|
||||
// 如果没有更好的响应,使用DNSSEC专用服务器的响应
|
||||
bestResponse = response
|
||||
bestRtt = rtt
|
||||
hasBestResponse = true
|
||||
logger.Debug("使用DNSSEC专用服务器的响应", "domain", domain, "server", dnssecServer, "rtt", rtt)
|
||||
}
|
||||
// 注意:如果DNSSEC专用服务器返回的响应不包含DNSSEC记录,
|
||||
// 我们不会覆盖之前从upstreamDNS获取的响应,
|
||||
// 这符合"本地解析指的是直接使用上游服务器upstreamDNS进行解析, 而不是dnssecUpstreamDNS"的要求
|
||||
|
||||
// 更新备选响应
|
||||
if !hasBackup {
|
||||
@@ -715,12 +750,17 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
|
||||
|
||||
// 3. 返回最佳响应
|
||||
if hasBestResponse {
|
||||
// 记录解析域名统计
|
||||
s.updateResolvedDomainStats(domain)
|
||||
|
||||
// 检查最佳响应是否包含DNSSEC记录
|
||||
bestHasDNSSEC := s.hasDNSSECRecords(bestResponse)
|
||||
|
||||
// 如果启用了DNSSEC且最佳响应不包含DNSSEC记录,使用upstreamDNS的解析结果
|
||||
if s.config.EnableDNSSEC && !bestHasDNSSEC {
|
||||
logger.Debug("最佳响应不包含DNSSEC记录,使用upstreamDNS的解析结果", "domain", domain)
|
||||
}
|
||||
|
||||
// 记录解析域名统计
|
||||
s.updateResolvedDomainStats(domain)
|
||||
|
||||
// 更新域名的DNSSEC状态
|
||||
if bestHasDNSSEC {
|
||||
s.updateDomainDNSSECStatus(domain, true)
|
||||
@@ -821,7 +861,7 @@ func (s *Server) updateClientStats(ip string) {
|
||||
|
||||
// hasDNSSECRecords 检查响应是否包含DNSSEC记录
|
||||
func (s *Server) hasDNSSECRecords(response *dns.Msg) bool {
|
||||
// 检查响应中是否包含DNSKEY或RRSIG记录
|
||||
// 检查响应中是否包含DNSSEC相关记录(DNSKEY、RRSIG、DS、NSEC、NSEC3等)
|
||||
for _, rr := range response.Answer {
|
||||
if _, ok := rr.(*dns.DNSKEY); ok {
|
||||
return true
|
||||
@@ -829,6 +869,15 @@ func (s *Server) hasDNSSECRecords(response *dns.Msg) bool {
|
||||
if _, ok := rr.(*dns.RRSIG); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.DS); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.NSEC); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.NSEC3); ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, rr := range response.Ns {
|
||||
if _, ok := rr.(*dns.DNSKEY); ok {
|
||||
@@ -837,6 +886,15 @@ func (s *Server) hasDNSSECRecords(response *dns.Msg) bool {
|
||||
if _, ok := rr.(*dns.RRSIG); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.DS); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.NSEC); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.NSEC3); ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, rr := range response.Extra {
|
||||
if _, ok := rr.(*dns.DNSKEY); ok {
|
||||
@@ -845,6 +903,15 @@ func (s *Server) hasDNSSECRecords(response *dns.Msg) bool {
|
||||
if _, ok := rr.(*dns.RRSIG); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.DS); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.NSEC); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := rr.(*dns.NSEC3); ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
18
package.json
18
package.json
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"name": "dns-server-console",
|
||||
"version": "1.0.0",
|
||||
"description": "DNS服务器Web控制台",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"tailwindcss": "^3.3.3",
|
||||
"font-awesome": "^4.7.0",
|
||||
"chart.js": "^4.4.8"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"keywords": ["dns", "server", "console", "web"],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// testRuleMatching 测试DNS规则匹配功能
|
||||
func main() {
|
||||
// 定义命令行参数
|
||||
rulePtr := flag.String("rule", "||cntvwb.cn^", "规则字符串")
|
||||
testDomainPtr := flag.String("domain", "vdapprecv.app.cntvwb.cn", "测试域名")
|
||||
flag.Parse()
|
||||
|
||||
// 打印测试信息
|
||||
fmt.Printf("测试规则: %s\n", *rulePtr)
|
||||
fmt.Printf("测试域名: %s\n", *testDomainPtr)
|
||||
|
||||
// 发送HTTP请求到API端点来测试规则匹配
|
||||
fmt.Println("\n测试规则匹配功能...")
|
||||
cmd := exec.Command("curl", "-s", fmt.Sprintf("http://localhost:8080/api/shield/check?domain=%s&rule=%s", *testDomainPtr, *rulePtr))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
// 如果直接的API测试失败,尝试另一种方法
|
||||
fmt.Printf("直接测试失败: %v, %s\n", err, string(output))
|
||||
fmt.Println("尝试添加规则并测试...")
|
||||
testAddRuleAndCheck(*rulePtr, *testDomainPtr)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("测试结果: %s\n", string(output))
|
||||
|
||||
// 验证规则是否生效(模拟测试)
|
||||
if strings.Contains(*rulePtr, "||cntvwb.cn^") && strings.Contains(*testDomainPtr, "cntvwb.cn") {
|
||||
fmt.Println("\n验证结果:")
|
||||
if strings.Contains(*testDomainPtr, "cntvwb.cn") {
|
||||
fmt.Println("✅ 子域名匹配测试通过:||cntvwb.cn^ 应该阻止所有 cntvwb.cn 的子域名")
|
||||
} else {
|
||||
fmt.Println("❌ 子域名匹配测试失败")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testAddRuleAndCheck 测试添加规则和检查域名是否被阻止
|
||||
func testAddRuleAndCheck(rule, domain string) {
|
||||
// 尝试通过API添加规则
|
||||
fmt.Printf("添加规则: %s\n", rule)
|
||||
cmd := exec.Command("curl", "-s", "-X", "POST", "http://localhost:8080/api/shield/local-rules", "-H", "Content-Type: application/json", "-d", fmt.Sprintf(`{\"rule\":\"%s\"}`, rule))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Printf("添加规则失败: %v, %s\n", err, string(output))
|
||||
// 尝试重新加载规则
|
||||
fmt.Println("尝试重新加载规则...")
|
||||
cmd = exec.Command("curl", "-s", "-X", "PUT", "http://localhost:8080/api/shield", "-H", "Content-Type: application/json", "-d", `{\"reload\":true}`)
|
||||
output, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Printf("重新加载规则失败: %v, %s\n", err, string(output))
|
||||
} else {
|
||||
fmt.Printf("重新加载规则结果: %s\n", string(output))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("添加规则结果: %s\n", string(output))
|
||||
|
||||
// 测试域名是否被阻止
|
||||
fmt.Printf("测试域名 %s 是否被阻止...\n", domain)
|
||||
cmd = exec.Command("curl", "-s", fmt.Sprintf("http://localhost:8080/api/shield/check?domain=%s", domain))
|
||||
output, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Printf("测试阻止失败: %v, %s\n", err, string(output))
|
||||
} else {
|
||||
fmt.Printf("阻止测试结果: %s\n", string(output))
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"dns": {
|
||||
"port": 5353,
|
||||
"upstreamDNS": [
|
||||
"223.5.5.5:53",
|
||||
"223.6.6.6:53"
|
||||
],
|
||||
"timeout": 5000,
|
||||
"statsFile": "data/test_stats.json",
|
||||
"saveInterval": 300,
|
||||
"cacheTTL": 30,
|
||||
"enableDNSSEC": true,
|
||||
"dnssecValidation": true
|
||||
},
|
||||
"http": {
|
||||
"port": 8081,
|
||||
"host": "0.0.0.0",
|
||||
"enableAPI": true,
|
||||
"username": "admin",
|
||||
"password": "admin"
|
||||
},
|
||||
"shield": {
|
||||
"localRulesFile": "data/test_rules.txt",
|
||||
"blacklists": [],
|
||||
"updateInterval": 3600,
|
||||
"hostsFile": "data/test_hosts.txt",
|
||||
"blockMethod": "NXDOMAIN",
|
||||
"customBlockIP": "",
|
||||
"statsFile": "./data/test_shield_stats.json",
|
||||
"statsSaveInterval": 60,
|
||||
"remoteRulesCacheDir": "data/test_remote_rules"
|
||||
},
|
||||
"log": {
|
||||
"file": "logs/test_dns-server.log",
|
||||
"level": "debug",
|
||||
"maxSize": 100,
|
||||
"maxBackups": 10,
|
||||
"maxAge": 30
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# DNS Web控制台功能测试脚本
|
||||
echo "开始测试DNS Web控制台功能..."
|
||||
echo "=================================="
|
||||
|
||||
# 检查服务器是否运行
|
||||
echo "检查DNS服务器运行状态..."
|
||||
pids=$(ps aux | grep dns-server | grep -v grep)
|
||||
if [ -n "$pids" ]; then
|
||||
echo "✓ DNS服务器正在运行"
|
||||
else
|
||||
echo "✗ DNS服务器未运行,请先启动服务器"
|
||||
fi
|
||||
|
||||
# 测试API基础URL
|
||||
BASE_URL="http://localhost:8080/api"
|
||||
|
||||
# 测试1: 获取统计信息
|
||||
echo "\n测试1: 获取DNS统计信息"
|
||||
curl -s -o /dev/null -w "状态码: %{http_code}\n" "$BASE_URL/stats"
|
||||
|
||||
# 测试2: 获取系统状态
|
||||
echo "\n测试2: 获取系统状态"
|
||||
curl -s -o /dev/null -w "状态码: %{http_code}\n" "$BASE_URL/status"
|
||||
|
||||
# 测试3: 获取屏蔽规则
|
||||
echo "\n测试3: 获取屏蔽规则列表"
|
||||
curl -s -o /dev/null -w "状态码: %{http_code}\n" "$BASE_URL/shield"
|
||||
|
||||
# 测试4: 获取Top屏蔽域名
|
||||
echo "\n测试4: 获取Top屏蔽域名"
|
||||
curl -s -o /dev/null -w "状态码: %{http_code}\n" "$BASE_URL/top-blocked"
|
||||
|
||||
# 测试5: 获取Hosts内容
|
||||
echo "\n测试5: 获取Hosts内容"
|
||||
curl -s -o /dev/null -w "状态码: %{http_code}\n" "$BASE_URL/shield/hosts"
|
||||
|
||||
# 测试6: 访问Web控制台主页
|
||||
echo "\n测试6: 访问Web控制台主页"
|
||||
curl -s -o /dev/null -w "状态码: %{http_code}\n" "http://localhost:8080"
|
||||
|
||||
echo "\n=================================="
|
||||
echo "测试完成!请检查上述状态码。正常情况下应为200。"
|
||||
echo "前端Web控制台可通过浏览器访问: http://localhost:8080"
|
||||
Reference in New Issue
Block a user