diff --git a/adapter/outbound/anytls.go b/adapter/outbound/anytls.go index 5210c3b7..6e28db20 100644 --- a/adapter/outbound/anytls.go +++ b/adapter/outbound/anytls.go @@ -33,6 +33,7 @@ type AnyTLSOption struct { ECHOpts ECHOptions `proxy:"ech-opts,omitempty"` ClientFingerprint string `proxy:"client-fingerprint,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + NameCertVerify string `proxy:"name-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` Certificate string `proxy:"certificate,omitempty"` PrivateKey string `proxy:"private-key,omitempty"` @@ -118,6 +119,7 @@ func NewAnyTLS(option AnyTLSOption) (*AnyTLS, error) { tlsConfig := &vmess.TLSConfig{ Host: option.SNI, SkipCertVerify: option.SkipCertVerify, + NameCertVerify: option.NameCertVerify, NextProtos: option.ALPN, FingerPrint: option.Fingerprint, Certificate: option.Certificate, diff --git a/adapter/outbound/gost_relay.go b/adapter/outbound/gost_relay.go index 43211c1b..b6b6674d 100644 --- a/adapter/outbound/gost_relay.go +++ b/adapter/outbound/gost_relay.go @@ -29,6 +29,7 @@ type GostRelayOption struct { Username string `proxy:"username,omitempty"` Password string `proxy:"password,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + NameCertVerify string `proxy:"name-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` Certificate string `proxy:"certificate,omitempty"` PrivateKey string `proxy:"private-key,omitempty"` @@ -100,6 +101,7 @@ func NewGostRelay(option GostRelayOption) (*GostRelay, error) { Username: option.Username, Password: option.Password, SkipCertVerify: option.SkipCertVerify, + NameCertVerify: option.NameCertVerify, Fingerprint: option.Fingerprint, Certificate: option.Certificate, PrivateKey: option.PrivateKey, diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index fe26c9c5..0f684488 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -35,6 +35,7 @@ type HttpOption struct { TLS bool `proxy:"tls,omitempty"` SNI string `proxy:"sni,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + NameCertVerify string `proxy:"name-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` Certificate string `proxy:"certificate,omitempty"` PrivateKey string `proxy:"private-key,omitempty"` @@ -157,9 +158,10 @@ func NewHttp(option HttpOption) (*Http, error) { InsecureSkipVerify: option.SkipCertVerify, ServerName: sni, }, - Fingerprint: option.Fingerprint, - Certificate: option.Certificate, - PrivateKey: option.PrivateKey, + Fingerprint: option.Fingerprint, + NameCertVerify: option.NameCertVerify, + Certificate: option.Certificate, + PrivateKey: option.PrivateKey, }) if err != nil { return nil, err diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index de35a0ee..26032953 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -116,6 +116,7 @@ type HysteriaOption struct { SNI string `proxy:"sni,omitempty"` ECHOpts ECHOptions `proxy:"ech-opts,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + NameCertVerify string `proxy:"name-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` Certificate string `proxy:"certificate,omitempty"` PrivateKey string `proxy:"private-key,omitempty"` @@ -158,9 +159,10 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { InsecureSkipVerify: option.SkipCertVerify, MinVersion: tls.VersionTLS13, }, - Fingerprint: option.Fingerprint, - Certificate: option.Certificate, - PrivateKey: option.PrivateKey, + Fingerprint: option.Fingerprint, + NameCertVerify: option.NameCertVerify, + Certificate: option.Certificate, + PrivateKey: option.PrivateKey, }) if err != nil { return nil, err diff --git a/adapter/outbound/hysteria2.go b/adapter/outbound/hysteria2.go index b31a1fad..8188d482 100644 --- a/adapter/outbound/hysteria2.go +++ b/adapter/outbound/hysteria2.go @@ -53,6 +53,7 @@ type Hysteria2Option struct { SNI string `proxy:"sni,omitempty"` ECHOpts ECHOptions `proxy:"ech-opts,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + NameCertVerify string `proxy:"name-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` Certificate string `proxy:"certificate,omitempty"` PrivateKey string `proxy:"private-key,omitempty"` @@ -80,6 +81,7 @@ type Hysteria2RealmOption struct { // for ServerURL SNI string `proxy:"sni,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + NameCertVerify string `proxy:"name-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` Certificate string `proxy:"certificate,omitempty"` PrivateKey string `proxy:"private-key,omitempty"` @@ -170,9 +172,10 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { InsecureSkipVerify: option.SkipCertVerify, MinVersion: tls.VersionTLS13, }, - Fingerprint: option.Fingerprint, - Certificate: option.Certificate, - PrivateKey: option.PrivateKey, + Fingerprint: option.Fingerprint, + NameCertVerify: option.NameCertVerify, + Certificate: option.Certificate, + PrivateKey: option.PrivateKey, }) if err != nil { return nil, err @@ -269,9 +272,10 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { InsecureSkipVerify: option.RealmOpts.SkipCertVerify, NextProtos: option.RealmOpts.ALPN, }, - Fingerprint: option.RealmOpts.Fingerprint, - Certificate: option.RealmOpts.Certificate, - PrivateKey: option.RealmOpts.PrivateKey, + Fingerprint: option.RealmOpts.Fingerprint, + NameCertVerify: option.RealmOpts.NameCertVerify, + Certificate: option.RealmOpts.Certificate, + PrivateKey: option.RealmOpts.PrivateKey, }) if err != nil { return nil, err diff --git a/adapter/outbound/masque.go b/adapter/outbound/masque.go index 9e03a3aa..e5cbbf2e 100644 --- a/adapter/outbound/masque.go +++ b/adapter/outbound/masque.go @@ -67,6 +67,7 @@ type MasqueOption struct { UDP bool `proxy:"udp,omitempty"` HandshakeTimeout int `proxy:"handshake-timeout,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + NameCertVerify string `proxy:"name-cert-verify,omitempty"` // placeholder; MASQUE does not verify certificate names Network string `proxy:"network,omitempty"` CongestionController string `proxy:"congestion-controller,omitempty"` diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 651d5da3..09892370 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -69,6 +69,7 @@ type v2rayObfsOption struct { PrivateKey string `obfs:"private-key,omitempty"` Headers map[string]string `obfs:"headers,omitempty"` SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` + NameCertVerify string `obfs:"name-cert-verify,omitempty"` Mux bool `obfs:"mux,omitempty"` V2rayHttpUpgrade bool `obfs:"v2ray-http-upgrade,omitempty"` V2rayHttpUpgradeFastOpen bool `obfs:"v2ray-http-upgrade-fast-open,omitempty"` @@ -85,6 +86,7 @@ type gostObfsOption struct { PrivateKey string `obfs:"private-key,omitempty"` Headers map[string]string `obfs:"headers,omitempty"` SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` + NameCertVerify string `obfs:"name-cert-verify,omitempty"` Mux bool `obfs:"mux,omitempty"` } @@ -95,6 +97,7 @@ type shadowTLSOption struct { Certificate string `obfs:"certificate,omitempty"` PrivateKey string `obfs:"private-key,omitempty"` SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` + NameCertVerify string `obfs:"name-cert-verify,omitempty"` Version int `obfs:"version,omitempty"` ALPN []string `obfs:"alpn,omitempty"` } @@ -106,6 +109,7 @@ type restlsOption struct { RestlsScript string `obfs:"restls-script,omitempty"` Fingerprint string `obfs:"fingerprint,omitempty"` SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` + NameCertVerify string `obfs:"name-cert-verify,omitempty"` ForceTLS12 bool `obfs:"force-tls12,omitempty"` // for test } @@ -331,6 +335,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { if opts.TLS { v2rayOption.TLS = true v2rayOption.SkipCertVerify = opts.SkipCertVerify + v2rayOption.NameCertVerify = opts.NameCertVerify v2rayOption.Fingerprint = opts.Fingerprint v2rayOption.Certificate = opts.Certificate v2rayOption.PrivateKey = opts.PrivateKey @@ -361,6 +366,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { if opts.TLS { gostOption.TLS = true gostOption.SkipCertVerify = opts.SkipCertVerify + gostOption.NameCertVerify = opts.NameCertVerify gostOption.Fingerprint = opts.Fingerprint gostOption.Certificate = opts.Certificate gostOption.PrivateKey = opts.PrivateKey @@ -388,6 +394,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { PrivateKey: opt.PrivateKey, ClientFingerprint: option.ClientFingerprint, SkipCertVerify: opt.SkipCertVerify, + NameCertVerify: opt.NameCertVerify, Version: opt.Version, } @@ -409,10 +416,12 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { } restlsConfig.InsecureSkipVerify = restlsOpt.SkipCertVerify if restlsOpt.Fingerprint != "" { - err = restls.SetFingerprint(restlsConfig, restlsOpt.Fingerprint) + err = restls.SetFingerprint(restlsConfig, restlsOpt.Fingerprint, restlsOpt.NameCertVerify) if err != nil { return nil, fmt.Errorf("ss %s initialize restls-plugin error: %w", addr, err) } + } else if restlsOpt.NameCertVerify != "" { + restls.SetNameCertVerify(restlsConfig, restlsOpt.NameCertVerify) } restlsConfig.ForceTLS12 = restlsOpt.ForceTLS12 } else if option.Plugin == kcptun.Mode { diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index 25bbbae2..7e29b842 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -180,6 +180,7 @@ func NewSnell(option SnellOption) (*Snell, error) { PrivateKey: opt.PrivateKey, ClientFingerprint: option.ClientFingerprint, SkipCertVerify: opt.SkipCertVerify, + NameCertVerify: opt.NameCertVerify, Version: opt.Version, } diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index cb35e3a0..7f3f111f 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -37,6 +37,7 @@ type Socks5Option struct { TLS bool `proxy:"tls,omitempty"` UDP bool `proxy:"udp,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + NameCertVerify string `proxy:"name-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` Certificate string `proxy:"certificate,omitempty"` PrivateKey string `proxy:"private-key,omitempty"` @@ -178,9 +179,10 @@ func NewSocks5(option Socks5Option) (*Socks5, error) { InsecureSkipVerify: option.SkipCertVerify, ServerName: option.Server, }, - Fingerprint: option.Fingerprint, - Certificate: option.Certificate, - PrivateKey: option.PrivateKey, + Fingerprint: option.Fingerprint, + NameCertVerify: option.NameCertVerify, + Certificate: option.Certificate, + PrivateKey: option.PrivateKey, }) if err != nil { return nil, err diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index cbf30c98..a8b30821 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -44,6 +44,7 @@ type TrojanOption struct { ALPN []string `proxy:"alpn,omitempty"` SNI string `proxy:"sni,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + NameCertVerify string `proxy:"name-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` Certificate string `proxy:"certificate,omitempty"` PrivateKey string `proxy:"private-key,omitempty"` @@ -105,9 +106,10 @@ func (t *Trojan) StreamConnContext(ctx context.Context, c net.Conn, metadata *C. InsecureSkipVerify: t.option.SkipCertVerify, ServerName: t.option.SNI, }, - Fingerprint: t.option.Fingerprint, - Certificate: t.option.Certificate, - PrivateKey: t.option.PrivateKey, + Fingerprint: t.option.Fingerprint, + NameCertVerify: t.option.NameCertVerify, + Certificate: t.option.Certificate, + PrivateKey: t.option.PrivateKey, }) if err != nil { return nil, err @@ -126,6 +128,7 @@ func (t *Trojan) StreamConnContext(ctx context.Context, c net.Conn, metadata *C. c, err = vmess.StreamTLSConn(ctx, c, &vmess.TLSConfig{ Host: t.option.SNI, SkipCertVerify: t.option.SkipCertVerify, + NameCertVerify: t.option.NameCertVerify, FingerPrint: t.option.Fingerprint, Certificate: t.option.Certificate, PrivateKey: t.option.PrivateKey, @@ -307,6 +310,7 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { tlsConfig := &vmess.TLSConfig{ Host: option.SNI, SkipCertVerify: option.SkipCertVerify, + NameCertVerify: option.NameCertVerify, FingerPrint: option.Fingerprint, Certificate: option.Certificate, PrivateKey: option.PrivateKey, diff --git a/adapter/outbound/trusttunnel.go b/adapter/outbound/trusttunnel.go index 16c341aa..81f784da 100644 --- a/adapter/outbound/trusttunnel.go +++ b/adapter/outbound/trusttunnel.go @@ -29,6 +29,7 @@ type TrustTunnelOption struct { ECHOpts ECHOptions `proxy:"ech-opts,omitempty"` ClientFingerprint string `proxy:"client-fingerprint,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + NameCertVerify string `proxy:"name-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` Certificate string `proxy:"certificate,omitempty"` PrivateKey string `proxy:"private-key,omitempty"` @@ -124,6 +125,7 @@ func NewTrustTunnel(option TrustTunnelOption) (*TrustTunnel, error) { tlsConfig := &vmess.TLSConfig{ Host: option.SNI, SkipCertVerify: option.SkipCertVerify, + NameCertVerify: option.NameCertVerify, NextProtos: option.ALPN, FingerPrint: option.Fingerprint, Certificate: option.Certificate, diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 21f81963..c08c80f6 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -54,6 +54,7 @@ type TuicOption struct { CWND int `proxy:"cwnd,omitempty"` BBRProfile string `proxy:"bbr-profile,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + NameCertVerify string `proxy:"name-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` Certificate string `proxy:"certificate,omitempty"` PrivateKey string `proxy:"private-key,omitempty"` @@ -138,9 +139,10 @@ func NewTuic(option TuicOption) (*Tuic, error) { InsecureSkipVerify: option.SkipCertVerify, MinVersion: tls.VersionTLS13, }, - Fingerprint: option.Fingerprint, - Certificate: option.Certificate, - PrivateKey: option.PrivateKey, + Fingerprint: option.Fingerprint, + NameCertVerify: option.NameCertVerify, + Certificate: option.Certificate, + PrivateKey: option.PrivateKey, }) if err != nil { return nil, err diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 9fe906fa..4c00297f 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -71,6 +71,7 @@ type VlessOption struct { XHTTPOpts XHTTPOptions `proxy:"xhttp-opts,omitempty"` WSHeaders map[string]string `proxy:"ws-headers,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + NameCertVerify string `proxy:"name-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` Certificate string `proxy:"certificate,omitempty"` PrivateKey string `proxy:"private-key,omitempty"` @@ -129,6 +130,7 @@ type XHTTPDownloadSettings struct { ECHOpts *ECHOptions `proxy:"ech-opts,omitempty"` RealityOpts *RealityOptions `proxy:"reality-opts,omitempty"` SkipCertVerify *bool `proxy:"skip-cert-verify,omitempty"` + NameCertVerify *string `proxy:"name-cert-verify,omitempty"` Fingerprint *string `proxy:"fingerprint,omitempty"` Certificate *string `proxy:"certificate,omitempty"` PrivateKey *string `proxy:"private-key,omitempty"` @@ -166,9 +168,10 @@ func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M InsecureSkipVerify: v.option.SkipCertVerify, NextProtos: []string{"http/1.1"}, }, - Fingerprint: v.option.Fingerprint, - Certificate: v.option.Certificate, - PrivateKey: v.option.PrivateKey, + Fingerprint: v.option.Fingerprint, + NameCertVerify: v.option.NameCertVerify, + Certificate: v.option.Certificate, + PrivateKey: v.option.PrivateKey, }) if err != nil { return nil, err @@ -273,6 +276,7 @@ func (v *Vless) streamTLSConn(ctx context.Context, conn net.Conn, isH2 bool) (ne tlsOpts := vmess.TLSConfig{ Host: host, SkipCertVerify: v.option.SkipCertVerify, + NameCertVerify: v.option.NameCertVerify, FingerPrint: v.option.Fingerprint, Certificate: v.option.Certificate, PrivateKey: v.option.PrivateKey, @@ -510,6 +514,7 @@ func NewVless(option VlessOption) (*Vless, error) { tlsConfig = &vmess.TLSConfig{ Host: option.ServerName, SkipCertVerify: option.SkipCertVerify, + NameCertVerify: option.NameCertVerify, FingerPrint: option.Fingerprint, Certificate: option.Certificate, PrivateKey: option.PrivateKey, @@ -596,6 +601,7 @@ func NewVless(option VlessOption) (*Vless, error) { tlsOpts := &vmess.TLSConfig{ Host: host, SkipCertVerify: v.option.SkipCertVerify, + NameCertVerify: v.option.NameCertVerify, FingerPrint: v.option.Fingerprint, Certificate: v.option.Certificate, PrivateKey: v.option.PrivateKey, @@ -658,6 +664,7 @@ func NewVless(option VlessOption) (*Vless, error) { } } downloadSkipCertVerify := lo.FromPtrOr(ds.SkipCertVerify, v.option.SkipCertVerify) + downloadNameCertVerify := lo.FromPtrOr(ds.NameCertVerify, v.option.NameCertVerify) downloadFingerprint := lo.FromPtrOr(ds.Fingerprint, v.option.Fingerprint) downloadCertificate := lo.FromPtrOr(ds.Certificate, v.option.Certificate) downloadPrivateKey := lo.FromPtrOr(ds.PrivateKey, v.option.PrivateKey) @@ -707,6 +714,7 @@ func NewVless(option VlessOption) (*Vless, error) { tlsOpts := vmess.TLSConfig{ Host: host, SkipCertVerify: downloadSkipCertVerify, + NameCertVerify: downloadNameCertVerify, FingerPrint: downloadFingerprint, Certificate: downloadCertificate, PrivateKey: downloadPrivateKey, @@ -734,6 +742,7 @@ func NewVless(option VlessOption) (*Vless, error) { tlsOpts := &vmess.TLSConfig{ Host: host, SkipCertVerify: downloadSkipCertVerify, + NameCertVerify: downloadNameCertVerify, FingerPrint: downloadFingerprint, Certificate: downloadCertificate, PrivateKey: downloadPrivateKey, diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index ecac585c..4c7197ca 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -57,6 +57,7 @@ type VmessOption struct { TLS bool `proxy:"tls,omitempty"` ALPN []string `proxy:"alpn,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + NameCertVerify string `proxy:"name-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` Certificate string `proxy:"certificate,omitempty"` PrivateKey string `proxy:"private-key,omitempty"` @@ -198,9 +199,10 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M InsecureSkipVerify: v.option.SkipCertVerify, NextProtos: []string{"http/1.1"}, }, - Fingerprint: v.option.Fingerprint, - Certificate: v.option.Certificate, - PrivateKey: v.option.PrivateKey, + Fingerprint: v.option.Fingerprint, + NameCertVerify: v.option.NameCertVerify, + Certificate: v.option.Certificate, + PrivateKey: v.option.PrivateKey, }) if err != nil { return nil, err @@ -321,6 +323,7 @@ func (v *Vmess) streamTLSConn(ctx context.Context, conn net.Conn, isH2 bool) (ne tlsOpts := mihomoVMess.TLSConfig{ Host: host, SkipCertVerify: v.option.SkipCertVerify, + NameCertVerify: v.option.NameCertVerify, FingerPrint: v.option.Fingerprint, Certificate: v.option.Certificate, PrivateKey: v.option.PrivateKey, @@ -538,6 +541,7 @@ func NewVmess(option VmessOption) (*Vmess, error) { tlsConfig = &mihomoVMess.TLSConfig{ Host: option.ServerName, SkipCertVerify: option.SkipCertVerify, + NameCertVerify: option.NameCertVerify, FingerPrint: option.Fingerprint, Certificate: option.Certificate, PrivateKey: option.PrivateKey, diff --git a/adapter/provider/override.go b/adapter/provider/override.go index af931e8c..aa688614 100644 --- a/adapter/provider/override.go +++ b/adapter/provider/override.go @@ -16,6 +16,7 @@ type overrideSchema struct { Down *string `provider:"down,omitempty"` DialerProxy *string `provider:"dialer-proxy,omitempty"` SkipCertVerify *bool `provider:"skip-cert-verify,omitempty"` + NameCertVerify *string `provider:"name-cert-verify,omitempty"` Interface *string `provider:"interface-name,omitempty"` RoutingMark *int `provider:"routing-mark,omitempty"` IPVersion *string `provider:"ip-version,omitempty"` @@ -63,6 +64,9 @@ func (o *overrideSchema) Apply(mapping map[string]any) error { if o.SkipCertVerify != nil { mapping["skip-cert-verify"] = *o.SkipCertVerify } + if o.NameCertVerify != nil { + mapping["name-cert-verify"] = *o.NameCertVerify + } if o.Interface != nil { mapping["interface-name"] = *o.Interface } diff --git a/component/ca/config.go b/component/ca/config.go index ab401cc9..0ee9995f 100644 --- a/component/ca/config.go +++ b/component/ca/config.go @@ -77,11 +77,12 @@ func GetCertPool() *x509.CertPool { } type Option struct { - TLSConfig *tls.Config - Fingerprint string - ZeroTrust bool - Certificate string - PrivateKey string + TLSConfig *tls.Config + Fingerprint string + NameCertVerify string + ZeroTrust bool + Certificate string + PrivateKey string } func GetTLSConfig(opt Option) (tlsConfig *tls.Config, err error) { @@ -106,7 +107,17 @@ func GetTLSConfig(opt Option) (tlsConfig *tls.Config, err error) { // [ConnectionState.ServerName] can return the actual ServerName needed for verification, // avoiding inconsistencies caused by [tlsConfig.ServerName] being modified after the [NewFingerprintVerifier] call. // https://github.com/golang/go/issues/36736#issuecomment-587925536 - return verifier(state.PeerCertificates, state.ServerName) + serverName := state.ServerName + if opt.NameCertVerify != "" { + serverName = opt.NameCertVerify + } + return verifier(state.PeerCertificates, serverName) + } + tlsConfig.InsecureSkipVerify = true + } else if opt.NameCertVerify != "" { + verifier := NewNameCertVerifier(opt.NameCertVerify, tlsConfig.RootCAs, tlsConfig.Time) + tlsConfig.VerifyConnection = func(state tls.ConnectionState) error { + return verifier(state.PeerCertificates) } tlsConfig.InsecureSkipVerify = true } diff --git a/component/ca/name_cert_verify.go b/component/ca/name_cert_verify.go new file mode 100644 index 00000000..99a33225 --- /dev/null +++ b/component/ca/name_cert_verify.go @@ -0,0 +1,31 @@ +package ca + +import ( + "crypto/x509" + "errors" + "time" +) + +// NewNameCertVerifier returns a verifier for a certificate chain and an explicit DNSName. +func NewNameCertVerifier(dnsName string, roots *x509.CertPool, now func() time.Time) func([]*x509.Certificate) error { + return func(certificates []*x509.Certificate) error { + if len(certificates) == 0 { + return errors.New("tls: no peer certificates") + } + + intermediates := x509.NewCertPool() + for _, certificate := range certificates[1:] { + intermediates.AddCert(certificate) + } + verifyOptions := x509.VerifyOptions{ + Roots: roots, + Intermediates: intermediates, + DNSName: dnsName, + } + if now != nil { + verifyOptions.CurrentTime = now() + } + _, err := certificates[0].Verify(verifyOptions) + return err + } +} diff --git a/component/ca/name_cert_verify_test.go b/component/ca/name_cert_verify_test.go new file mode 100644 index 00000000..72879b65 --- /dev/null +++ b/component/ca/name_cert_verify_test.go @@ -0,0 +1,98 @@ +package ca + +import ( + "crypto/x509" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestNameCertVerifier(t *testing.T) { + roots := x509.NewCertPool() + roots.AddCert(rootCert) + untrustedRoots := x509.NewCertPool() + untrustedRoots.AddCert(smimeRootCert) + + tests := []struct { + name string + dnsName string + roots *x509.CertPool + now func() time.Time + certificates []*x509.Certificate + wantErr bool + }{ + { + name: "valid chain", + dnsName: leafServerName, + roots: roots, + now: certTime, + certificates: []*x509.Certificate{leafCert, intermediateCert}, + }, + { + name: "valid chain with root", + dnsName: leafServerName, + roots: roots, + now: certTime, + certificates: []*x509.Certificate{leafCert, intermediateCert, rootCert}, + }, + { + name: "wrong DNS name", + dnsName: wrongLeafServerName, + roots: roots, + now: certTime, + certificates: []*x509.Certificate{leafCert, intermediateCert}, + wantErr: true, + }, + { + name: "no certificates", + dnsName: leafServerName, + roots: roots, + now: certTime, + wantErr: true, + }, + { + name: "missing intermediate", + dnsName: leafServerName, + roots: roots, + now: certTime, + certificates: []*x509.Certificate{leafCert}, + wantErr: true, + }, + { + name: "untrusted root", + dnsName: leafServerName, + roots: untrustedRoots, + now: certTime, + certificates: []*x509.Certificate{leafCert, intermediateCert}, + wantErr: true, + }, + { + name: "expired certificate", + dnsName: leafServerName, + roots: roots, + now: func() time.Time { return leafCert.NotAfter.Add(time.Second) }, + certificates: []*x509.Certificate{leafCert, intermediateCert}, + wantErr: true, + }, + { + name: "invalid certificate signature", + dnsName: leafServerName, + roots: roots, + now: certTime, + certificates: []*x509.Certificate{leafWithInvalidHashCert, intermediateCert}, + wantErr: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := NewNameCertVerifier(test.dnsName, test.roots, test.now)(test.certificates) + if test.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/dns/doh.go b/dns/doh.go index 5e13e044..88c14e27 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -69,6 +69,7 @@ type dnsOverHTTPS struct { dialer *dnsDialer addr string skipCertVerify bool + nameCertVerify string } // type check @@ -100,6 +101,7 @@ func newDoHClient(urlString string, r resolver.Resolver, preferH3 bool, params m if params["skip-cert-verify"] == "true" { doh.skipCertVerify = true } + doh.nameCertVerify = params["name-cert-verify"] runtime.SetFinalizer(doh, (*dnsOverHTTPS).Close) @@ -406,6 +408,7 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp MinVersion: tls.VersionTLS12, SessionTicketsDisabled: false, }, + NameCertVerify: doh.nameCertVerify, }) if err != nil { return nil, err diff --git a/dns/doq.go b/dns/doq.go index ff41c17f..2ae589a3 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -62,6 +62,7 @@ type dnsOverQUIC struct { addr string dialer *dnsDialer skipCertVerify bool + nameCertVerify string } // type check @@ -81,6 +82,7 @@ func newDoQ(addr string, resolver resolver.Resolver, params map[string]string, p if params["skip-cert-verify"] == "true" { doq.skipCertVerify = true } + doq.nameCertVerify = params["name-cert-verify"] runtime.SetFinalizer(doq, (*dnsOverQUIC).Close) return doq @@ -349,6 +351,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (quicConn *quic.Conn }, SessionTicketsDisabled: false, }, + NameCertVerify: doq.nameCertVerify, }) if err != nil { return nil, err diff --git a/dns/dot.go b/dns/dot.go index a386b7cb..78baef19 100644 --- a/dns/dot.go +++ b/dns/dot.go @@ -24,6 +24,7 @@ type dnsOverTLS struct { host string dialer *dnsDialer skipCertVerify bool + nameCertVerify string disableReuse bool access sync.Mutex @@ -128,6 +129,7 @@ func (t *dnsOverTLS) dialContext(ctx context.Context) (net.Conn, error) { ServerName: t.host, InsecureSkipVerify: t.skipCertVerify, }, + NameCertVerify: t.nameCertVerify, }) if err != nil { _ = conn.Close() @@ -171,6 +173,7 @@ func newDoTClient(addr string, resolver resolver.Resolver, params map[string]str if params["skip-cert-verify"] == "true" { c.skipCertVerify = true } + c.nameCertVerify = params["name-cert-verify"] if params["disable-reuse"] == "true" { c.disableReuse = true } diff --git a/docs/config.yaml b/docs/config.yaml index 5786db9e..82ef053c 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -379,6 +379,7 @@ proxies: # socks5 # certificate: ./client.crt # 证书 PEM 格式,或者 证书的路径 # private-key: ./client.key # 证书对应的私钥 PEM 格式,或者私钥路径 # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # udp: true # ip-version: ipv6 @@ -391,6 +392,7 @@ proxies: # socks5 # password: password # tls: true # https # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # sni: custom.com # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取 # 下面两项如果填写则开启 mTLS(需要同时填写) @@ -489,6 +491,7 @@ proxies: # socks5 # config: AEn+DQBFKwAgACABWIHUGj4u+PIggYXcR5JF0gYk3dCRioBW8uJq9H4mKAAIAAEAAQABAANAEnB1YmxpYy50bHMtZWNoLmRldgAA # # query-server-name: xxx.com # 可选项,不为空时用于指定通过dns解析时的域名 # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # host: bing.com # path: "/" # mux: true @@ -526,6 +529,7 @@ proxies: # socks5 # certificate: ./client.crt # 证书 PEM 格式,或者 证书的路径 # private-key: ./client.key # 证书对应的私钥 PEM 格式,或者私钥路径 # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # host: bing.com # path: "/" # mux: true @@ -549,6 +553,7 @@ proxies: # socks5 # certificate: ./client.crt # private-key: ./client.key # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI - name: "ss6-gost-relay" type: ss @@ -677,8 +682,9 @@ proxies: # socks5 # private-key: ./client.key # 证书对应的私钥 PEM 格式,或者私钥路径 # client-fingerprint: chrome # Available: "chrome","firefox","safari","ios","random", currently only support TLS transport in TCP/GRPC/WS/HTTP for VLESS/Vmess and trojan. # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # servername: example.com # priority over wss host - # 如果填写 tlsmirror-opts 则开启 tlsmirror(当 tls: true 时)。tlsmirror 的 TLS 载体会使用同一出站中的 `servername`、`alpn`、`skip-cert-verify`、`fingerprint`、`certificate`、`private-key`、`client-fingerprint` 和 `ech-opts` 配置;`servername` 为空时使用 `server`。 + # 如果填写 tlsmirror-opts 则开启 tlsmirror(当 tls: true 时)。tlsmirror 的 TLS 载体会使用同一出站中的 `servername`、`alpn`、`skip-cert-verify`、`name-cert-verify`、`fingerprint`、`certificate`、`private-key`、`client-fingerprint` 和 `ech-opts` 配置;`servername` 为空时使用 `server`。 # tlsmirror-opts: # primary-key: MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY= # 必填,32 字节主密钥的 base64 编码 # explicit-nonce-ciphersuites: [156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 49195, 49196, 49197, 49198, 49199, 49200, 49201, 49202, 49290, 49291, 49293, 49316, 49317, 49318, 49319, 49320, 49321, 49322, 49323, 49324, 49325, 49326, 49327, 52392, 52393, 52394, 52395, 52396, 52397, 52398] # TLS 1.2 载体使用显式 nonce 的加密套件 @@ -816,6 +822,7 @@ proxies: # socks5 # private-key: ./client.key # 证书对应的私钥 PEM 格式,或者私钥路径 servername: example.com # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI grpc-opts: grpc-service-name: "example" # grpc-user-agent: "grpc-go/1.36.0" @@ -834,6 +841,7 @@ proxies: # socks5 network: tcp servername: example.com # AKA SNI # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # 下面两项如果填写则开启 mTLS(需要同时填写) # certificate: ./client.crt # 证书 PEM 格式,或者 证书的路径 # private-key: ./client.key # 证书对应的私钥 PEM 格式,或者私钥路径 @@ -860,6 +868,7 @@ proxies: # socks5 # private-key: ./client.key # 证书对应的私钥 PEM 格式,或者私钥路径 # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取 # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI - name: "vless-encryption" type: vless @@ -908,6 +917,7 @@ proxies: # socks5 udp: true flow: # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI client-fingerprint: chrome servername: testingcf.jsdelivr.net grpc-opts: @@ -934,6 +944,7 @@ proxies: # socks5 # client-fingerprint: random # Available: "chrome","firefox","safari","random","none" servername: example.com # priority over wss host # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取 # 下面两项如果填写则开启 mTLS(需要同时填写) # certificate: ./client.crt # 证书 PEM 格式,或者 证书的路径 @@ -957,6 +968,7 @@ proxies: # socks5 # ech-opts: ... # reality-opts: ... # skip-cert-verify: false + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # fingerprint: ... # certificate: ... # private-key: ... @@ -1014,6 +1026,7 @@ proxies: # socks5 # ech-opts: ... # reality-opts: ... # skip-cert-verify: false + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # fingerprint: ... # certificate: ... # private-key: ... @@ -1038,6 +1051,7 @@ proxies: # socks5 # - h2 # - http/1.1 # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # ss-opts: # like trojan-go's `shadowsocks` config # enabled: false # method: aes-128-gcm # aes-128-gcm/aes-256-gcm/chacha20-ietf-poly1305 @@ -1056,6 +1070,7 @@ proxies: # socks5 network: grpc sni: example.com # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取 # 下面两项如果填写则开启 mTLS(需要同时填写) # certificate: ./client.crt # 证书 PEM 格式,或者 证书的路径 @@ -1077,6 +1092,7 @@ proxies: # socks5 network: ws sni: example.com # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取 # 下面两项如果填写则开启 mTLS(需要同时填写) # certificate: ./client.crt # 证书 PEM 格式,或者 证书的路径 @@ -1099,6 +1115,7 @@ proxies: # socks5 # udp: true # sni: example.com # aka server name # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取 # 下面两项如果填写则开启 mTLS(需要同时填写) # certificate: ./client.crt # 证书 PEM 格式,或者 证书的路径 @@ -1124,6 +1141,7 @@ proxies: # socks5 # config: AEn+DQBFKwAgACABWIHUGj4u+PIggYXcR5JF0gYk3dCRioBW8uJq9H4mKAAIAAEAAQABAANAEnB1YmxpYy50bHMtZWNoLmRldgAA # # query-server-name: xxx.com # 可选项,不为空时用于指定通过dns解析时的域名 # skip-cert-verify: false + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # recv-window-conn: 12582912 # recv-window: 52428800 # disable-mtu-discovery: false @@ -1156,6 +1174,7 @@ proxies: # socks5 # config: AEn+DQBFKwAgACABWIHUGj4u+PIggYXcR5JF0gYk3dCRioBW8uJq9H4mKAAIAAEAAQABAANAEnB1YmxpYy50bHMtZWNoLmRldgAA # # query-server-name: xxx.com # 可选项,不为空时用于指定通过dns解析时的域名 # skip-cert-verify: false + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取 # 下面两项如果填写则开启 mTLS(需要同时填写) # certificate: ./client.crt # 证书 PEM 格式,或者 证书的路径 @@ -1171,8 +1190,9 @@ proxies: # socks5 # - stun.nextcloud.com:3478 # - stun.sip.us:3478 # - global.stun.twilio.com:3478 - # # 下面支持填写针对server-url的TLS配置(sni, skip-cert-verify, fingerprint, certificate, private-key, alpn) + # # 下面支持填写针对server-url的TLS配置(sni, skip-cert-verify, name-cert-verify, fingerprint, certificate, private-key, alpn) # # skip-cert-verify: false + # # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # # ...... ###quic-go特殊配置项,不要随意修改除非你知道你在干什么### # initial-stream-receive-window: 8388608 @@ -1384,6 +1404,7 @@ proxies: # socks5 # max-udp-relay-packet-size: 1500 # fast-open: true # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # max-open-streams: 20 # default 100, too many open streams may hurt performance # sni: example.com # ech-opts: @@ -1482,6 +1503,7 @@ proxies: # socks5 # - h2 # - http/1.1 # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # trusttunnel - name: trusttunnel @@ -1497,6 +1519,7 @@ proxies: # socks5 # alpn: # - h2 # skip-cert-verify: true + # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI ### quic options # quic: true # 默认为false # congestion-controller: bbr @@ -1619,6 +1642,7 @@ proxy-providers: # expected-status: 204 # 当健康检查返回状态码与期望值不符时,认为节点不可用 override: # 覆写节点加载时的一些配置项 skip-cert-verify: true + name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI udp: true # down: "50 Mbps" # up: "10 Mbps" @@ -2323,8 +2347,9 @@ listeners: # - stun.sip.us:3478 # - global.stun.twilio.com:3478 # # proxy: DIRECT # 设置server-url通过哪个代理进行连接 - # # 下面支持填写针对server-url的TLS配置(sni, skip-cert-verify, fingerprint, certificate, private-key, alpn) + # # 下面支持填写针对server-url的TLS配置(sni, skip-cert-verify, name-cert-verify, fingerprint, certificate, private-key, alpn) # # skip-cert-verify: false + # # name-cert-verify: example.com # 仅修改证书 DNSName 校验目标,不修改 SNI # # ...... # 注意,这是用于自建hysteria2入站和出站中realm-opts中server-url的HTTP/HTTPS服务器,请勿混淆 diff --git a/listener/config/hysteria2.go b/listener/config/hysteria2.go index 1d59b3bc..16a3ce8b 100644 --- a/listener/config/hysteria2.go +++ b/listener/config/hysteria2.go @@ -49,6 +49,7 @@ type Hysteria2RealmOption struct { // for ServerURL SNI string `yaml:"sni" json:"sni,omitempty"` SkipCertVerify bool `yaml:"skip-cert-verify" json:"skip-cert-verify,omitempty"` + NameCertVerify string `yaml:"name-cert-verify" json:"name-cert-verify,omitempty"` Fingerprint string `yaml:"fingerprint" json:"fingerprint,omitempty"` Certificate string `yaml:"certificate" json:"certificate,omitempty"` PrivateKey string `yaml:"private-key" json:"private-key,omitempty"` diff --git a/listener/inbound/hysteria2.go b/listener/inbound/hysteria2.go index 939a45ea..dcf315e1 100644 --- a/listener/inbound/hysteria2.go +++ b/listener/inbound/hysteria2.go @@ -51,6 +51,7 @@ type Hysteria2RealmOption struct { // for ServerURL SNI string `inbound:"sni,omitempty"` SkipCertVerify bool `inbound:"skip-cert-verify,omitempty"` + NameCertVerify string `inbound:"name-cert-verify,omitempty"` Fingerprint string `inbound:"fingerprint,omitempty"` Certificate string `inbound:"certificate,omitempty"` PrivateKey string `inbound:"private-key,omitempty"` @@ -67,6 +68,7 @@ func (o Hysteria2RealmOption) Build() LC.Hysteria2RealmOption { STUNServers: o.STUNServers, SNI: o.SNI, SkipCertVerify: o.SkipCertVerify, + NameCertVerify: o.NameCertVerify, Fingerprint: o.Fingerprint, Certificate: o.Certificate, PrivateKey: o.PrivateKey, diff --git a/listener/sing_hysteria2/server.go b/listener/sing_hysteria2/server.go index fb257ac3..08f9cbc1 100644 --- a/listener/sing_hysteria2/server.go +++ b/listener/sing_hysteria2/server.go @@ -163,9 +163,10 @@ func New(config LC.Hysteria2Server, lc C.InboundListenConfig, tunnel C.Tunnel, a InsecureSkipVerify: config.RealmOpts.SkipCertVerify, NextProtos: config.RealmOpts.ALPN, }, - Fingerprint: config.RealmOpts.Fingerprint, - Certificate: config.RealmOpts.Certificate, - PrivateKey: config.RealmOpts.PrivateKey, + Fingerprint: config.RealmOpts.Fingerprint, + NameCertVerify: config.RealmOpts.NameCertVerify, + Certificate: config.RealmOpts.Certificate, + PrivateKey: config.RealmOpts.PrivateKey, }) if err != nil { return nil, err diff --git a/transport/gost/relay.go b/transport/gost/relay.go index b0bda84c..9d59bfc1 100644 --- a/transport/gost/relay.go +++ b/transport/gost/relay.go @@ -58,6 +58,7 @@ type RelayOption struct { Username string `proxy:"username,omitempty"` Password string `proxy:"password,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + NameCertVerify string `proxy:"name-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` Certificate string `proxy:"certificate,omitempty"` PrivateKey string `proxy:"private-key,omitempty"` @@ -175,6 +176,7 @@ func (d *relayDialer) dialRelayServer(ctx context.Context, fallbackAddress strin tlsConn, err := mihomoVMess.StreamTLSConn(ctx, conn, &mihomoVMess.TLSConfig{ Host: d.serverName(relayAddress), SkipCertVerify: d.option.SkipCertVerify, + NameCertVerify: d.option.NameCertVerify, FingerPrint: d.option.Fingerprint, Certificate: d.option.Certificate, PrivateKey: d.option.PrivateKey, diff --git a/transport/gost/websocket.go b/transport/gost/websocket.go index c668c189..d4e03a34 100644 --- a/transport/gost/websocket.go +++ b/transport/gost/websocket.go @@ -22,6 +22,7 @@ type Option struct { TLS bool ECHConfig *ech.Config SkipCertVerify bool + NameCertVerify string Fingerprint string Certificate string PrivateKey string @@ -69,9 +70,10 @@ func NewGostWebsocket(ctx context.Context, conn net.Conn, option *Option) (net.C InsecureSkipVerify: option.SkipCertVerify, NextProtos: []string{"http/1.1"}, }, - Fingerprint: option.Fingerprint, - Certificate: option.Certificate, - PrivateKey: option.PrivateKey, + Fingerprint: option.Fingerprint, + NameCertVerify: option.NameCertVerify, + Certificate: option.Certificate, + PrivateKey: option.PrivateKey, }) if err != nil { return nil, err diff --git a/transport/restls/restls.go b/transport/restls/restls.go index 8575828d..d680dc7f 100644 --- a/transport/restls/restls.go +++ b/transport/restls/restls.go @@ -38,18 +38,30 @@ type ServerConfig = tls.RestlsServerConfig var Server = tls.RestlsServer -func SetFingerprint(config *Config, fingerprint string) (err error) { +func SetFingerprint(config *Config, fingerprint string, nameCertVerify string) (err error) { verifier, err := ca.NewFingerprintVerifier(fingerprint, ntp.Now) if err != nil { return err } config.InsecureSkipVerify = true config.VerifyConnection = func(state tls.ConnectionState) error { - return verifier(state.PeerCertificates, state.ServerName) + serverName := state.ServerName + if nameCertVerify != "" { + serverName = nameCertVerify + } + return verifier(state.PeerCertificates, serverName) } return nil } +func SetNameCertVerify(config *Config, dnsName string) { + verifier := ca.NewNameCertVerifier(dnsName, config.RootCAs, config.Time) + config.InsecureSkipVerify = true + config.VerifyConnection = func(state tls.ConnectionState) error { + return verifier(state.PeerCertificates) + } +} + // NewRestls return a Restls Connection func NewRestls(ctx context.Context, conn net.Conn, config *Config) (net.Conn, error) { clientHellowID := tls.HelloChrome_Auto diff --git a/transport/sing-shadowtls/shadowtls.go b/transport/sing-shadowtls/shadowtls.go index aee3e679..ef43e2f6 100644 --- a/transport/sing-shadowtls/shadowtls.go +++ b/transport/sing-shadowtls/shadowtls.go @@ -30,6 +30,7 @@ type ShadowTLSOption struct { PrivateKey string ClientFingerprint string SkipCertVerify bool + NameCertVerify string Version int ALPN []string } @@ -42,9 +43,10 @@ func NewShadowTLS(ctx context.Context, conn net.Conn, option *ShadowTLSOption) ( InsecureSkipVerify: option.SkipCertVerify, ServerName: option.Host, }, - Fingerprint: option.Fingerprint, - Certificate: option.Certificate, - PrivateKey: option.PrivateKey, + Fingerprint: option.Fingerprint, + NameCertVerify: option.NameCertVerify, + Certificate: option.Certificate, + PrivateKey: option.PrivateKey, }) if err != nil { return nil, err diff --git a/transport/tlsmirror/client.go b/transport/tlsmirror/client.go index eeef79be..03f722ea 100644 --- a/transport/tlsmirror/client.go +++ b/transport/tlsmirror/client.go @@ -62,9 +62,10 @@ func Dial(ctx context.Context, rawConn net.Conn, cfg ClientConfig) (*Conn, error InsecureSkipVerify: cfg.SkipCertVerify, NextProtos: cfg.ALPN, }, - Fingerprint: cfg.Fingerprint, - Certificate: cfg.Certificate, - PrivateKey: cfg.PrivateKey, + Fingerprint: cfg.Fingerprint, + NameCertVerify: cfg.NameCertVerify, + Certificate: cfg.Certificate, + PrivateKey: cfg.PrivateKey, }) if err != nil { _ = hidden.Close() diff --git a/transport/tlsmirror/config.go b/transport/tlsmirror/config.go index fe452287..19e67106 100644 --- a/transport/tlsmirror/config.go +++ b/transport/tlsmirror/config.go @@ -19,6 +19,7 @@ type ClientConfig struct { ServerName string SkipCertVerify bool + NameCertVerify string ALPN []string Fingerprint string Certificate string diff --git a/transport/v2ray-plugin/websocket.go b/transport/v2ray-plugin/websocket.go index 67b7c3ea..be1f222a 100644 --- a/transport/v2ray-plugin/websocket.go +++ b/transport/v2ray-plugin/websocket.go @@ -21,6 +21,7 @@ type Option struct { TLS bool ECHConfig *ech.Config SkipCertVerify bool + NameCertVerify string Fingerprint string Certificate string PrivateKey string @@ -55,9 +56,10 @@ func NewV2rayObfs(ctx context.Context, conn net.Conn, option *Option) (net.Conn, InsecureSkipVerify: option.SkipCertVerify, NextProtos: []string{"http/1.1"}, }, - Fingerprint: option.Fingerprint, - Certificate: option.Certificate, - PrivateKey: option.PrivateKey, + Fingerprint: option.Fingerprint, + NameCertVerify: option.NameCertVerify, + Certificate: option.Certificate, + PrivateKey: option.PrivateKey, }) if err != nil { return nil, err diff --git a/transport/vmess/tls.go b/transport/vmess/tls.go index 79924004..02241d2a 100644 --- a/transport/vmess/tls.go +++ b/transport/vmess/tls.go @@ -16,6 +16,7 @@ import ( type TLSConfig struct { Host string SkipCertVerify bool + NameCertVerify string FingerPrint string Certificate string PrivateKey string @@ -34,9 +35,10 @@ func (cfg *TLSConfig) ToStdConfig() (*tls.Config, error) { InsecureSkipVerify: cfg.SkipCertVerify, NextProtos: cfg.NextProtos, }, - Fingerprint: cfg.FingerPrint, - Certificate: cfg.Certificate, - PrivateKey: cfg.PrivateKey, + Fingerprint: cfg.FingerPrint, + NameCertVerify: cfg.NameCertVerify, + Certificate: cfg.Certificate, + PrivateKey: cfg.PrivateKey, }) } @@ -46,6 +48,7 @@ func StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn Config: *cfg.TLSMirror, ServerName: cfg.Host, SkipCertVerify: cfg.SkipCertVerify, + NameCertVerify: cfg.NameCertVerify, ALPN: cfg.NextProtos, Fingerprint: cfg.FingerPrint, Certificate: cfg.Certificate,