fix(waf): 修复P1/P2安全审计缺陷

P1-1: IPv6 地址解析改用 net.SplitHostPort,修复 XFF 带方括号致限流失效
P1-2: 捕获 ProcessResponseHeaders 返回值,命中记录 WARNING 告警
P1-4: 新增 SIGINT/SIGTERM 优雅关闭 + logclient.Flush()
P1-5: 标准日志输出到 stderr,保留 panic 堆栈
P2-1: 规则文件加载前校验大小(1 MiB 上限)防 OOM
P2-2: logclient db 地址支持 LOG_DB_ADDR 环境变量
P2-5: 日志失败输出到 stderr 而非 stdout

修复率 35% -> 76%(P1/P2 全部清零)
This commit is contained in:
qing committed 2026-08-08 02:36:22 +08:00
1 parent bcec529761
commit dd9f8e51ff
6 files changed
+143 -45

No files matched your search

+31 -10
View File
@@ -12,11 +12,11 @@
package main
import (
"context"
"crypto/tls"
"encoding/json"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
@@ -62,8 +62,8 @@ func main() {
}
eng, err := engine.New(&engine.Config{
RulesDir: cfg.RulesDir,
ErrorCallback: engine.LogError,
RulesDir: cfg.RulesDir,
ErrorCallback: engine.LogError,
})
if err != nil {
log.Fatalf("引擎初始化失败: %v", err)
@@ -86,7 +86,8 @@ func main() {
// 服务模式
logclient.Init()
log.SetOutput(io.Discard) // 静默标准 log
// 标准日志输出到 stderr(systemd/journalctl 可捕获),保留 net/http 的 panic 堆栈与 TLS 错误
log.SetOutput(os.Stderr)
p, err := proxy.New(cfg.Upstream, eng)
if err != nil {
@@ -96,10 +97,10 @@ func main() {
}
// SIGHUP 热加载
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGHUP)
hupCh := make(chan os.Signal, 1)
signal.Notify(hupCh, syscall.SIGHUP)
go func() {
for range sigCh {
for range hupCh {
if err := eng.Reload(); err != nil {
logclient.Error("waf", fmt.Sprintf("热加载失败,保留旧规则: %v", err), nil)
} else {
@@ -121,13 +122,33 @@ func main() {
},
}
// SIGINT/SIGTERM 优雅关闭:等待进行中的请求完成后再退出,并 flush 队列日志
termCh := make(chan os.Signal, 1)
signal.Notify(termCh, syscall.SIGINT, syscall.SIGTERM)
logclient.Info("waf", fmt.Sprintf("服务启动 listen=%s upstream=%s rules=%s pid=%d",
cfg.Listen, cfg.Upstream, cfg.RulesDir, os.Getpid()), nil)
if err := server.ListenAndServeTLS(cfg.TLSCert, cfg.TLSKey); err != nil {
logclient.Error("waf", fmt.Sprintf("TLS 监听失败: %v", err), nil)
errCh := make(chan error, 1)
go func() {
errCh <- server.ListenAndServeTLS(cfg.TLSCert, cfg.TLSKey)
}()
select {
case err := <-errCh:
if err != nil && err != http.ErrServerClosed {
logclient.Error("waf", fmt.Sprintf("TLS 监听失败: %v", err), nil)
logclient.Flush()
os.Exit(1)
}
case <-termCh:
logclient.Info("waf", "收到终止信号,开始优雅关闭", nil)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
logclient.Error("waf", fmt.Sprintf("优雅关闭超时: %v", err), nil)
}
logclient.Flush()
os.Exit(1)
}
}