mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2026-10-11 17:58:13 +08:00
feat: support restls for anytls outbound and listener
This commit is contained in:
1 parent
769ea6f5b4
commit
0e7c3c79e3
6 files changed
+82
-5
No files matched your search
@@ -33,6 +33,7 @@ type AnyTLSOption struct {
|
||||
SNI string `proxy:"sni,omitempty"`
|
||||
ECHOpts ECHOptions `proxy:"ech-opts,omitempty"`
|
||||
ShadowTLSOpts ShadowTLSOptions `proxy:"shadow-tls-opts,omitempty"`
|
||||
RestlsOpts RestlsOptions `proxy:"restls-opts,omitempty"`
|
||||
JLSOpts JLSOptions `proxy:"jls-opts,omitempty"`
|
||||
ClientFingerprint string `proxy:"client-fingerprint,omitempty"`
|
||||
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
|
||||
@@ -123,12 +124,24 @@ func NewAnyTLS(option AnyTLSOption) (*AnyTLS, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
restlsConfig, err := option.RestlsOpts.Parse(option.SNI, option.ClientFingerprint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jlsConfig, err := option.JLSOpts.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if shadowTLSConfig != nil && jlsConfig != nil {
|
||||
return nil, errors.New("ShadowTLS is incompatible with JLS")
|
||||
if shadowTLSConfig != nil {
|
||||
if restlsConfig != nil {
|
||||
return nil, errors.New("ShadowTLS is incompatible with Restls")
|
||||
}
|
||||
if jlsConfig != nil {
|
||||
return nil, errors.New("ShadowTLS is incompatible with JLS")
|
||||
}
|
||||
}
|
||||
if restlsConfig != nil && jlsConfig != nil {
|
||||
return nil, errors.New("Restls is incompatible with JLS")
|
||||
}
|
||||
tlsConfig := &vmess.TLSConfig{
|
||||
Host: option.SNI,
|
||||
@@ -141,6 +154,7 @@ func NewAnyTLS(option AnyTLSOption) (*AnyTLS, error) {
|
||||
ClientFingerprint: option.ClientFingerprint,
|
||||
ECH: echConfig,
|
||||
ShadowTLS: shadowTLSConfig,
|
||||
Restls: restlsConfig,
|
||||
JLS: jlsConfig,
|
||||
}
|
||||
if tlsConfig.Host == "" {
|
||||
|
||||
+13
-2
@@ -1579,6 +1579,10 @@ proxies: # socks5
|
||||
# shadow-tls-opts: # 使用 sni 作为 ShadowTLS SNI
|
||||
# version: 3 # 支持 v1/v2/v3;留空时默认为 v2
|
||||
# password: shadow-tls-password
|
||||
# restls-opts: # 使用 sni 作为 Restls SNI
|
||||
# password: restls-password
|
||||
# version-hint: tls13 # 可选值:tls12、tls13
|
||||
# # restls-script: ""
|
||||
# jls-opts: # 使用 sni 作为 JLS SNI
|
||||
# username: jls-user
|
||||
# password: jls-password
|
||||
@@ -2368,7 +2372,7 @@ listeners:
|
||||
users:
|
||||
username1: password1
|
||||
username2: password2
|
||||
# "shadow-tls" 和 "jls-config" 均未启用且 "allow-insecure" 不为 true 时,必须填写 "certificate" 和 "private-key";启用 ShadowTLS 或 JLS 时不要填写
|
||||
# "shadow-tls"、"res-tls" 和 "jls-config" 均未启用且 "allow-insecure" 不为 true 时,必须填写 "certificate" 和 "private-key";启用 ShadowTLS、ResTLS 或 JLS 时不要填写
|
||||
certificate: ./server.crt # 证书 PEM 格式,或者 证书的路径
|
||||
private-key: ./server.key
|
||||
# 下面两项为mTLS配置项,如果client-auth-type设置为 "verify-if-given" 或 "require-and-verify" 则client-auth-cert必须不为空
|
||||
@@ -2391,6 +2395,13 @@ listeners:
|
||||
# handshake:
|
||||
# dest: www.example.com:443
|
||||
# # proxy: ""
|
||||
# res-tls:
|
||||
# enable: true
|
||||
# dest: www.example.com:443
|
||||
# password: restls-password
|
||||
# # restls-script: ""
|
||||
# # min-record-len: 0
|
||||
# # proxy: ""
|
||||
# jls-config: # JLS 替代普通 TLS;未认证连接回落到 dest
|
||||
# enable: true
|
||||
# users:
|
||||
@@ -2401,7 +2412,7 @@ listeners:
|
||||
# # alpn: [h2, http/1.1]
|
||||
# # proxy: ""
|
||||
# # rate-limit: 0 # fallback 转发限速,单位 bit/s;0 表示不限速
|
||||
### 注意,anytls listener, 如果 "allow-insecure" 不为 true, 至少需要填写 “certificate和private-key” 或 “shadow-tls” 或 “jls-config” 的其中一项 ###
|
||||
### 注意,anytls listener, 如果 "allow-insecure" 不为 true, 至少需要填写 “certificate和private-key” 或 “shadow-tls” 或 “res-tls” 或 “jls-config” 的其中一项 ###
|
||||
# allow-insecure: false # 是否允许不开启tls加密(注意:仅用于有 nginx, caddy 前置的情况)
|
||||
# padding-scheme: "" # https://github.com/anytls/anytls-go/blob/main/docs/protocol.md#cmdupdatepaddingscheme
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/jls"
|
||||
"github.com/metacubex/mihomo/listener/restls"
|
||||
"github.com/metacubex/mihomo/listener/shadowtls"
|
||||
"github.com/metacubex/mihomo/listener/sing"
|
||||
"github.com/metacubex/mihomo/ntp"
|
||||
@@ -46,6 +47,7 @@ func New(config LC.AnyTLSServer, lc C.InboundListenConfig, tunnel C.Tunnel, addi
|
||||
}
|
||||
|
||||
var shadowTLSBuilder *shadowtls.Builder
|
||||
var restlsBuilder *restls.Builder
|
||||
var jlsBuilder *jls.Builder
|
||||
tlsConfig := &tls.Config{Time: ntp.Now}
|
||||
if config.Certificate != "" && config.PrivateKey != "" {
|
||||
@@ -89,6 +91,18 @@ func New(config LC.AnyTLSServer, lc C.InboundListenConfig, tunnel C.Tunnel, addi
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if config.ResTLS.Enable {
|
||||
if tlsConfig.GetCertificate != nil {
|
||||
return nil, errors.New("certificate is unavailable in Restls")
|
||||
}
|
||||
if tlsConfig.ClientAuth != tls.NoClientCert {
|
||||
return nil, errors.New("client-auth is unavailable in Restls")
|
||||
}
|
||||
if shadowTLSBuilder != nil {
|
||||
return nil, errors.New("ShadowTLS is unavailable in Restls")
|
||||
}
|
||||
restlsBuilder = restls.New(config.ResTLS, tunnel)
|
||||
}
|
||||
if config.JLSConfig.Enable {
|
||||
if tlsConfig.GetCertificate != nil {
|
||||
return nil, errors.New("certificate is unavailable in JLS")
|
||||
@@ -99,6 +113,9 @@ func New(config LC.AnyTLSServer, lc C.InboundListenConfig, tunnel C.Tunnel, addi
|
||||
if shadowTLSBuilder != nil {
|
||||
return nil, errors.New("ShadowTLS is unavailable in JLS")
|
||||
}
|
||||
if restlsBuilder != nil {
|
||||
return nil, errors.New("Restls is unavailable in JLS")
|
||||
}
|
||||
jlsBuilder, err = jls.New(config.JLSConfig, tunnel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -143,12 +160,14 @@ func New(config LC.AnyTLSServer, lc C.InboundListenConfig, tunnel C.Tunnel, addi
|
||||
}
|
||||
if shadowTLSBuilder != nil {
|
||||
l = shadowTLSBuilder.NewListener(l)
|
||||
} else if restlsBuilder != nil {
|
||||
l = restlsBuilder.NewListener(l)
|
||||
} else if jlsBuilder != nil {
|
||||
l = jlsBuilder.NewListener(l)
|
||||
} else if tlsConfig.GetCertificate != nil {
|
||||
l = tls.NewListener(l, tlsConfig)
|
||||
} else if !config.AllowInsecure {
|
||||
return nil, errors.New("disallow using AnyTLS without certificates/shadow-tls/jls/allow-insecure config")
|
||||
return nil, errors.New("disallow using AnyTLS without certificates/shadow-tls/res-tls/jls/allow-insecure config")
|
||||
}
|
||||
sl.listeners = append(sl.listeners, l)
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ type AnyTLSServer struct {
|
||||
ClientAuthCert string `yaml:"client-auth-cert" json:"client-auth-cert,omitempty"`
|
||||
EchKey string `yaml:"ech-key" json:"ech-key,omitempty"`
|
||||
ShadowTLS ShadowTLS `yaml:"shadow-tls" json:"shadow-tls,omitempty"`
|
||||
ResTLS ResTLS `yaml:"res-tls" json:"res-tls,omitempty"`
|
||||
JLSConfig JLSConfig `yaml:"jls-config" json:"jls-config,omitempty"`
|
||||
AllowInsecure bool `yaml:"allow-insecure" json:"allow-insecure,omitempty"`
|
||||
PaddingScheme string `yaml:"padding-scheme" json:"padding-scheme,omitempty"`
|
||||
|
||||
@@ -18,6 +18,7 @@ type AnyTLSOption struct {
|
||||
ClientAuthCert string `inbound:"client-auth-cert,omitempty"`
|
||||
EchKey string `inbound:"ech-key,omitempty"`
|
||||
ShadowTLS ShadowTLS `inbound:"shadow-tls,omitempty"`
|
||||
ResTLS ResTLS `inbound:"res-tls,omitempty"`
|
||||
JLSConfig JLSConfig `inbound:"jls-config,omitempty"`
|
||||
AllowInsecure bool `inbound:"allow-insecure,omitempty"`
|
||||
PaddingScheme string `inbound:"padding-scheme,omitempty"`
|
||||
@@ -52,6 +53,7 @@ func NewAnyTLS(options *AnyTLSOption) (*AnyTLS, error) {
|
||||
ClientAuthCert: options.ClientAuthCert,
|
||||
EchKey: options.EchKey,
|
||||
ShadowTLS: options.ShadowTLS.Build(),
|
||||
ResTLS: options.ResTLS.Build(),
|
||||
JLSConfig: options.JLSConfig.Build(),
|
||||
AllowInsecure: options.AllowInsecure,
|
||||
PaddingScheme: options.PaddingScheme,
|
||||
|
||||
@@ -127,6 +127,36 @@ func TestInboundAnyTLS_ShadowTLS(t *testing.T) {
|
||||
testInboundAnyTLSShadowTLS(t, inboundOptions, outboundOptions)
|
||||
}
|
||||
|
||||
func testInboundAnyTLSRestls(t *testing.T, inboundOptions inbound.AnyTLSOption, outboundOptions outbound.AnyTLSOption) {
|
||||
t.Parallel()
|
||||
t.Run("Conn", func(t *testing.T) {
|
||||
inboundOptions, outboundOptions := inboundOptions, outboundOptions // don't modify outside options value
|
||||
testInboundAnyTLS(t, inboundOptions, outboundOptions)
|
||||
})
|
||||
t.Run("UConn", func(t *testing.T) {
|
||||
inboundOptions, outboundOptions := inboundOptions, outboundOptions // don't modify outside options value
|
||||
outboundOptions.ClientFingerprint = "chrome"
|
||||
testInboundAnyTLS(t, inboundOptions, outboundOptions)
|
||||
})
|
||||
}
|
||||
|
||||
func TestInboundAnyTLS_Restls(t *testing.T) {
|
||||
const password = "restls-password"
|
||||
inboundOptions := inbound.AnyTLSOption{
|
||||
ResTLS: inbound.ResTLS{
|
||||
Enable: true,
|
||||
Dest: net.JoinHostPort(realityDest, "443"),
|
||||
Password: password,
|
||||
},
|
||||
}
|
||||
outboundOptions := outbound.AnyTLSOption{
|
||||
SNI: realityDest,
|
||||
Fingerprint: tlsFingerprint,
|
||||
RestlsOpts: outbound.RestlsOptions{Password: password, VersionHint: "tls13"},
|
||||
}
|
||||
testInboundAnyTLSRestls(t, inboundOptions, outboundOptions)
|
||||
}
|
||||
|
||||
func testInboundAnyTLSJLS(t *testing.T, inboundOptions inbound.AnyTLSOption, outboundOptions outbound.AnyTLSOption) {
|
||||
t.Parallel()
|
||||
t.Run("Conn", func(t *testing.T) {
|
||||
|
||||
Reference in new issue
Block a user