From adff74795ec5de25e8d5258cb875a93983f33865 Mon Sep 17 00:00:00 2001 From: Fangliding Date: Thu, 8 Oct 2026 14:26:12 +0800 Subject: [PATCH] Use bundle CA as default --- go.mod | 1 + go.sum | 2 ++ infra/conf/transport_security.go | 2 ++ transport/internet/tls/config.go | 37 ++++++++++++++++++++++++ transport/internet/tls/config.pb.go | 13 +++++++-- transport/internet/tls/config.proto | 2 ++ transport/internet/tls/config_other.go | 33 ++------------------- transport/internet/tls/config_windows.go | 22 ++++++++++++-- 8 files changed, 78 insertions(+), 34 deletions(-) diff --git a/go.mod b/go.mod index e42b021fc..6ddfa871c 100644 --- a/go.mod +++ b/go.mod @@ -55,6 +55,7 @@ require ( github.com/vishvananda/netns v0.0.5 // indirect github.com/wlynxg/anet v0.0.5 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect + golang.org/x/crypto/x509roots/fallback v0.0.0-20261005185213-c3db4df58582 // indirect golang.org/x/text v0.42.0 // indirect golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.49.0 // indirect diff --git a/go.sum b/go.sum index 27a84f980..f72773596 100644 --- a/go.sum +++ b/go.sum @@ -91,6 +91,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M= golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA= +golang.org/x/crypto/x509roots/fallback v0.0.0-20261005185213-c3db4df58582 h1:wjDBrGbfLuifgrVLFEWUBJYAfh5Q1wkMc5t0FY0tCbs= +golang.org/x/crypto/x509roots/fallback v0.0.0-20261005185213-c3db4df58582/go.mod h1:HPze8vhfG6fO06AM+VSvxRm4E3+5Yk375mgrJ5M2z1E= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= diff --git a/infra/conf/transport_security.go b/infra/conf/transport_security.go index b5cd7d28b..00e5387a9 100644 --- a/infra/conf/transport_security.go +++ b/infra/conf/transport_security.go @@ -316,6 +316,7 @@ type TLSConfig struct { ECHServerKeys string `json:"echServerKeys"` ECHConfigList string `json:"echConfigList"` ECHSocketSettings *SocketConfig `json:"echSockopt"` + UseSystemCA bool `json:"useSystemCA"` } // Build implements Buildable. @@ -403,6 +404,7 @@ func (c *TLSConfig) Build() (proto.Message, error) { } config.EchSocketSettings = ss } + config.UseSystemCa = c.UseSystemCA return config, nil } diff --git a/transport/internet/tls/config.go b/transport/internet/tls/config.go index f33e829c1..6fbe59320 100644 --- a/transport/internet/tls/config.go +++ b/transport/internet/tls/config.go @@ -19,6 +19,7 @@ import ( "github.com/xtls/xray-core/common/platform/filesystem" "github.com/xtls/xray-core/common/protocol/tls/cert" "github.com/xtls/xray-core/transport/internet" + "golang.org/x/crypto/x509roots/fallback/bundle" ) var globalSessionCache = tls.NewLRUClientSessionCache(128) @@ -578,3 +579,39 @@ func verifyChain(certs []*x509.Certificate, pinnedPeerCertSha256 [][]byte) (veri } return certNotFound, nil } + +var bundleCertPool = sync.OnceValue(func() *x509.CertPool { + pool := x509.NewCertPool() + for r := range bundle.Roots() { + cert, err := x509.ParseCertificate(r.Certificate) + if err != nil { + continue + } + if r.Constraint != nil { + pool.AddCertWithConstraint(cert, r.Constraint) + } else { + pool.AddCert(cert) + } + } + return pool +}) + +var systemCertPool = sync.OnceValue(func() *x509.CertPool { + pool, err := x509.SystemCertPool() + if err != nil { + // use bundle cert pool as fallback + pool = bundleCertPool() + } + return pool +}) + +// pool should not be modified directly, use CertPool.Clone() if needed. +func loadCA(useSystem bool) *x509.CertPool { + var pool *x509.CertPool + if useSystem { + pool = systemCertPool() + } else { + pool = bundleCertPool() + } + return pool +} diff --git a/transport/internet/tls/config.pb.go b/transport/internet/tls/config.pb.go index b622d5a61..a1c32db33 100644 --- a/transport/internet/tls/config.pb.go +++ b/transport/internet/tls/config.pb.go @@ -206,6 +206,7 @@ type Config struct { EchConfigList string `protobuf:"bytes,19,opt,name=ech_config_list,json=echConfigList,proto3" json:"ech_config_list,omitempty"` EchSocketSettings *internet.SocketConfig `protobuf:"bytes,21,opt,name=ech_socket_settings,json=echSocketSettings,proto3" json:"ech_socket_settings,omitempty"` PinnedPeerCertSha256 [][]byte `protobuf:"bytes,22,rep,name=pinned_peer_cert_sha256,json=pinnedPeerCertSha256,proto3" json:"pinned_peer_cert_sha256,omitempty"` + UseSystemCa bool `protobuf:"varint,23,opt,name=use_system_ca,json=useSystemCa,proto3" json:"use_system_ca,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -359,6 +360,13 @@ func (x *Config) GetPinnedPeerCertSha256() [][]byte { return nil } +func (x *Config) GetUseSystemCa() bool { + if x != nil { + return x.UseSystemCa + } + return false +} + var File_transport_internet_tls_config_proto protoreflect.FileDescriptor const file_transport_internet_tls_config_proto_rawDesc = "" + @@ -377,7 +385,7 @@ const file_transport_internet_tls_config_proto_rawDesc = "" + "\x05Usage\x12\x10\n" + "\fENCIPHERMENT\x10\x00\x12\x14\n" + "\x10AUTHORITY_VERIFY\x10\x01\x12\x13\n" + - "\x0fAUTHORITY_ISSUE\x10\x02\"\xa6\x06\n" + + "\x0fAUTHORITY_ISSUE\x10\x02\"\xca\x06\n" + "\x06Config\x12J\n" + "\vcertificate\x18\x02 \x03(\v2(.xray.transport.internet.tls.CertificateR\vcertificate\x12\x1f\n" + "\vserver_name\x18\x03 \x01(\tR\n" + @@ -398,7 +406,8 @@ const file_transport_internet_tls_config_proto_rawDesc = "" + "\x0fech_server_keys\x18\x12 \x01(\fR\rechServerKeys\x12&\n" + "\x0fech_config_list\x18\x13 \x01(\tR\rechConfigList\x12U\n" + "\x13ech_socket_settings\x18\x15 \x01(\v2%.xray.transport.internet.SocketConfigR\x11echSocketSettings\x125\n" + - "\x17pinned_peer_cert_sha256\x18\x16 \x03(\fR\x14pinnedPeerCertSha256Bs\n" + + "\x17pinned_peer_cert_sha256\x18\x16 \x03(\fR\x14pinnedPeerCertSha256\x12\"\n" + + "\ruse_system_ca\x18\x17 \x01(\bR\vuseSystemCaBs\n" + "\x1fcom.xray.transport.internet.tlsP\x01Z0github.com/xtls/xray-core/transport/internet/tls\xaa\x02\x1bXray.Transport.Internet.Tlsb\x06proto3" var ( diff --git a/transport/internet/tls/config.proto b/transport/internet/tls/config.proto index a05cc0494..b30418c82 100644 --- a/transport/internet/tls/config.proto +++ b/transport/internet/tls/config.proto @@ -86,4 +86,6 @@ message Config { SocketConfig ech_socket_settings = 21; repeated bytes pinned_peer_cert_sha256 = 22; + + bool use_system_ca = 23; } diff --git a/transport/internet/tls/config_other.go b/transport/internet/tls/config_other.go index 994723bfe..985c56c9c 100644 --- a/transport/internet/tls/config_other.go +++ b/transport/internet/tls/config_other.go @@ -5,50 +5,23 @@ package tls import ( "crypto/x509" - "sync" "github.com/xtls/xray-core/common/errors" ) -type rootCertsCache struct { - sync.Mutex - pool *x509.CertPool -} - -func (c *rootCertsCache) load() (*x509.CertPool, error) { - c.Lock() - defer c.Unlock() - - if c.pool != nil { - return c.pool, nil - } - - pool, err := x509.SystemCertPool() - if err != nil { - return nil, err - } - c.pool = pool - return pool, nil -} - -var rootCerts rootCertsCache - func (c *Config) getCertPool() (*x509.CertPool, error) { if c.DisableSystemRoot { return c.loadSelfCertPool() } if len(c.Certificate) == 0 { - return rootCerts.load() + return loadCA(c.UseSystemCa), nil } - pool, err := x509.SystemCertPool() - if err != nil { - return nil, errors.New("system root").Base(err) - } + pool := loadCA(c.UseSystemCa).Clone() for _, cert := range c.Certificate { if !pool.AppendCertsFromPEM(cert.Certificate) { - return nil, errors.New("append cert to root").Base(err) + return nil, errors.New("append cert to root") } } return pool, nil diff --git a/transport/internet/tls/config_windows.go b/transport/internet/tls/config_windows.go index 8818befe3..2ae586f45 100644 --- a/transport/internet/tls/config_windows.go +++ b/transport/internet/tls/config_windows.go @@ -3,12 +3,30 @@ package tls -import "crypto/x509" +import ( + "crypto/x509" + + "github.com/xtls/xray-core/common/errors" +) func (c *Config) getCertPool() (*x509.CertPool, error) { if c.DisableSystemRoot { return c.loadSelfCertPool() } - return nil, nil + // Windows should keep RootCAs nil for using the system CA. + if c.UseSystemCa && len(c.Certificate) == 0 { + return nil, nil + } + + if len(c.Certificate) == 0 { + return loadCA(c.UseSystemCa), nil + } + pool := loadCA(c.UseSystemCa).Clone() + for _, cert := range c.Certificate { + if !pool.AppendCertsFromPEM(cert.Certificate) { + return nil, errors.New("failed to append cert") + } + } + return pool, nil }