go-proxy/tls_runtime.go
Virgil 465ea38308 feat(proxy): honour TLS config and pool keepalive
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-04 11:16:29 +00:00

120 lines
3.9 KiB
Go

package proxy
import (
"crypto/tls"
"strconv"
"strings"
)
func buildTLSConfig(config TLSConfig) *tls.Config {
tlsConfig := &tls.Config{}
if versions := parseTLSVersions(config.Protocols); versions != nil {
tlsConfig.MinVersion = versions.min
tlsConfig.MaxVersion = versions.max
}
if suites := parseCipherSuites(config.Ciphers); len(suites) > 0 {
tlsConfig.CipherSuites = suites
}
return tlsConfig
}
type tlsVersionBounds struct {
min uint16
max uint16
}
func parseTLSVersions(value string) *tlsVersionBounds {
if strings.TrimSpace(value) == "" {
return nil
}
bounds := tlsVersionBounds{}
for _, token := range splitTLSList(value) {
version, ok := parseTLSVersionToken(token)
if !ok {
continue
}
if bounds.min == 0 || version < bounds.min {
bounds.min = version
}
if version > bounds.max {
bounds.max = version
}
}
if bounds.min == 0 || bounds.max == 0 {
return nil
}
return &bounds
}
func parseTLSVersionToken(token string) (uint16, bool) {
switch strings.ToLower(strings.TrimSpace(token)) {
case "tls1.0", "tlsv1.0", "tls1", "tlsv1", "1.0", "tls10":
return tls.VersionTLS10, true
case "tls1.1", "tlsv1.1", "1.1", "tls11":
return tls.VersionTLS11, true
case "tls1.2", "tlsv1.2", "1.2", "tls12":
return tls.VersionTLS12, true
case "tls1.3", "tlsv1.3", "1.3", "tls13":
return tls.VersionTLS13, true
}
if raw, errorValue := strconv.ParseUint(strings.TrimSpace(token), 10, 16); errorValue == nil {
switch uint16(raw) {
case tls.VersionTLS10, tls.VersionTLS11, tls.VersionTLS12, tls.VersionTLS13:
return uint16(raw), true
}
}
return 0, false
}
func parseCipherSuites(value string) []uint16 {
if strings.TrimSpace(value) == "" {
return nil
}
var suites []uint16
for _, token := range splitTLSList(value) {
if suite, ok := tlsCipherSuiteNames[strings.ToUpper(strings.TrimSpace(token))]; ok {
suites = append(suites, suite)
}
}
return suites
}
func splitTLSList(value string) []string {
return strings.FieldsFunc(value, func(r rune) bool {
switch r {
case ':', ',', ' ', ';':
return true
default:
return false
}
})
}
var tlsCipherSuiteNames = map[string]uint16{
"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_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_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_RSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
"TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256,
"TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384,
"TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_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,
"AES128-GCM-SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
"AES256-GCM-SHA384": tls.TLS_RSA_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,
"CHACHA20-POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
}