refactor(ssl): extract TLS config generation to sslutil
- 新增 internal/sslutil/tlsconfig.go 统一 TLS 配置函数 - 提取 ParseTLSVersion/ParseCipherSuites/DefaultCipherSuites 等 - 更新 ssl.go/stream/ssl.go/proxy_ssl.go 使用统一函数 - 消除约 150 行重复代码 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
d25c7b21a6
commit
6b8b00c900
@ -31,20 +31,9 @@ import (
|
||||
|
||||
"rua.plus/lolly/internal/config"
|
||||
"rua.plus/lolly/internal/logging"
|
||||
"rua.plus/lolly/internal/sslutil"
|
||||
)
|
||||
|
||||
// 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。
|
||||
//
|
||||
// 参数:
|
||||
@ -105,7 +94,7 @@ func CreateTLSConfig(cfg *config.ProxySSLConfig, defaultServerName string) (*tls
|
||||
tlsCfg.MinVersion = tls.VersionTLS12
|
||||
|
||||
if cfg.MinVersion != "" {
|
||||
version, ok := tlsVersionMap[strings.ToUpper(cfg.MinVersion)]
|
||||
version, ok := sslutil.TLSVersionMap[strings.ToUpper(cfg.MinVersion)]
|
||||
if !ok {
|
||||
return nil, errors.New("invalid TLS min version: " + cfg.MinVersion)
|
||||
}
|
||||
@ -118,7 +107,7 @@ func CreateTLSConfig(cfg *config.ProxySSLConfig, defaultServerName string) (*tls
|
||||
}
|
||||
|
||||
if cfg.MaxVersion != "" {
|
||||
version, ok := tlsVersionMap[strings.ToUpper(cfg.MaxVersion)]
|
||||
version, ok := sslutil.TLSVersionMap[strings.ToUpper(cfg.MaxVersion)]
|
||||
if !ok {
|
||||
return nil, errors.New("invalid TLS max version: " + cfg.MaxVersion)
|
||||
}
|
||||
|
||||
@ -43,12 +43,12 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"rua.plus/lolly/internal/config"
|
||||
"rua.plus/lolly/internal/logging"
|
||||
"rua.plus/lolly/internal/netutil"
|
||||
"rua.plus/lolly/internal/sslutil"
|
||||
)
|
||||
|
||||
// TLSManager TLS 配置管理器。
|
||||
@ -116,19 +116,19 @@ func NewTLSManager(cfg *config.SSLConfig) (*TLSManager, error) {
|
||||
|
||||
// 应用 TLS 1.2 的加密套件
|
||||
if len(cfg.Ciphers) > 0 {
|
||||
ciphers, err := parseCipherSuites(cfg.Ciphers)
|
||||
ciphers, err := sslutil.ParseCipherSuites(cfg.Ciphers)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid cipher suites: %w", err)
|
||||
}
|
||||
tlsCfg.CipherSuites = ciphers
|
||||
} else {
|
||||
// 使用安全的默认加密套件
|
||||
tlsCfg.CipherSuites = defaultCipherSuites()
|
||||
tlsCfg.CipherSuites = sslutil.DefaultCipherSuites()
|
||||
}
|
||||
|
||||
// 解析 TLS 协议版本
|
||||
if len(cfg.Protocols) > 0 {
|
||||
minVer, maxVer, err := parseTLSVersions(cfg.Protocols)
|
||||
minVer, maxVer, err := sslutil.ParseTLSVersions(cfg.Protocols)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid TLS protocols: %w", err)
|
||||
}
|
||||
@ -590,17 +590,17 @@ func createTLSConfig(cfg *config.SSLConfig) (*tls.Config, error) {
|
||||
}
|
||||
|
||||
if len(cfg.Ciphers) > 0 {
|
||||
ciphers, err := parseCipherSuites(cfg.Ciphers)
|
||||
ciphers, err := sslutil.ParseCipherSuites(cfg.Ciphers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsCfg.CipherSuites = ciphers
|
||||
} else {
|
||||
tlsCfg.CipherSuites = defaultCipherSuites()
|
||||
tlsCfg.CipherSuites = sslutil.DefaultCipherSuites()
|
||||
}
|
||||
|
||||
if len(cfg.Protocols) > 0 {
|
||||
minVer, maxVer, err := parseTLSVersions(cfg.Protocols)
|
||||
minVer, maxVer, err := sslutil.ParseTLSVersions(cfg.Protocols)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -611,123 +611,6 @@ func createTLSConfig(cfg *config.SSLConfig) (*tls.Config, error) {
|
||||
return tlsCfg, nil
|
||||
}
|
||||
|
||||
// parseTLSVersions 解析 TLS 协议版本字符串。
|
||||
//
|
||||
// 返回最小和最大 TLS 版本。
|
||||
//
|
||||
// 参数:
|
||||
// - protocols: 协议名称列表(如 "TLSv1.2", "TLSv1.3")
|
||||
//
|
||||
// 返回值:
|
||||
// - uint16: 最小版本
|
||||
// - uint16: 最大版本
|
||||
// - error: 无效协议时返回错误
|
||||
func parseTLSVersions(protocols []string) (uint16, uint16, error) {
|
||||
var minVer, maxVer uint16
|
||||
minVer = tls.VersionTLS13 // 默认最高版本
|
||||
maxVer = tls.VersionTLS13
|
||||
|
||||
for _, p := range protocols {
|
||||
switch p {
|
||||
case "TLSv1.2":
|
||||
if minVer > tls.VersionTLS12 {
|
||||
minVer = tls.VersionTLS12
|
||||
}
|
||||
case "TLSv1.3":
|
||||
maxVer = tls.VersionTLS13
|
||||
case "TLSv1.0", "TLSv1.1":
|
||||
return 0, 0, fmt.Errorf("insecure TLS version %s is not supported", p)
|
||||
default:
|
||||
return 0, 0, fmt.Errorf("unknown TLS version: %s", p)
|
||||
}
|
||||
}
|
||||
|
||||
return minVer, maxVer, nil
|
||||
}
|
||||
|
||||
// parseCipherSuites 解析加密套件名称字符串为 TLS ID。
|
||||
//
|
||||
// 参数:
|
||||
// - ciphers: 加密套件名称列表
|
||||
//
|
||||
// 返回值:
|
||||
// - []uint16: 加密套件 ID 列表
|
||||
// - error: 未知或不安全的加密套件时返回错误
|
||||
func parseCipherSuites(ciphers []string) ([]uint16, error) {
|
||||
result := make([]uint16, 0, len(ciphers))
|
||||
|
||||
for _, c := range ciphers {
|
||||
id, ok := cipherSuiteMap[c]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown cipher suite: %s", c)
|
||||
}
|
||||
// 检查不安全的加密套件
|
||||
if isInsecureCipher(id) {
|
||||
return nil, fmt.Errorf("insecure cipher suite %s is not allowed", c)
|
||||
}
|
||||
result = append(result, id)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// isInsecureCipher 检查加密套件是否不安全。
|
||||
//
|
||||
// 参数:
|
||||
// - id: 加密套件 ID
|
||||
//
|
||||
// 返回值:
|
||||
// - bool: 不安全返回 true
|
||||
func isInsecureCipher(id uint16) bool {
|
||||
insecureCiphers := []uint16{
|
||||
tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
}
|
||||
|
||||
return slices.Contains(insecureCiphers, id)
|
||||
}
|
||||
|
||||
// defaultCipherSuites 返回 TLS 1.2 推荐的加密套件。
|
||||
//
|
||||
// 优先选择前向保密和 AEAD 加密算法。
|
||||
//
|
||||
// 返回值:
|
||||
// - []uint16: 加密套件 ID 列表
|
||||
func defaultCipherSuites() []uint16 {
|
||||
return []uint16{
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
}
|
||||
}
|
||||
|
||||
// cipherSuiteMap 加密套件名称到 TLS ID 的映射。
|
||||
var cipherSuiteMap = map[string]uint16{
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
}
|
||||
|
||||
// ValidateCertificate 验证证书文件。
|
||||
//
|
||||
// 检查证书是否有效且未过期。
|
||||
|
||||
@ -17,6 +17,7 @@ import (
|
||||
"time"
|
||||
|
||||
"rua.plus/lolly/internal/config"
|
||||
"rua.plus/lolly/internal/sslutil"
|
||||
)
|
||||
|
||||
// BenchmarkTLSHandshake 基准测试 TLS 握手性能。
|
||||
@ -660,7 +661,7 @@ func BenchmarkCipherSuiteParsing(b *testing.B) {
|
||||
|
||||
b.ResetTimer()
|
||||
for b.Loop() {
|
||||
_, err := parseCipherSuites(ciphers)
|
||||
_, err := sslutil.ParseCipherSuites(ciphers)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
@ -673,7 +674,7 @@ func BenchmarkTLSVersionsParsing(b *testing.B) {
|
||||
|
||||
b.ResetTimer()
|
||||
for b.Loop() {
|
||||
_, _, err := parseTLSVersions(protocols)
|
||||
_, _, err := sslutil.ParseTLSVersions(protocols)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ import (
|
||||
"time"
|
||||
|
||||
"rua.plus/lolly/internal/config"
|
||||
"rua.plus/lolly/internal/sslutil"
|
||||
)
|
||||
|
||||
func TestNewTLSManager(t *testing.T) {
|
||||
@ -172,7 +173,7 @@ func TestParseTLSVersions(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
minVer, maxVer, err := parseTLSVersions(tt.protocols)
|
||||
minVer, maxVer, err := sslutil.ParseTLSVersions(tt.protocols)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("parseTLSVersions() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
@ -218,7 +219,7 @@ func TestParseCipherSuites(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := parseCipherSuites(tt.ciphers)
|
||||
result, err := sslutil.ParseCipherSuites(tt.ciphers)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("parseCipherSuites() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
@ -231,14 +232,14 @@ func TestParseCipherSuites(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDefaultCipherSuites(t *testing.T) {
|
||||
suites := defaultCipherSuites()
|
||||
suites := sslutil.DefaultCipherSuites()
|
||||
if len(suites) == 0 {
|
||||
t.Error("Expected non-empty default cipher suites")
|
||||
}
|
||||
|
||||
// Check that all default ciphers are secure
|
||||
for _, suite := range suites {
|
||||
if isInsecureCipher(suite) {
|
||||
if sslutil.IsInsecureCipher(suite) {
|
||||
t.Errorf("Default cipher suite %v is insecure", suite)
|
||||
}
|
||||
}
|
||||
@ -252,7 +253,7 @@ func TestIsInsecureCipher(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, c := range insecureCiphers {
|
||||
if !isInsecureCipher(c) {
|
||||
if !sslutil.IsInsecureCipher(c) {
|
||||
t.Errorf("Expected cipher %v to be insecure", c)
|
||||
}
|
||||
}
|
||||
@ -264,7 +265,7 @@ func TestIsInsecureCipher(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, c := range secureCiphers {
|
||||
if isInsecureCipher(c) {
|
||||
if sslutil.IsInsecureCipher(c) {
|
||||
t.Errorf("Expected cipher %v to be secure", c)
|
||||
}
|
||||
}
|
||||
|
||||
224
internal/sslutil/tlsconfig.go
Normal file
224
internal/sslutil/tlsconfig.go
Normal file
@ -0,0 +1,224 @@
|
||||
// Package sslutil provides SSL/TLS utility functions.
|
||||
package sslutil
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// ParseTLSVersion parses a TLS version string to a tls constant.
|
||||
//
|
||||
// Parameters:
|
||||
// - version: TLS version string (e.g., "TLSv1.0", "TLSv1.1", "TLSv1.2", "TLSv1.3")
|
||||
//
|
||||
// Returns:
|
||||
// - uint16: TLS version constant
|
||||
// - error: Invalid version string returns error
|
||||
func ParseTLSVersion(version string) (uint16, error) {
|
||||
switch version {
|
||||
case "TLSv1.0", "TLSV1.0":
|
||||
return tls.VersionTLS10, nil
|
||||
case "TLSv1.1", "TLSV1.1":
|
||||
return tls.VersionTLS11, nil
|
||||
case "TLSv1.2", "TLSV1.2":
|
||||
return tls.VersionTLS12, nil
|
||||
case "TLSv1.3", "TLSV1.3":
|
||||
return tls.VersionTLS13, nil
|
||||
case "":
|
||||
return 0, nil // Empty string means use default
|
||||
default:
|
||||
return 0, fmt.Errorf("invalid TLS version: %s", version)
|
||||
}
|
||||
}
|
||||
|
||||
// ParseTLSVersions parses TLS protocol version strings.
|
||||
//
|
||||
// Returns minimum and maximum TLS versions.
|
||||
//
|
||||
// Parameters:
|
||||
// - protocols: Protocol name list (e.g., "TLSv1.2", "TLSv1.3")
|
||||
//
|
||||
// Returns:
|
||||
// - uint16: Minimum version
|
||||
// - uint16: Maximum version
|
||||
// - error: Invalid protocol returns error
|
||||
func ParseTLSVersions(protocols []string) (uint16, uint16, error) {
|
||||
var minVer, maxVer uint16
|
||||
minVer = tls.VersionTLS13 // Default to highest version
|
||||
maxVer = tls.VersionTLS13
|
||||
|
||||
for _, p := range protocols {
|
||||
switch p {
|
||||
case "TLSv1.2", "TLSV1.2":
|
||||
if minVer > tls.VersionTLS12 {
|
||||
minVer = tls.VersionTLS12
|
||||
}
|
||||
case "TLSv1.3", "TLSV1.3":
|
||||
maxVer = tls.VersionTLS13
|
||||
case "TLSv1.0", "TLSV1.0", "TLSv1.1", "TLSV1.1":
|
||||
return 0, 0, fmt.Errorf("insecure TLS version %s is not supported", p)
|
||||
default:
|
||||
return 0, 0, fmt.Errorf("unknown TLS version: %s", p)
|
||||
}
|
||||
}
|
||||
|
||||
return minVer, maxVer, nil
|
||||
}
|
||||
|
||||
// ParseMinTLSVersion parses the minimum TLS version from protocol list.
|
||||
//
|
||||
// Parameters:
|
||||
// - protocols: Protocol version list
|
||||
//
|
||||
// Returns:
|
||||
// - uint16: TLS version constant
|
||||
func ParseMinTLSVersion(protocols []string) uint16 {
|
||||
for _, p := range protocols {
|
||||
switch p {
|
||||
case "TLSv1.3", "TLSV1.3":
|
||||
return tls.VersionTLS13
|
||||
case "TLSv1.2", "TLSV1.2":
|
||||
return tls.VersionTLS12
|
||||
}
|
||||
}
|
||||
return tls.VersionTLS12
|
||||
}
|
||||
|
||||
// cipherNameToID maps cipher suite names to TLS IDs.
|
||||
// Supports both OpenSSL-style names and Go standard names.
|
||||
var cipherNameToID = map[string]uint16{
|
||||
// OpenSSL-style names (ECDHE-RSA-AES128-GCM-SHA256)
|
||||
"ECDHE-RSA-AES128-GCM-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"ECDHE-RSA-AES256-GCM-SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"ECDHE-ECDSA-AES128-GCM-SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
"ECDHE-ECDSA-AES256-GCM-SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
"ECDHE-RSA-CHACHA20-POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
"ECDHE-ECDSA-CHACHA20-POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
"AES128-GCM-SHA256": tls.TLS_AES_128_GCM_SHA256,
|
||||
"AES256-GCM-SHA384": tls.TLS_AES_256_GCM_SHA384,
|
||||
"CHACHA20-POLY1305": tls.TLS_CHACHA20_POLY1305_SHA256,
|
||||
"ECDHE-RSA-AES128-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
"ECDHE-RSA-AES256-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
"ECDHE-ECDSA-AES128-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
"ECDHE-ECDSA-AES256-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
"RSA-AES128-GCM-SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"RSA-AES256-GCM-SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"RSA-AES128-CBC-SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
"RSA-AES256-CBC-SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
"ECDHE-RSA-3DES-EDE-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
"RSA-3DES-EDE-CBC-SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
// Go standard names (TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
}
|
||||
|
||||
// insecureCipherIDs contains cipher suite IDs considered insecure.
|
||||
var insecureCipherIDs = []uint16{
|
||||
tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
}
|
||||
|
||||
// IsInsecureCipher checks if a cipher suite is insecure.
|
||||
//
|
||||
// Parameters:
|
||||
// - id: Cipher suite ID
|
||||
//
|
||||
// Returns:
|
||||
// - bool: True if insecure
|
||||
func IsInsecureCipher(id uint16) bool {
|
||||
return slices.Contains(insecureCipherIDs, id)
|
||||
}
|
||||
|
||||
// ParseCipherSuites parses cipher suite name strings to TLS IDs.
|
||||
//
|
||||
// Parameters:
|
||||
// - ciphers: Cipher suite name list
|
||||
//
|
||||
// Returns:
|
||||
// - []uint16: Cipher suite ID list
|
||||
// - error: Unknown or insecure cipher suite returns error
|
||||
func ParseCipherSuites(ciphers []string) ([]uint16, error) {
|
||||
result := make([]uint16, 0, len(ciphers))
|
||||
|
||||
for _, c := range ciphers {
|
||||
id, ok := cipherNameToID[c]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown cipher suite: %s", c)
|
||||
}
|
||||
// Check for insecure cipher suites
|
||||
if IsInsecureCipher(id) {
|
||||
return nil, fmt.Errorf("insecure cipher suite %s is not allowed", c)
|
||||
}
|
||||
result = append(result, id)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ParseCipherSuitesLenient parses cipher suites without error on unknown names.
|
||||
// Returns nil if no valid suites found, allowing fallback to defaults.
|
||||
//
|
||||
// Parameters:
|
||||
// - ciphers: Cipher suite name list
|
||||
//
|
||||
// Returns:
|
||||
// - []uint16: Cipher suite ID list (nil if none valid)
|
||||
func ParseCipherSuitesLenient(ciphers []string) []uint16 {
|
||||
var suites []uint16
|
||||
for _, c := range ciphers {
|
||||
if id, ok := cipherNameToID[c]; ok && !IsInsecureCipher(id) {
|
||||
suites = append(suites, id)
|
||||
}
|
||||
}
|
||||
if len(suites) == 0 {
|
||||
return nil // Use defaults
|
||||
}
|
||||
return suites
|
||||
}
|
||||
|
||||
// DefaultCipherSuites returns recommended cipher suites for TLS 1.2.
|
||||
//
|
||||
// Prioritizes forward secrecy and AEAD encryption algorithms.
|
||||
//
|
||||
// Returns:
|
||||
// - []uint16: Cipher suite ID list
|
||||
func DefaultCipherSuites() []uint16 {
|
||||
return []uint16{
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
}
|
||||
}
|
||||
|
||||
// TLSVersionMap maps TLS version strings to tls constants.
|
||||
// Supports TLSv1.0, TLSv1.1, TLSv1.2, TLSv1.3 formats (case insensitive).
|
||||
// Empty string means use Go standard library default.
|
||||
var TLSVersionMap = map[string]uint16{
|
||||
"TLSV1.0": tls.VersionTLS10,
|
||||
"TLSV1.1": tls.VersionTLS11,
|
||||
"TLSV1.2": tls.VersionTLS12,
|
||||
"TLSV1.3": tls.VersionTLS13,
|
||||
"": 0, // Empty string means use default
|
||||
}
|
||||
@ -131,12 +131,12 @@ func (m *SSLManager) GetTLSConfig() *tls.Config {
|
||||
|
||||
// 设置协议版本
|
||||
if len(m.config.Protocols) > 0 {
|
||||
tlsConfig.MinVersion = parseMinTLSVersion(m.config.Protocols)
|
||||
tlsConfig.MinVersion = sslutil.ParseMinTLSVersion(m.config.Protocols)
|
||||
}
|
||||
|
||||
// 设置加密套件
|
||||
if len(m.config.Ciphers) > 0 {
|
||||
tlsConfig.CipherSuites = parseCipherSuites(m.config.Ciphers)
|
||||
tlsConfig.CipherSuites = sslutil.ParseCipherSuitesLenient(m.config.Ciphers)
|
||||
}
|
||||
|
||||
// 配置客户端证书验证(mTLS)
|
||||
@ -183,7 +183,7 @@ func (m *ProxySSLManager) GetClientTLSConfig(serverName string) *tls.Config {
|
||||
|
||||
// 设置协议版本
|
||||
if len(m.config.Protocols) > 0 {
|
||||
tlsConfig.MinVersion = parseMinTLSVersion(m.config.Protocols)
|
||||
tlsConfig.MinVersion = sslutil.ParseMinTLSVersion(m.config.Protocols)
|
||||
}
|
||||
|
||||
// 配置服务器证书验证
|
||||
@ -212,65 +212,3 @@ func (m *SSLManager) IsEnabled() bool {
|
||||
func (m *ProxySSLManager) IsEnabled() bool {
|
||||
return m.config.Enabled
|
||||
}
|
||||
|
||||
// parseMinTLSVersion 解析最小 TLS 版本。
|
||||
//
|
||||
// 参数:
|
||||
// - protocols: 协议版本列表
|
||||
//
|
||||
// 返回值:
|
||||
// - uint16: TLS 版本常量
|
||||
func parseMinTLSVersion(protocols []string) uint16 {
|
||||
for _, p := range protocols {
|
||||
switch p {
|
||||
case "TLSv1.3":
|
||||
return tls.VersionTLS13
|
||||
case "TLSv1.2":
|
||||
return tls.VersionTLS12
|
||||
}
|
||||
}
|
||||
return tls.VersionTLS12
|
||||
}
|
||||
|
||||
// parseCipherSuites 解析加密套件列表。
|
||||
//
|
||||
// 参数:
|
||||
// - ciphers: 加密套件名称列表
|
||||
//
|
||||
// 返回值:
|
||||
// - []uint16: 加密套件 ID 列表
|
||||
func parseCipherSuites(ciphers []string) []uint16 {
|
||||
var suites []uint16
|
||||
for _, c := range ciphers {
|
||||
if id, ok := cipherNameToID[c]; ok {
|
||||
suites = append(suites, id)
|
||||
}
|
||||
}
|
||||
if len(suites) == 0 {
|
||||
return nil // 使用默认值
|
||||
}
|
||||
return suites
|
||||
}
|
||||
|
||||
// cipherNameToID 加密套件名称到 ID 的映射
|
||||
var cipherNameToID = map[string]uint16{
|
||||
"ECDHE-RSA-AES128-GCM-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"ECDHE-RSA-AES256-GCM-SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"ECDHE-ECDSA-AES128-GCM-SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
"ECDHE-ECDSA-AES256-GCM-SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
"ECDHE-RSA-CHACHA20-POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
"ECDHE-ECDSA-CHACHA20-POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
"AES128-GCM-SHA256": tls.TLS_AES_128_GCM_SHA256,
|
||||
"AES256-GCM-SHA384": tls.TLS_AES_256_GCM_SHA384,
|
||||
"CHACHA20-POLY1305": tls.TLS_CHACHA20_POLY1305_SHA256,
|
||||
"ECDHE-RSA-AES128-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
"ECDHE-RSA-AES256-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
"ECDHE-ECDSA-AES128-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
"ECDHE-ECDSA-AES256-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
"RSA-AES128-GCM-SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"RSA-AES256-GCM-SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"RSA-AES128-CBC-SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
"RSA-AES256-CBC-SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
"ECDHE-RSA-3DES-EDE-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
"RSA-3DES-EDE-CBC-SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
}
|
||||
|
||||
@ -284,7 +284,7 @@ func TestParseMinTLSVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
got := parseMinTLSVersion(tt.protocols)
|
||||
got := sslutil.ParseMinTLSVersion(tt.protocols)
|
||||
if got != tt.wantVersion {
|
||||
t.Errorf("parseMinTLSVersion(%v) = %v, want %v", tt.protocols, got, tt.wantVersion)
|
||||
}
|
||||
@ -316,7 +316,7 @@ func TestParseCipherSuites(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := parseCipherSuites(tt.ciphers)
|
||||
got := sslutil.ParseCipherSuitesLenient(tt.ciphers)
|
||||
if tt.wantLen == 0 && got != nil {
|
||||
t.Errorf("Expected nil, got %v", got)
|
||||
} else if tt.wantLen > 0 && len(got) != tt.wantLen {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user