- 提取 App 公共逻辑到 app_common.go,消除 app.go/app_windows.go 重复定义
- 提取 Server 生命周期/中间件/路由逻辑到独立文件(lifecycle.go/middleware_builder.go/router.go)
- 提取 Proxy 缓存处理/头部修改/目标选择到独立模块
- 提取 CheckIPAccess/CheckTokenAuth 到 utils/httperror.go,消除 status/purge 重复实现
- 修复 stream 双向转发:任一方向完成立即关闭双端,避免连接泄漏
- 修复 SSL/TLS 中静默忽略错误的问题,添加日志记录
- 统一日志消息为英文
💘 Generated with Crush
Assisted-by: GLM 5.1 via Crush <crush@charm.land>
130 lines
3.7 KiB
Go
130 lines
3.7 KiB
Go
// Package proxy 反向代理包,为 Lolly HTTP 服务器提供反向代理功能。
|
||
//
|
||
// 该文件提供上游 SSL/TLS 配置支持,包括自定义 CA 证书、
|
||
// 客户端证书(mTLS)、SNI 和 TLS 版本控制。
|
||
//
|
||
// 主要功能:
|
||
// - 自定义 CA 证书验证:支持指定受信任的 CA 证书文件
|
||
// - 客户端证书认证(mTLS):支持双向 TLS 认证
|
||
// - SNI 支持:自动从目标 URL 提取或使用配置的主机名
|
||
// - TLS 版本控制:支持 TLSv1.0 到 TLSv1.3 的最小/最大版本限制
|
||
// - 跳过证书验证:仅测试环境使用
|
||
//
|
||
// 主要用途:
|
||
//
|
||
// 用于代理与上游服务器之间的 TLS 连接配置,支持自签名证书、
|
||
// 内部 CA 签发证书以及双向认证场景。
|
||
//
|
||
// 注意事项:
|
||
// - InsecureSkipVerify 仅建议在测试环境使用
|
||
// - 证书文件路径为绝对路径或相对于工作目录
|
||
//
|
||
// 作者:xfy
|
||
package proxy
|
||
|
||
import (
|
||
"crypto/tls"
|
||
"crypto/x509"
|
||
"errors"
|
||
"os"
|
||
"strings"
|
||
|
||
"rua.plus/lolly/internal/config"
|
||
"rua.plus/lolly/internal/logging"
|
||
)
|
||
|
||
// tlsVersionMap TLS 版本字符串到 tls 常量的映射表。
|
||
//
|
||
// 支持 TLSv1.0、TLSv1.1、TLSv1.2、TLSv1.3 格式(大小写不敏感)。
|
||
// 空字符串表示使用 Go 标准库默认值。
|
||
var tlsVersionMap = map[string]uint16{
|
||
"TLSV1.0": tls.VersionTLS10,
|
||
"TLSV1.1": tls.VersionTLS11,
|
||
"TLSV1.2": tls.VersionTLS12,
|
||
"TLSV1.3": tls.VersionTLS13,
|
||
"": 0, // 空字符串表示使用默认
|
||
}
|
||
|
||
// CreateTLSConfig 从 ProxySSLConfig 创建 tls.Config。
|
||
//
|
||
// 参数:
|
||
// - cfg: 上游 SSL 配置
|
||
// - defaultServerName: 默认 SNI 名称(从目标 URL 提取)
|
||
//
|
||
// 返回值:
|
||
// - *tls.Config: TLS 配置对象
|
||
// - error: 配置错误(证书加载失败等)
|
||
//
|
||
// 注意事项:
|
||
// - cfg 为 nil 或 Enabled=false 时返回 nil
|
||
// - TrustedCA 加载失败时返回错误
|
||
// - ClientCert/ClientKey 加载失败时返回错误
|
||
func CreateTLSConfig(cfg *config.ProxySSLConfig, defaultServerName string) (*tls.Config, error) {
|
||
if cfg == nil || !cfg.Enabled {
|
||
return nil, nil
|
||
}
|
||
|
||
tlsCfg := &tls.Config{}
|
||
|
||
// SNI 配置
|
||
if cfg.ServerName != "" {
|
||
tlsCfg.ServerName = cfg.ServerName
|
||
} else if defaultServerName != "" {
|
||
tlsCfg.ServerName = defaultServerName
|
||
}
|
||
|
||
// 跳过证书验证(仅测试环境)
|
||
if cfg.InsecureSkipVerify {
|
||
tlsCfg.InsecureSkipVerify = true
|
||
}
|
||
|
||
// CA 证书验证
|
||
if cfg.TrustedCA != "" {
|
||
caData, err := os.ReadFile(cfg.TrustedCA)
|
||
if err != nil {
|
||
return nil, errors.New("failed to read CA certificate: " + err.Error())
|
||
}
|
||
|
||
caPool := x509.NewCertPool()
|
||
if !caPool.AppendCertsFromPEM(caData) {
|
||
return nil, errors.New("failed to parse CA certificate")
|
||
}
|
||
tlsCfg.RootCAs = caPool
|
||
}
|
||
|
||
// 客户端证书(mTLS)
|
||
if cfg.ClientCert != "" && cfg.ClientKey != "" {
|
||
cert, err := tls.LoadX509KeyPair(cfg.ClientCert, cfg.ClientKey)
|
||
if err != nil {
|
||
return nil, errors.New("failed to load client certificate: " + err.Error())
|
||
}
|
||
tlsCfg.Certificates = []tls.Certificate{cert}
|
||
}
|
||
|
||
// TLS 版本配置:默认 MinVersion = TLS 1.2
|
||
tlsCfg.MinVersion = tls.VersionTLS12
|
||
|
||
if cfg.MinVersion != "" {
|
||
version, ok := tlsVersionMap[strings.ToUpper(cfg.MinVersion)]
|
||
if !ok {
|
||
return nil, errors.New("invalid TLS min version: " + cfg.MinVersion)
|
||
}
|
||
tlsCfg.MinVersion = version
|
||
}
|
||
|
||
// 警告:TLS 1.0/1.1 已不安全,不应在生产环境使用
|
||
if tlsCfg.MinVersion < tls.VersionTLS12 {
|
||
logging.Warn().Msgf("上游 TLS MinVersion 设置为 %s(低于 TLS 1.2),存在安全风险", cfg.MinVersion)
|
||
}
|
||
|
||
if cfg.MaxVersion != "" {
|
||
version, ok := tlsVersionMap[strings.ToUpper(cfg.MaxVersion)]
|
||
if !ok {
|
||
return nil, errors.New("invalid TLS max version: " + cfg.MaxVersion)
|
||
}
|
||
tlsCfg.MaxVersion = version
|
||
}
|
||
|
||
return tlsCfg, nil
|
||
}
|