mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-10-08 22:59:51 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adff74795e |
@@ -7,11 +7,9 @@ import (
|
|||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/buf"
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/mux"
|
"github.com/xtls/xray-core/common/mux"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/protocol"
|
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
"github.com/xtls/xray-core/testing/mocks"
|
"github.com/xtls/xray-core/testing/mocks"
|
||||||
"github.com/xtls/xray-core/transport"
|
"github.com/xtls/xray-core/transport"
|
||||||
@@ -116,48 +114,3 @@ func TestClientWorkerClose(t *testing.T) {
|
|||||||
|
|
||||||
common.Must(w2.Close())
|
common.Must(w2.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClientWorkerUDPSource(t *testing.T) {
|
|
||||||
downR, downW := pipe.New(pipe.WithoutSizeLimit())
|
|
||||||
upR, upW := pipe.New(pipe.WithoutSizeLimit())
|
|
||||||
worker, err := mux.NewClientWorker(transport.Link{Reader: downR, Writer: upW}, mux.ClientStrategy{})
|
|
||||||
common.Must(err)
|
|
||||||
|
|
||||||
inR, inW := pipe.New(pipe.WithoutSizeLimit())
|
|
||||||
outR, outW := pipe.New(pipe.WithoutSizeLimit())
|
|
||||||
ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{
|
|
||||||
Target: net.UDPDestination(net.ParseAddress("8.8.8.8"), 53),
|
|
||||||
}})
|
|
||||||
if !worker.Dispatch(ctx, &transport.Link{Reader: inR, Writer: outW}) {
|
|
||||||
t.Fatal("failed to dispatch")
|
|
||||||
}
|
|
||||||
b := buf.New()
|
|
||||||
b.WriteString("query")
|
|
||||||
common.Must(inW.WriteMultiBuffer(buf.MultiBuffer{b}))
|
|
||||||
mb, err := upR.ReadMultiBuffer() // New frame, the session is UDP from now on
|
|
||||||
common.Must(err)
|
|
||||||
buf.ReleaseMulti(mb)
|
|
||||||
|
|
||||||
srcs := []net.Destination{
|
|
||||||
net.UDPDestination(net.ParseAddress("1.1.1.1"), 1111),
|
|
||||||
net.UDPDestination(net.DomainAddress("example.com"), 2222),
|
|
||||||
net.UDPDestination(net.ParseAddress("3.3.3.3"), 3333),
|
|
||||||
}
|
|
||||||
w := mux.NewResponseWriter(1, downW, protocol.TransferTypePacket)
|
|
||||||
var got buf.MultiBuffer
|
|
||||||
for i := range srcs {
|
|
||||||
b := buf.New()
|
|
||||||
b.WriteString("reply")
|
|
||||||
b.UDP = &srcs[i]
|
|
||||||
common.Must(w.WriteMultiBuffer(buf.MultiBuffer{b}))
|
|
||||||
// keep earlier replies around while the next frame is parsed
|
|
||||||
mb, err := outR.ReadMultiBuffer()
|
|
||||||
common.Must(err)
|
|
||||||
got = append(got, mb...)
|
|
||||||
}
|
|
||||||
for i, b := range got {
|
|
||||||
if b.UDP == nil || *b.UDP != srcs[i] {
|
|
||||||
t.Errorf("reply %d: source = %v, want %v", i, b.UDP, srcs[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -14,16 +14,15 @@ import (
|
|||||||
type PacketReader struct {
|
type PacketReader struct {
|
||||||
reader io.Reader
|
reader io.Reader
|
||||||
eof bool
|
eof bool
|
||||||
dest net.Destination
|
dest *net.Destination
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPacketReader creates a new PacketReader.
|
// NewPacketReader creates a new PacketReader.
|
||||||
// dest is copied because the caller reuses it for the next frame.
|
|
||||||
func NewPacketReader(reader io.Reader, dest *net.Destination) *PacketReader {
|
func NewPacketReader(reader io.Reader, dest *net.Destination) *PacketReader {
|
||||||
return &PacketReader{
|
return &PacketReader{
|
||||||
reader: reader,
|
reader: reader,
|
||||||
eof: false,
|
eof: false,
|
||||||
dest: *dest,
|
dest: dest,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,8 +47,8 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
r.eof = true
|
r.eof = true
|
||||||
if r.dest.Network == net.Network_UDP {
|
if r.dest != nil && r.dest.Network == net.Network_UDP {
|
||||||
b.UDP = &r.dest // only one packet is read, so b owns r.dest
|
b.UDP = r.dest
|
||||||
}
|
}
|
||||||
return buf.MultiBuffer{b}, nil
|
return buf.MultiBuffer{b}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ require (
|
|||||||
github.com/vishvananda/netns v0.0.5 // indirect
|
github.com/vishvananda/netns v0.0.5 // indirect
|
||||||
github.com/wlynxg/anet v0.0.5 // indirect
|
github.com/wlynxg/anet v0.0.5 // indirect
|
||||||
go.yaml.in/yaml/v3 v3.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/text v0.42.0 // indirect
|
||||||
golang.org/x/time v0.14.0 // indirect
|
golang.org/x/time v0.14.0 // indirect
|
||||||
golang.org/x/tools v0.49.0 // indirect
|
golang.org/x/tools v0.49.0 // indirect
|
||||||
|
|||||||
@@ -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.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 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
||||||
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
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 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
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=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
|||||||
@@ -316,6 +316,7 @@ type TLSConfig struct {
|
|||||||
ECHServerKeys string `json:"echServerKeys"`
|
ECHServerKeys string `json:"echServerKeys"`
|
||||||
ECHConfigList string `json:"echConfigList"`
|
ECHConfigList string `json:"echConfigList"`
|
||||||
ECHSocketSettings *SocketConfig `json:"echSockopt"`
|
ECHSocketSettings *SocketConfig `json:"echSockopt"`
|
||||||
|
UseSystemCA bool `json:"useSystemCA"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
@@ -403,6 +404,7 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
config.EchSocketSettings = ss
|
config.EchSocketSettings = ss
|
||||||
}
|
}
|
||||||
|
config.UseSystemCa = c.UseSystemCA
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,17 +170,9 @@ func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatche
|
|||||||
return errors.New("UDP associate with listen port failed")
|
return errors.New("UDP associate with listen port failed")
|
||||||
}
|
}
|
||||||
tempUDPConn.SetTimeout(plcy.Timeouts.ConnectionIdle)
|
tempUDPConn.SetTimeout(plcy.Timeouts.ConnectionIdle)
|
||||||
var udpConn stat.Connection = tempUDPConn
|
|
||||||
if counters, ok := conn.(*stat.CounterConnection); ok {
|
|
||||||
udpConn = &stat.CounterConnection{
|
|
||||||
Connection: tempUDPConn,
|
|
||||||
ReadCounter: counters.ReadCounter,
|
|
||||||
WriteCounter: counters.WriteCounter,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
errCh := make(chan error, 1)
|
errCh := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
errCh <- s.handleUDPPayload(ctx, udpConn, dispatcher)
|
errCh <- s.handleUDPPayload(ctx, tempUDPConn, dispatcher)
|
||||||
}()
|
}()
|
||||||
// Associated TCP keeps the UDP alive
|
// Associated TCP keeps the UDP alive
|
||||||
// Close UDP if TCP connection is closed
|
// Close UDP if TCP connection is closed
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||||
"github.com/xtls/xray-core/common/protocol/tls/cert"
|
"github.com/xtls/xray-core/common/protocol/tls/cert"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
"golang.org/x/crypto/x509roots/fallback/bundle"
|
||||||
)
|
)
|
||||||
|
|
||||||
var globalSessionCache = tls.NewLRUClientSessionCache(128)
|
var globalSessionCache = tls.NewLRUClientSessionCache(128)
|
||||||
@@ -578,3 +579,39 @@ func verifyChain(certs []*x509.Certificate, pinnedPeerCertSha256 [][]byte) (veri
|
|||||||
}
|
}
|
||||||
return certNotFound, nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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"`
|
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"`
|
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"`
|
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
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -359,6 +360,13 @@ func (x *Config) GetPinnedPeerCertSha256() [][]byte {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetUseSystemCa() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.UseSystemCa
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
var File_transport_internet_tls_config_proto protoreflect.FileDescriptor
|
var File_transport_internet_tls_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
const file_transport_internet_tls_config_proto_rawDesc = "" +
|
const file_transport_internet_tls_config_proto_rawDesc = "" +
|
||||||
@@ -377,7 +385,7 @@ const file_transport_internet_tls_config_proto_rawDesc = "" +
|
|||||||
"\x05Usage\x12\x10\n" +
|
"\x05Usage\x12\x10\n" +
|
||||||
"\fENCIPHERMENT\x10\x00\x12\x14\n" +
|
"\fENCIPHERMENT\x10\x00\x12\x14\n" +
|
||||||
"\x10AUTHORITY_VERIFY\x10\x01\x12\x13\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" +
|
"\x06Config\x12J\n" +
|
||||||
"\vcertificate\x18\x02 \x03(\v2(.xray.transport.internet.tls.CertificateR\vcertificate\x12\x1f\n" +
|
"\vcertificate\x18\x02 \x03(\v2(.xray.transport.internet.tls.CertificateR\vcertificate\x12\x1f\n" +
|
||||||
"\vserver_name\x18\x03 \x01(\tR\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_server_keys\x18\x12 \x01(\fR\rechServerKeys\x12&\n" +
|
||||||
"\x0fech_config_list\x18\x13 \x01(\tR\rechConfigList\x12U\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" +
|
"\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"
|
"\x1fcom.xray.transport.internet.tlsP\x01Z0github.com/xtls/xray-core/transport/internet/tls\xaa\x02\x1bXray.Transport.Internet.Tlsb\x06proto3"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -86,4 +86,6 @@ message Config {
|
|||||||
SocketConfig ech_socket_settings = 21;
|
SocketConfig ech_socket_settings = 21;
|
||||||
|
|
||||||
repeated bytes pinned_peer_cert_sha256 = 22;
|
repeated bytes pinned_peer_cert_sha256 = 22;
|
||||||
|
|
||||||
|
bool use_system_ca = 23;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,50 +5,23 @@ package tls
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"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) {
|
func (c *Config) getCertPool() (*x509.CertPool, error) {
|
||||||
if c.DisableSystemRoot {
|
if c.DisableSystemRoot {
|
||||||
return c.loadSelfCertPool()
|
return c.loadSelfCertPool()
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.Certificate) == 0 {
|
if len(c.Certificate) == 0 {
|
||||||
return rootCerts.load()
|
return loadCA(c.UseSystemCa), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pool, err := x509.SystemCertPool()
|
pool := loadCA(c.UseSystemCa).Clone()
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("system root").Base(err)
|
|
||||||
}
|
|
||||||
for _, cert := range c.Certificate {
|
for _, cert := range c.Certificate {
|
||||||
if !pool.AppendCertsFromPEM(cert.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
|
return pool, nil
|
||||||
|
|||||||
@@ -3,12 +3,30 @@
|
|||||||
|
|
||||||
package tls
|
package tls
|
||||||
|
|
||||||
import "crypto/x509"
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
)
|
||||||
|
|
||||||
func (c *Config) getCertPool() (*x509.CertPool, error) {
|
func (c *Config) getCertPool() (*x509.CertPool, error) {
|
||||||
if c.DisableSystemRoot {
|
if c.DisableSystemRoot {
|
||||||
return c.loadSelfCertPool()
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user