diff --git a/README.md b/README.md index 4d05af57..9fd33484 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,13 @@ Documentations are now moved to [GitHub Wiki](https://github.com/Dreamacro/clash/wiki). ## Advanced usage for this branch +### General configuration +```yaml +sniffing: true # Sniff TLS SNI + +force-cert-verify: true # force verify TLS Certificate, prevent machine-in-the-middle attacks +``` + ### MITM configuration A root CA certificate is required, the MITM proxy server will generate a CA certificate file and a CA private key file in your Clash home directory, you can use your own certificate replace it. @@ -247,12 +254,6 @@ proxies: # skip-cert-verify: true ``` -### Sniffing configuration -Sniff TLS SNI. -```yaml -sniffing: true -``` - ### IPTABLES configuration Work on Linux OS who's supported `iptables` diff --git a/adapter/parser.go b/adapter/parser.go index 13701bc3..444edd1a 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -8,7 +8,7 @@ import ( C "github.com/Dreamacro/clash/constant" ) -func ParseProxy(mapping map[string]any) (C.Proxy, error) { +func ParseProxy(mapping map[string]any, forceCertVerify bool) (C.Proxy, error) { decoder := structure.NewDecoder(structure.Option{TagName: "proxy", WeaklyTypedInput: true}) proxyType, existType := mapping["type"].(string) if !existType { @@ -40,6 +40,9 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { if err != nil { break } + if forceCertVerify { + socksOption.SkipCertVerify = false + } proxy = outbound.NewSocks5(*socksOption) case "http": httpOption := &outbound.HttpOption{} @@ -47,6 +50,9 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { if err != nil { break } + if forceCertVerify { + httpOption.SkipCertVerify = false + } proxy = outbound.NewHttp(*httpOption) case "vmess": vmessOption := &outbound.VmessOption{ @@ -59,6 +65,9 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { if err != nil { break } + if forceCertVerify { + vmessOption.SkipCertVerify = false + } proxy, err = outbound.NewVmess(*vmessOption) case "vless": vlessOption := &outbound.VlessOption{} @@ -66,6 +75,9 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { if err != nil { break } + if forceCertVerify { + vlessOption.SkipCertVerify = false + } proxy, err = outbound.NewVless(*vlessOption) case "snell": snellOption := &outbound.SnellOption{} @@ -80,6 +92,9 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { if err != nil { break } + if forceCertVerify { + trojanOption.SkipCertVerify = false + } proxy, err = outbound.NewTrojan(*trojanOption) default: return nil, fmt.Errorf("unsupport proxy type: %s", proxyType) diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index 887ac5cd..3833dc7e 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -20,15 +20,16 @@ type healthCheckSchema struct { } type proxyProviderSchema struct { - Type string `provider:"type"` - Path string `provider:"path"` - URL string `provider:"url,omitempty"` - Interval int `provider:"interval,omitempty"` - Filter string `provider:"filter,omitempty"` - HealthCheck healthCheckSchema `provider:"health-check,omitempty"` + Type string `provider:"type"` + Path string `provider:"path"` + URL string `provider:"url,omitempty"` + Interval int `provider:"interval,omitempty"` + Filter string `provider:"filter,omitempty"` + HealthCheck healthCheckSchema `provider:"health-check,omitempty"` + ForceCertVerify bool `provider:"force-cert-verify,omitempty"` } -func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvider, error) { +func ParseProxyProvider(name string, mapping map[string]any, forceCertVerify bool) (types.ProxyProvider, error) { decoder := structure.NewDecoder(structure.Option{TagName: "provider", WeaklyTypedInput: true}) schema := &proxyProviderSchema{ @@ -36,6 +37,11 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide Lazy: true, }, } + + if forceCertVerify { + schema.ForceCertVerify = true + } + if err := decoder.Decode(mapping, schema); err != nil { return nil, err } @@ -60,5 +66,5 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide interval := time.Duration(uint(schema.Interval)) * time.Second filter := schema.Filter - return NewProxySetProvider(name, interval, filter, vehicle, hc) + return NewProxySetProvider(name, interval, filter, vehicle, hc, schema.ForceCertVerify) } diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index ffe00447..8c3c272c 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -96,7 +96,7 @@ func stopProxyProvider(pd *ProxySetProvider) { _ = pd.fetcher.Destroy() } -func NewProxySetProvider(name string, interval time.Duration, filter string, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) { +func NewProxySetProvider(name string, interval time.Duration, filter string, vehicle types.Vehicle, hc *HealthCheck, forceCertVerify bool) (*ProxySetProvider, error) { filterReg, err := regexp.Compile(filter) if err != nil { return nil, fmt.Errorf("invalid filter regex: %w", err) @@ -111,7 +111,7 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, veh healthCheck: hc, } - fetcher := newFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, filterReg), proxiesOnUpdate(pd)) + fetcher := newFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, filterReg, forceCertVerify), proxiesOnUpdate(pd)) pd.fetcher = fetcher wrapper := &ProxySetProvider{pd} @@ -202,7 +202,7 @@ func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) { } } -func proxiesParseAndFilter(filter string, filterReg *regexp.Regexp) parser[[]C.Proxy] { +func proxiesParseAndFilter(filter string, filterReg *regexp.Regexp, forceCertVerify bool) parser[[]C.Proxy] { return func(buf []byte) ([]C.Proxy, error) { schema := &ProxySchema{} @@ -219,7 +219,7 @@ func proxiesParseAndFilter(filter string, filterReg *regexp.Regexp) parser[[]C.P if name, ok := mapping["name"]; ok && len(filter) > 0 && !filterReg.MatchString(name.(string)) { continue } - proxy, err := adapter.ParseProxy(mapping) + proxy, err := adapter.ParseProxy(mapping, forceCertVerify) if err != nil { return nil, fmt.Errorf("proxy %d error: %w", idx, err) } diff --git a/config/config.go b/config/config.go index d808d97f..906cf215 100644 --- a/config/config.go +++ b/config/config.go @@ -184,6 +184,7 @@ type RawConfig struct { Interface string `yaml:"interface-name"` RoutingMark int `yaml:"routing-mark"` Sniffing bool `yaml:"sniffing"` + ForceCertVerify bool `yaml:"force-cert-verify"` ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` Hosts map[string]string `yaml:"hosts"` @@ -211,16 +212,17 @@ func Parse(buf []byte) (*Config, error) { func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { // config with default value rawCfg := &RawConfig{ - AllowLan: false, - Sniffing: false, - BindAddress: "*", - Mode: T.Rule, - Authentication: []string{}, - LogLevel: log.INFO, - Hosts: map[string]string{}, - Rule: []string{}, - Proxy: []map[string]any{}, - ProxyGroup: []map[string]any{}, + AllowLan: false, + Sniffing: false, + ForceCertVerify: false, + BindAddress: "*", + Mode: T.Rule, + Authentication: []string{}, + LogLevel: log.INFO, + Hosts: map[string]string{}, + Rule: []string{}, + Proxy: []map[string]any{}, + ProxyGroup: []map[string]any{}, Tun: Tun{ Enable: false, Device: "", @@ -388,6 +390,7 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ proxiesConfig := cfg.Proxy groupsConfig := cfg.ProxyGroup providersConfig := cfg.ProxyProvider + forceCertVerify := cfg.ForceCertVerify var proxyList []string @@ -397,7 +400,7 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ // parse proxy for idx, mapping := range proxiesConfig { - proxy, err := adapter.ParseProxy(mapping) + proxy, err := adapter.ParseProxy(mapping, forceCertVerify) if err != nil { return nil, nil, fmt.Errorf("proxy %d: %w", idx, err) } @@ -429,7 +432,7 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ return nil, nil, fmt.Errorf("can not defined a provider called `%s`", provider.ReservedName) } - pd, err := provider.ParseProxyProvider(name, mapping) + pd, err := provider.ParseProxyProvider(name, mapping, forceCertVerify) if err != nil { return nil, nil, fmt.Errorf("parse proxy provider %s error: %w", name, err) }