chore: cleanup internal ca using

This commit is contained in:
wwqgtxx 2025-09-13 14:07:49 +08:00
parent 08fc100c85
commit 57e14e5b62
21 changed files with 212 additions and 152 deletions

View File

@ -2,7 +2,6 @@ package adapter
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"net"
@ -236,6 +235,11 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In
}
req = req.WithContext(ctx)
tlsConfig, err := ca.GetTLSConfig(ca.Option{})
if err != nil {
return
}
transport := &http.Transport{
DialContext: func(context.Context, string, string) (net.Conn, error) {
return instance, nil
@ -245,7 +249,7 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: ca.GetGlobalTLSConfig(&tls.Config{}),
TLSClientConfig: tlsConfig,
}
client := http.Client{

View File

@ -167,10 +167,13 @@ func NewHttp(option HttpOption) (*Http, error) {
sni = option.SNI
}
var err error
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(&tls.Config{
InsecureSkipVerify: option.SkipCertVerify,
ServerName: sni,
}, option.Fingerprint)
tlsConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
InsecureSkipVerify: option.SkipCertVerify,
ServerName: sni,
},
Fingerprint: option.Fingerprint,
})
if err != nil {
return nil, err
}

View File

@ -167,7 +167,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
}
var err error
tlsConfig, err = ca.GetTLSConfig(tlsConfig, option.Fingerprint, option.CustomCA, option.CustomCAString)
tlsConfig, err = ca.GetTLSConfig(ca.Option{TLSConfig: tlsConfig, Fingerprint: option.Fingerprint, CustomCA: option.CustomCA, CustomCAString: option.CustomCAString})
if err != nil {
return nil, err
}

View File

@ -148,7 +148,7 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {
}
var err error
tlsConfig, err = ca.GetTLSConfig(tlsConfig, option.Fingerprint, option.CustomCA, option.CustomCAString)
tlsConfig, err = ca.GetTLSConfig(ca.Option{TLSConfig: tlsConfig, Fingerprint: option.Fingerprint, CustomCA: option.CustomCA, CustomCAString: option.CustomCAString})
if err != nil {
return nil, err
}

View File

@ -193,13 +193,14 @@ func (ss *Socks5) clientHandshakeContext(ctx context.Context, c net.Conn, addr s
func NewSocks5(option Socks5Option) (*Socks5, error) {
var tlsConfig *tls.Config
if option.TLS {
tlsConfig = &tls.Config{
InsecureSkipVerify: option.SkipCertVerify,
ServerName: option.Server,
}
var err error
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
tlsConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
InsecureSkipVerify: option.SkipCertVerify,
ServerName: option.Server,
},
Fingerprint: option.Fingerprint,
})
if err != nil {
return nil, err
}

View File

@ -100,14 +100,15 @@ func (t *Trojan) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.
}
wsOpts.TLS = true
tlsConfig := &tls.Config{
NextProtos: alpn,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: t.option.SkipCertVerify,
ServerName: t.option.SNI,
}
wsOpts.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, t.option.Fingerprint)
wsOpts.TLSConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
NextProtos: alpn,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: t.option.SkipCertVerify,
ServerName: t.option.SNI,
},
Fingerprint: t.option.Fingerprint,
})
if err != nil {
return nil, err
}
@ -363,15 +364,15 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
return c, nil
}
tlsConfig := &tls.Config{
NextProtos: option.ALPN,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: option.SkipCertVerify,
ServerName: option.SNI,
}
var err error
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
NextProtos: option.ALPN,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: option.SkipCertVerify,
ServerName: option.SNI,
},
Fingerprint: option.Fingerprint,
})
if err != nil {
return nil, err
}

View File

@ -171,7 +171,7 @@ func NewTuic(option TuicOption) (*Tuic, error) {
}
var err error
tlsConfig, err = ca.GetTLSConfig(tlsConfig, option.Fingerprint, option.CustomCA, option.CustomCAString)
tlsConfig, err = ca.GetTLSConfig(ca.Option{TLSConfig: tlsConfig, Fingerprint: option.Fingerprint, CustomCA: option.CustomCA, CustomCAString: option.CustomCAString})
if err != nil {
return nil, err
}

View File

@ -95,14 +95,15 @@ func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
}
if v.option.TLS {
wsOpts.TLS = true
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS12,
ServerName: host,
InsecureSkipVerify: v.option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
}
wsOpts.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint)
wsOpts.TLSConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
ServerName: host,
InsecureSkipVerify: v.option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
},
Fingerprint: v.option.Fingerprint,
})
if err != nil {
return nil, err
}
@ -498,10 +499,13 @@ func NewVless(option VlessOption) (*Vless, error) {
}
var tlsConfig *tls.Config
if option.TLS {
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(&tls.Config{
InsecureSkipVerify: v.option.SkipCertVerify,
ServerName: v.option.ServerName,
}, v.option.Fingerprint)
tlsConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
InsecureSkipVerify: v.option.SkipCertVerify,
ServerName: v.option.ServerName,
},
Fingerprint: v.option.Fingerprint,
})
if err != nil {
return nil, err
}

View File

@ -123,13 +123,14 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
if v.option.TLS {
wsOpts.TLS = true
tlsConfig := &tls.Config{
ServerName: host,
InsecureSkipVerify: v.option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
}
wsOpts.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint)
wsOpts.TLSConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: host,
InsecureSkipVerify: v.option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
},
Fingerprint: v.option.Fingerprint,
})
if err != nil {
return nil, err
}
@ -501,10 +502,13 @@ func NewVmess(option VmessOption) (*Vmess, error) {
}
var tlsConfig *tls.Config
if option.TLS {
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(&tls.Config{
InsecureSkipVerify: v.option.SkipCertVerify,
ServerName: v.option.ServerName,
}, v.option.Fingerprint)
tlsConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
InsecureSkipVerify: v.option.SkipCertVerify,
ServerName: v.option.ServerName,
},
Fingerprint: v.option.Fingerprint,
})
if err != nil {
return nil, err
}

View File

@ -10,6 +10,7 @@ import (
"strconv"
"sync"
"github.com/metacubex/mihomo/common/once"
C "github.com/metacubex/mihomo/constant"
)
@ -65,18 +66,6 @@ func ResetCertificate() {
initializeCertPool()
}
func getCertPool() *x509.CertPool {
if globalCertPool == nil {
mutex.Lock()
defer mutex.Unlock()
if globalCertPool != nil {
return globalCertPool
}
initializeCertPool()
}
return globalCertPool
}
func GetCertPool(customCA string, customCAString string) (*x509.CertPool, error) {
var certificate []byte
var err error
@ -99,22 +88,40 @@ func GetCertPool(customCA string, customCAString string) (*x509.CertPool, error)
}
return certPool, nil
} else {
return getCertPool(), nil
mutex.Lock()
defer mutex.Unlock()
if globalCertPool == nil {
initializeCertPool()
}
return globalCertPool, nil
}
}
// GetTLSConfig specified fingerprint, customCA and customCAString
func GetTLSConfig(tlsConfig *tls.Config, fingerprint string, customCA string, customCAString string) (_ *tls.Config, err error) {
type Option struct {
TLSConfig *tls.Config
Fingerprint string
CustomCA string
CustomCAString string
ZeroTrust bool
}
func GetTLSConfig(opt Option) (tlsConfig *tls.Config, err error) {
tlsConfig = opt.TLSConfig
if tlsConfig == nil {
tlsConfig = &tls.Config{}
}
tlsConfig.RootCAs, err = GetCertPool(customCA, customCAString)
if err != nil {
return nil, err
if opt.ZeroTrust {
tlsConfig.RootCAs = zeroTrustCertPool()
} else {
tlsConfig.RootCAs, err = GetCertPool(opt.CustomCA, opt.CustomCAString)
if err != nil {
return nil, err
}
}
if len(fingerprint) > 0 {
tlsConfig.VerifyPeerCertificate, err = NewFingerprintVerifier(fingerprint)
if len(opt.Fingerprint) > 0 {
tlsConfig.VerifyPeerCertificate, err = NewFingerprintVerifier(opt.Fingerprint)
if err != nil {
return nil, err
}
@ -123,12 +130,12 @@ func GetTLSConfig(tlsConfig *tls.Config, fingerprint string, customCA string, cu
return tlsConfig, nil
}
// GetSpecifiedFingerprintTLSConfig specified fingerprint
func GetSpecifiedFingerprintTLSConfig(tlsConfig *tls.Config, fingerprint string) (*tls.Config, error) {
return GetTLSConfig(tlsConfig, fingerprint, "", "")
}
func GetGlobalTLSConfig(tlsConfig *tls.Config) *tls.Config {
tlsConfig, _ = GetTLSConfig(tlsConfig, "", "", "")
return tlsConfig
}
var zeroTrustCertPool = once.OnceValue(func() *x509.CertPool {
if len(_CaCertificates) != 0 { // always using embed cert first
zeroTrustCertPool := x509.NewCertPool()
if zeroTrustCertPool.AppendCertsFromPEM(_CaCertificates) {
return zeroTrustCertPool
}
}
return nil // fallback to system pool
})

View File

@ -2,7 +2,6 @@ package http
import (
"context"
"crypto/tls"
"io"
"net"
"net/http"
@ -28,11 +27,11 @@ func SetUA(UA string) {
ua = UA
}
func HttpRequest(ctx context.Context, url, method string, header map[string][]string, body io.Reader) (*http.Response, error) {
return HttpRequestWithProxy(ctx, url, method, header, body, "")
}
func HttpRequestWithProxy(ctx context.Context, url, method string, header map[string][]string, body io.Reader, specialProxy string) (*http.Response, error) {
func HttpRequest(ctx context.Context, url, method string, header map[string][]string, body io.Reader, options ...Option) (*http.Response, error) {
opt := option{}
for _, o := range options {
o(&opt)
}
method = strings.ToUpper(method)
urlRes, err := URL.Parse(url)
if err != nil {
@ -40,6 +39,10 @@ func HttpRequestWithProxy(ctx context.Context, url, method string, header map[st
}
req, err := http.NewRequest(method, urlRes.String(), body)
if err != nil {
return nil, err
}
for k, v := range header {
for _, v := range v {
req.Header.Add(k, v)
@ -50,10 +53,6 @@ func HttpRequestWithProxy(ctx context.Context, url, method string, header map[st
req.Header.Set("User-Agent", UA())
}
if err != nil {
return nil, err
}
if user := urlRes.User; user != nil {
password, _ := user.Password()
req.SetBasicAuth(user.Username(), password)
@ -61,6 +60,11 @@ func HttpRequestWithProxy(ctx context.Context, url, method string, header map[st
req = req.WithContext(ctx)
tlsConfig, err := ca.GetTLSConfig(opt.caOption)
if err != nil {
return nil, err
}
transport := &http.Transport{
// from http.DefaultTransport
DisableKeepAlives: runtime.GOOS == "android",
@ -69,15 +73,34 @@ func HttpRequestWithProxy(ctx context.Context, url, method string, header map[st
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
if conn, err := inner.HandleTcp(inner.GetTunnel(), address, specialProxy); err == nil {
if conn, err := inner.HandleTcp(inner.GetTunnel(), address, opt.specialProxy); err == nil {
return conn, nil
} else {
return dialer.DialContext(ctx, network, address)
}
},
TLSClientConfig: ca.GetGlobalTLSConfig(&tls.Config{}),
TLSClientConfig: tlsConfig,
}
client := http.Client{Transport: transport}
return client.Do(req)
}
type Option func(opt *option)
type option struct {
specialProxy string
caOption ca.Option
}
func WithSpecialProxy(name string) Option {
return func(opt *option) {
opt.specialProxy = name
}
}
func WithCAOption(caOption ca.Option) Option {
return func(opt *option) {
opt.caOption = caOption
}
}

View File

@ -135,7 +135,7 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []b
setIfNoneMatch = true
}
}
resp, err := mihomoHttp.HttpRequestWithProxy(ctx, h.url, http.MethodGet, header, nil, h.proxy)
resp, err := mihomoHttp.HttpRequest(ctx, h.url, http.MethodGet, header, nil, mihomoHttp.WithSpecialProxy(h.proxy))
if err != nil {
return
}

View File

@ -15,6 +15,7 @@ import (
"sync"
"time"
"github.com/metacubex/mihomo/component/ca"
mihomoHttp "github.com/metacubex/mihomo/component/http"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/constant/features"
@ -171,7 +172,7 @@ func (u *CoreUpdater) Update(currentExePath string, channel string, force bool)
func (u *CoreUpdater) getLatestVersion(versionURL string) (version string, err error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, nil, nil)
resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, nil, nil, mihomoHttp.WithCAOption(ca.Option{ZeroTrust: true}))
if err != nil {
return "", err
}
@ -194,7 +195,7 @@ func (u *CoreUpdater) getLatestVersion(versionURL string) (version string, err e
func (u *CoreUpdater) download(updateDir, packagePath, packageURL string) (err error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
defer cancel()
resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, nil, nil)
resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, nil, nil, mihomoHttp.WithCAOption(ca.Option{ZeroTrust: true}))
if err != nil {
return fmt.Errorf("http request failed: %w", err)
}

View File

@ -48,6 +48,11 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
network = "tcp"
}
tlsConfig, err := ca.GetTLSConfig(ca.Option{TLSConfig: c.Client.TLSConfig})
if err != nil {
return nil, err
}
addr := net.JoinHostPort(c.host, c.port)
conn, err := c.dialer.DialContext(ctx, network, addr)
if err != nil {
@ -66,7 +71,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
ch := make(chan result, 1)
go func() {
if strings.HasSuffix(c.Client.Net, "tls") {
conn = tls.Client(conn, ca.GetGlobalTLSConfig(c.Client.TLSConfig))
conn = tls.Client(conn, tlsConfig)
}
dConn := &D.Conn{

View File

@ -397,12 +397,14 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp
return transport, nil
}
tlsConfig := ca.GetGlobalTLSConfig(
&tls.Config{
InsecureSkipVerify: doh.skipCertVerify,
MinVersion: tls.VersionTLS12,
SessionTicketsDisabled: false,
})
tlsConfig, err := ca.GetTLSConfig(ca.Option{TLSConfig: &tls.Config{
InsecureSkipVerify: doh.skipCertVerify,
MinVersion: tls.VersionTLS12,
SessionTicketsDisabled: false,
}})
if err != nil {
return nil, err
}
var nextProtos []string
for _, v := range doh.httpVersions {
nextProtos = append(nextProtos, string(v))

View File

@ -331,15 +331,17 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn *quic.Conn, er
return nil, err
}
tlsConfig := ca.GetGlobalTLSConfig(
&tls.Config{
ServerName: host,
InsecureSkipVerify: doq.skipCertVerify,
NextProtos: []string{
NextProtoDQ,
},
SessionTicketsDisabled: false,
})
tlsConfig, err := ca.GetTLSConfig(ca.Option{TLSConfig: &tls.Config{
ServerName: host,
InsecureSkipVerify: doq.skipCertVerify,
NextProtos: []string{
NextProtoDQ,
},
SessionTicketsDisabled: false,
}})
if err != nil {
return nil, err
}
transport := quic.Transport{Conn: udp}
transport.SetCreatedConn(true) // auto close conn

View File

@ -39,7 +39,7 @@ var userUUID = utils.NewUUIDV4().String()
var tlsCertificate, tlsPrivateKey, tlsFingerprint, _ = ca.NewRandomTLSKeyPair(ca.KeyPairTypeP256)
var tlsConfigCert, _ = tls.X509KeyPair([]byte(tlsCertificate), []byte(tlsPrivateKey))
var tlsConfig = &tls.Config{Certificates: []tls.Certificate{tlsConfigCert}, NextProtos: []string{"h2", "http/1.1"}}
var tlsClientConfig, _ = ca.GetTLSConfig(nil, tlsFingerprint, "", "")
var tlsClientConfig, _ = ca.GetTLSConfig(ca.Option{Fingerprint: tlsFingerprint})
var realityPrivateKey, realityPublickey string
var realityDest = "itunes.apple.com"
var realityShortid = "10f897e26c4b9478"

View File

@ -57,15 +57,17 @@ func NewGostWebsocket(ctx context.Context, conn net.Conn, option *Option) (net.C
Headers: header,
}
var err error
if option.TLS {
config.TLS = true
tlsConfig := &tls.Config{
ServerName: option.Host,
InsecureSkipVerify: option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
}
var err error
config.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
config.TLSConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: option.Host,
InsecureSkipVerify: option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
},
Fingerprint: option.Fingerprint,
})
if err != nil {
return nil, err
}
@ -75,7 +77,6 @@ func NewGostWebsocket(ctx context.Context, conn net.Conn, option *Option) (net.C
}
}
var err error
conn, err = vmess.StreamWebsocketConn(ctx, conn, config)
if err != nil {
return nil, err

View File

@ -33,22 +33,23 @@ type ShadowTLSOption struct {
}
func NewShadowTLS(ctx context.Context, conn net.Conn, option *ShadowTLSOption) (net.Conn, error) {
tlsConfig := &tls.Config{
NextProtos: option.ALPN,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: option.SkipCertVerify,
ServerName: option.Host,
}
if option.Version == 1 {
tlsConfig.MaxVersion = tls.VersionTLS12 // ShadowTLS v1 only support TLS 1.2
}
var err error
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
NextProtos: option.ALPN,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: option.SkipCertVerify,
ServerName: option.Host,
},
Fingerprint: option.Fingerprint,
})
if err != nil {
return nil, err
}
if option.Version == 1 {
tlsConfig.MaxVersion = tls.VersionTLS12 // ShadowTLS v1 only support TLS 1.2
}
tlsHandshake := uTLSHandshakeFunc(tlsConfig, option.ClientFingerprint, option.Version)
client, err := shadowtls.NewClient(shadowtls.ClientConfig{
Version: option.Version,

View File

@ -43,15 +43,17 @@ func NewV2rayObfs(ctx context.Context, conn net.Conn, option *Option) (net.Conn,
Headers: header,
}
var err error
if option.TLS {
config.TLS = true
tlsConfig := &tls.Config{
ServerName: option.Host,
InsecureSkipVerify: option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
}
var err error
config.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
config.TLSConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: option.Host,
InsecureSkipVerify: option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
},
Fingerprint: option.Fingerprint,
})
if err != nil {
return nil, err
}
@ -61,7 +63,6 @@ func NewV2rayObfs(ctx context.Context, conn net.Conn, option *Option) (net.Conn,
}
}
var err error
conn, err = vmess.StreamWebsocketConn(ctx, conn, config)
if err != nil {
return nil, err

View File

@ -26,14 +26,14 @@ type ECHConfig struct {
}
func StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn, error) {
tlsConfig := &tls.Config{
ServerName: cfg.Host,
InsecureSkipVerify: cfg.SkipCertVerify,
NextProtos: cfg.NextProtos,
}
var err error
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, cfg.FingerPrint)
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: cfg.Host,
InsecureSkipVerify: cfg.SkipCertVerify,
NextProtos: cfg.NextProtos,
},
Fingerprint: cfg.FingerPrint,
})
if err != nil {
return nil, err
}