feat(ReverseProxy): add SSL/TLS configuration options and build TLS config method

This commit is contained in:
yusing
2025-09-21 10:47:37 +08:00
parent a48ccb4423
commit de1f4da126
2 changed files with 112 additions and 4 deletions

View File

@@ -1,7 +1,6 @@
package route
import (
"crypto/tls"
"net/http"
"sync"
@@ -44,10 +43,12 @@ func NewReverseProxyRoute(base *Route) (*ReveseProxyRoute, gperr.Error) {
trans = a.Transport()
proxyURL = nettypes.NewURL(agent.HTTPProxyURL)
} else {
trans = gphttp.NewTransport()
if httpConfig.NoTLSVerify {
trans.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} //nolint:gosec
tlsConfig, err := httpConfig.BuildTLSConfig(base.ProxyURL)
if err != nil {
return nil, err
}
trans = gphttp.NewTransportWithTLSConfig(tlsConfig)
if httpConfig.ResponseHeaderTimeout > 0 {
trans.ResponseHeaderTimeout = httpConfig.ResponseHeaderTimeout
}

View File

@@ -1,11 +1,118 @@
package route
import (
"crypto/tls"
"crypto/x509"
"os"
"strings"
"time"
"github.com/yusing/go-proxy/internal/gperr"
nettypes "github.com/yusing/go-proxy/internal/net/types"
)
type HTTPConfig struct {
NoTLSVerify bool `json:"no_tls_verify,omitempty"`
ResponseHeaderTimeout time.Duration `json:"response_header_timeout,omitempty" swaggertype:"primitive,integer"`
DisableCompression bool `json:"disable_compression,omitempty"`
// SSL/TLS proxy options (nginx-like)
SSLServerName *string `json:"ssl_server_name,omitempty"` // SNI server name
SSLTrustedCertificate string `json:"ssl_trusted_certificate,omitempty"` // Path to trusted CA certificates
SSLCertificate string `json:"ssl_certificate,omitempty"` // Path to client certificate
SSLCertificateKey string `json:"ssl_certificate_key,omitempty"` // Path to client certificate key
SSLProtocols []string `json:"ssl_protocols,omitempty"` // Allowed TLS protocols
}
// BuildTLSConfig creates a TLS configuration based on the HTTP config options.
func (cfg *HTTPConfig) BuildTLSConfig(targetURL *nettypes.URL) (*tls.Config, gperr.Error) {
tlsConfig := &tls.Config{}
// Handle InsecureSkipVerify (legacy NoTLSVerify option)
if cfg.NoTLSVerify {
tlsConfig.InsecureSkipVerify = true
}
// Handle ssl_server_name (SNI)
if cfg.SSLServerName != nil {
switch *cfg.SSLServerName {
case "off":
// Disable SNI by setting empty string
tlsConfig.ServerName = ""
case "on", "":
// Use hostname from target URL for SNI
tlsConfig.ServerName = targetURL.Hostname()
default:
tlsConfig.ServerName = *cfg.SSLServerName
}
} else {
// Default behavior - use hostname for SNI
tlsConfig.ServerName = targetURL.Hostname()
}
// Handle ssl_trusted_certificate
if cfg.SSLTrustedCertificate != "" {
caCertData, err := os.ReadFile(cfg.SSLTrustedCertificate)
if err != nil {
return nil, gperr.New("failed to read trusted certificate file").
Subject(cfg.SSLTrustedCertificate).
With(err)
}
caCertPool := x509.NewCertPool()
if !caCertPool.AppendCertsFromPEM(caCertData) {
return nil, gperr.New("failed to parse trusted certificates").
Subject(cfg.SSLTrustedCertificate)
}
tlsConfig.RootCAs = caCertPool
}
// Handle ssl_certificate and ssl_certificate_key (client certificates)
if cfg.SSLCertificate != "" {
if cfg.SSLCertificateKey == "" {
return nil, gperr.New("ssl_certificate_key is required when ssl_certificate is specified")
}
clientCert, err := tls.LoadX509KeyPair(cfg.SSLCertificate, cfg.SSLCertificateKey)
if err != nil {
return nil, gperr.New("failed to load client certificate").
Subject(cfg.SSLCertificate).
With(err)
}
tlsConfig.Certificates = []tls.Certificate{clientCert}
}
// Handle ssl_protocols (TLS versions)
if len(cfg.SSLProtocols) > 0 {
var minVersion, maxVersion uint16
for _, protocol := range cfg.SSLProtocols {
var version uint16
switch strings.ToLower(protocol) {
case "tlsv1.0":
version = tls.VersionTLS10
case "tlsv1.1":
version = tls.VersionTLS11
case "tlsv1.2":
version = tls.VersionTLS12
case "tlsv1.3":
version = tls.VersionTLS13
default:
return nil, gperr.New("unsupported TLS protocol").
Subject(protocol)
}
if minVersion == 0 || version < minVersion {
minVersion = version
}
if maxVersion == 0 || version > maxVersion {
maxVersion = version
}
}
tlsConfig.MinVersion = minVersion
tlsConfig.MaxVersion = maxVersion
}
return tlsConfig, nil
}