waf 同端口明文 HTTP 请求 301 重定向到 HTTPS
按连接首字节分发 TLS(0x16)与明文连接:明文走独立 handler 301 跳转 https 同路径(保留路径/查询串),替代被停用的 redirect80 的入口重定向职责;证书改为显式 LoadX509KeyPair 注入 TLSConfig。
This commit is contained in:
1 parent
7d5d7b9e36
commit
2832566e87
1 file changed
+76
-2
+76
-2
@@ -12,12 +12,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@@ -200,9 +203,37 @@ func main() {
|
|||||||
logclient.Info("waf", fmt.Sprintf("服务启动 instance=%s listen=%s upstream=%s rules=%s pid=%d",
|
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)
|
*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() {
|
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 {
|
select {
|
||||||
@@ -219,10 +250,53 @@ func main() {
|
|||||||
if err := server.Shutdown(ctx); err != nil {
|
if err := server.Shutdown(ctx); err != nil {
|
||||||
logclient.Error("waf", fmt.Sprintf("优雅关闭超时: %v", err), nil)
|
logclient.Error("waf", fmt.Sprintf("优雅关闭超时: %v", err), nil)
|
||||||
}
|
}
|
||||||
|
plainServer.Shutdown(ctx)
|
||||||
logclient.Flush()
|
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 {
|
func loadConfig(path string) *Config {
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in new issue
Block a user