From 2832566e87b97b05aac01c0598b542d485088fbf Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Thu, 27 Aug 2026 14:31:30 +0000 Subject: [PATCH] =?UTF-8?q?waf=20=E5=90=8C=E7=AB=AF=E5=8F=A3=E6=98=8E?= =?UTF-8?q?=E6=96=87=20HTTP=20=E8=AF=B7=E6=B1=82=20301=20=E9=87=8D?= =?UTF-8?q?=E5=AE=9A=E5=90=91=E5=88=B0=20HTTPS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 按连接首字节分发 TLS(0x16)与明文连接:明文走独立 handler 301 跳转 https 同路径(保留路径/查询串),替代被停用的 redirect80 的入口重定向职责;证书改为显式 LoadX509KeyPair 注入 TLSConfig。 --- waf/main.go | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/waf/main.go b/waf/main.go index a72cf16..d1c2e9e 100644 --- a/waf/main.go +++ b/waf/main.go @@ -12,12 +12,15 @@ package main import ( + "bufio" "context" "crypto/tls" "encoding/json" "flag" "fmt" + "io" "log" + "net" "net/http" "os" "os/signal" @@ -200,9 +203,37 @@ func main() { logclient.Info("waf", fmt.Sprintf("服务启动 instance=%s listen=%s upstream=%s rules=%s pid=%d", *instance, cfg.Listen, cfg.Upstream, cfg.RulesDir, os.Getpid()), nil) - errCh := make(chan error, 1) + errCh := make(chan error, 2) + // 同一端口区分 TLS 与明文 HTTP:明文请求 301 重定向到 HTTPS(原 redirect80 的职责) + cert, err := tls.LoadX509KeyPair(cfg.TLSCert, cfg.TLSKey) + if err != nil { + logclient.Error("waf", fmt.Sprintf("加载 TLS 证书失败: %v", err), nil) + logclient.Flush() + os.Exit(1) + } + server.TLSConfig.Certificates = []tls.Certificate{cert} + rawLn, err := net.Listen("tcp", cfg.Listen) + if err != nil { + logclient.Error("waf", fmt.Sprintf("监听失败: %v", err), nil) + logclient.Flush() + os.Exit(1) + } + tlsCh := make(chan net.Conn) + plainCh := make(chan net.Conn) + go acceptAndDispatch(rawLn, tlsCh, plainCh) + + plainServer := &http.Server{ + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, "https://"+r.Host+r.URL.RequestURI(), http.StatusMovedPermanently) + }), + ReadHeaderTimeout: 10 * time.Second, + } + go func() { - errCh <- server.ListenAndServeTLS(cfg.TLSCert, cfg.TLSKey) + errCh <- server.Serve(tls.NewListener(&chanListener{ch: tlsCh, addr: rawLn.Addr()}, server.TLSConfig)) + }() + go func() { + errCh <- plainServer.Serve(&chanListener{ch: plainCh, addr: rawLn.Addr()}) }() select { @@ -219,10 +250,53 @@ func main() { if err := server.Shutdown(ctx); err != nil { logclient.Error("waf", fmt.Sprintf("优雅关闭超时: %v", err), nil) } + plainServer.Shutdown(ctx) logclient.Flush() } } +// bufferedConn 保留已 peek 的首字节,避免后续 Read 丢数据。 +type bufferedConn struct { + net.Conn + r *bufio.Reader +} + +func (c *bufferedConn) Read(p []byte) (int, error) { return c.r.Read(p) } + +// chanListener 从 channel 取连接,供两个 http.Server 分别消费 TLS / 明文连接。 +type chanListener struct { + ch chan net.Conn + addr net.Addr +} + +func (l *chanListener) Accept() (net.Conn, error) { + c, ok := <-l.ch + if !ok { + return nil, io.EOF + } + return c, nil +} + +func (l *chanListener) Close() error { return nil } +func (l *chanListener) Addr() net.Addr { return l.addr } + +// acceptAndDispatch 按首字节分发:0x16 为 TLS ClientHello → TLS 通道,其余 → 明文 HTTP 通道。 +func acceptAndDispatch(ln net.Listener, tlsCh, plainCh chan<- net.Conn) { + for { + c, err := ln.Accept() + if err != nil { + return + } + br := bufio.NewReader(c) + b, err := br.Peek(1) + if err == nil && len(b) == 1 && b[0] == 0x16 { + tlsCh <- &bufferedConn{Conn: c, r: br} + } else { + plainCh <- &bufferedConn{Conn: c, r: br} + } + } +} + func loadConfig(path string) *Config { data, err := os.ReadFile(path) if err != nil {