feat: initial goget release — modern IPv6-first download utility
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
//go:build linux || freebsd
|
||||
// +build linux freebsd
|
||||
|
||||
package transport
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TLSConfig configures TLS
|
||||
type TLSConfig struct {
|
||||
// Minimum TLS version
|
||||
MinVersion uint16
|
||||
|
||||
// Maximum TLS version
|
||||
MaxVersion uint16
|
||||
|
||||
// Path to CA certificates
|
||||
CACertFile string
|
||||
|
||||
// Skip certificate verification
|
||||
InsecureSkipVerify bool
|
||||
|
||||
// Client certificate for mTLS
|
||||
CertFile string
|
||||
|
||||
// Client private key for mTLS
|
||||
KeyFile string
|
||||
|
||||
// Certificate pinning (SHA-256 hash of certificate)
|
||||
PinnedCertHash string
|
||||
|
||||
// TLSServerNameOverride forces the SNI server name and the hostname used
|
||||
// for certificate verification. Leave empty (the default) so that Go's
|
||||
// stdlib derives the server name from the dial target, which is the safe
|
||||
// behavior. Setting this to a value that does not match the host you are
|
||||
// connecting to enables a man-in-the-middle attacker with a valid cert
|
||||
// for the overridden name to impersonate the target. Only set this when
|
||||
// you genuinely need to override SNI (e.g. connecting to an IP address
|
||||
// whose certificate carries a different hostname).
|
||||
TLSServerNameOverride string
|
||||
|
||||
// Path to SSLKEYLOGFILE for Wireshark debugging
|
||||
KeyLogFile string
|
||||
}
|
||||
|
||||
// DefaultTLSConfig returns a safe default configuration
|
||||
func DefaultTLSConfig() *TLSConfig {
|
||||
return &TLSConfig{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
MaxVersion: tls.VersionTLS13,
|
||||
InsecureSkipVerify: false,
|
||||
}
|
||||
}
|
||||
|
||||
// ToTLSConfig converts to tls.Config
|
||||
func (c *TLSConfig) ToTLSConfig() (*tls.Config, error) {
|
||||
tlsCfg := &tls.Config{
|
||||
MinVersion: c.MinVersion,
|
||||
MaxVersion: c.MaxVersion,
|
||||
InsecureSkipVerify: c.InsecureSkipVerify,
|
||||
}
|
||||
|
||||
// Only apply the override if explicitly set. When empty, Go's net/http
|
||||
// transport derives ServerName from the dial address, which matches the
|
||||
// certificate the server is expected to present.
|
||||
if c.TLSServerNameOverride != "" {
|
||||
tlsCfg.ServerName = c.TLSServerNameOverride
|
||||
}
|
||||
|
||||
// Load custom CA certificates
|
||||
if c.CACertFile != "" {
|
||||
caCert, err := os.ReadFile(c.CACertFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read ca cert: %w", err)
|
||||
}
|
||||
|
||||
caCertPool := x509.NewCertPool()
|
||||
if !caCertPool.AppendCertsFromPEM(caCert) {
|
||||
return nil, fmt.Errorf("failed to parse ca cert")
|
||||
}
|
||||
|
||||
tlsCfg.RootCAs = caCertPool
|
||||
}
|
||||
|
||||
// Certificate pinning: verify SHA-256 hash of server certificate
|
||||
if c.PinnedCertHash != "" {
|
||||
expectedHash := strings.ToLower(strings.TrimSpace(c.PinnedCertHash))
|
||||
tlsCfg.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
|
||||
for _, rawCert := range rawCerts {
|
||||
cert, err := x509.ParseCertificate(rawCert)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
hash := sha256.Sum256(cert.Raw)
|
||||
actual := hex.EncodeToString(hash[:])
|
||||
if actual == expectedHash {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("certificate pinning failed: no certificate matches sha-256 %s", expectedHash)
|
||||
}
|
||||
}
|
||||
|
||||
// Load client certificate for mTLS
|
||||
if c.CertFile != "" && c.KeyFile != "" {
|
||||
cert, err := tls.LoadX509KeyPair(c.CertFile, c.KeyFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load client certificate: %w", err)
|
||||
}
|
||||
tlsCfg.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
|
||||
// SSLKEYLOGFILE for Wireshark/TLS debugging
|
||||
if c.KeyLogFile != "" {
|
||||
f, err := os.OpenFile(c.KeyLogFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open keylog file: %w", err)
|
||||
}
|
||||
tlsCfg.KeyLogWriter = f
|
||||
}
|
||||
|
||||
return tlsCfg, nil
|
||||
}
|
||||
|
||||
// GetTLSVersionName returns a human-readable TLS version name
|
||||
func GetTLSVersionName(version uint16) string {
|
||||
switch version {
|
||||
case tls.VersionTLS10:
|
||||
return "TLS 1.0"
|
||||
case tls.VersionTLS11:
|
||||
return "TLS 1.1"
|
||||
case tls.VersionTLS12:
|
||||
return "TLS 1.2"
|
||||
case tls.VersionTLS13:
|
||||
return "TLS 1.3"
|
||||
default:
|
||||
return fmt.Sprintf("TLS 0x%04X", version)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user