diff --git a/docs/config.yaml b/docs/config.yaml index 9641fafa..88030a1b 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -1104,6 +1104,9 @@ listeners: # users: # 如果不填写users项,则遵从全局authentication设置,如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: [] # - username: aaa # password: aaa + # 下面两项如果填写则开启 tls(需要同时填写) + # certificate: ./server.crt + # private-key: ./server.key - name: http-in-1 type: http @@ -1114,6 +1117,9 @@ listeners: # users: # 如果不填写users项,则遵从全局authentication设置,如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: [] # - username: aaa # password: aaa + # 下面两项如果填写则开启 tls(需要同时填写) + # certificate: ./server.crt + # private-key: ./server.key - name: mixed-in-1 type: mixed # HTTP(S) 和 SOCKS 代理混合 @@ -1125,6 +1131,9 @@ listeners: # users: # 如果不填写users项,则遵从全局authentication设置,如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: [] # - username: aaa # password: aaa + # 下面两项如果填写则开启 tls(需要同时填写) + # certificate: ./server.crt + # private-key: ./server.key - name: reidr-in-1 type: redir diff --git a/listener/config/auth.go b/listener/config/auth.go new file mode 100644 index 00000000..a99f87fb --- /dev/null +++ b/listener/config/auth.go @@ -0,0 +1,16 @@ +package config + +import ( + "github.com/metacubex/mihomo/component/auth" + "github.com/metacubex/mihomo/listener/reality" +) + +// AuthServer for http/socks/mixed server +type AuthServer struct { + Enable bool + Listen string + AuthStore auth.AuthStore + Certificate string + PrivateKey string + RealityConfig reality.Config +} diff --git a/listener/http/server.go b/listener/http/server.go index 24f07e8b..e32b55dd 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -1,12 +1,16 @@ package http import ( + "crypto/tls" + "errors" "net" "github.com/metacubex/mihomo/adapter/inbound" - "github.com/metacubex/mihomo/component/auth" + N "github.com/metacubex/mihomo/common/net" C "github.com/metacubex/mihomo/constant" authStore "github.com/metacubex/mihomo/listener/auth" + LC "github.com/metacubex/mihomo/listener/config" + "github.com/metacubex/mihomo/listener/reality" ) type Listener struct { @@ -32,7 +36,7 @@ func (l *Listener) Close() error { } func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { - return NewWithAuthenticator(addr, tunnel, authStore.Default, additions...) + return NewWithConfig(LC.AuthServer{Enable: true, Listen: addr, AuthStore: authStore.Default}, tunnel, additions...) } // NewWithAuthenticate @@ -40,12 +44,12 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additions ...inbound.Addition) (*Listener, error) { store := authStore.Default if !authenticate { - store = authStore.Default + store = authStore.Nil } - return NewWithAuthenticator(addr, tunnel, store, additions...) + return NewWithConfig(LC.AuthServer{Enable: true, Listen: addr, AuthStore: store}, tunnel, additions...) } -func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) (*Listener, error) { +func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { isDefault := false if len(additions) == 0 { isDefault = true @@ -55,15 +59,42 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad } } - l, err := inbound.Listen("tcp", addr) + l, err := inbound.Listen("tcp", config.Listen) if err != nil { return nil, err } + tlsConfig := &tls.Config{} + var realityBuilder *reality.Builder + + if config.Certificate != "" && config.PrivateKey != "" { + cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path) + if err != nil { + return nil, err + } + tlsConfig.Certificates = []tls.Certificate{cert} + } + if config.RealityConfig.PrivateKey != "" { + if tlsConfig.Certificates != nil { + return nil, errors.New("certificate is unavailable in reality") + } + realityBuilder, err = config.RealityConfig.Build() + if err != nil { + return nil, err + } + } + + if realityBuilder != nil { + l = realityBuilder.NewListener(l) + } else if len(tlsConfig.Certificates) > 0 { + l = tls.NewListener(l, tlsConfig) + } + hl := &Listener{ listener: l, - addr: addr, + addr: config.Listen, } + go func() { for { conn, err := hl.listener.Accept() @@ -74,7 +105,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad continue } - store := store + store := config.AuthStore if isDefault || store == authStore.Default { // only apply on default listener if !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) { _ = conn.Close() diff --git a/listener/inbound/http.go b/listener/inbound/http.go index 018c8a7e..8a4df008 100644 --- a/listener/inbound/http.go +++ b/listener/inbound/http.go @@ -6,13 +6,17 @@ import ( "strings" C "github.com/metacubex/mihomo/constant" + LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/http" "github.com/metacubex/mihomo/log" ) type HTTPOption struct { BaseOption - Users AuthUsers `inbound:"users,omitempty"` + Users AuthUsers `inbound:"users,omitempty"` + Certificate string `inbound:"certificate,omitempty"` + PrivateKey string `inbound:"private-key,omitempty"` + RealityConfig RealityConfig `inbound:"reality-config,omitempty"` } func (o HTTPOption) Equal(config C.InboundConfig) bool { @@ -53,7 +57,18 @@ func (h *HTTP) Address() string { // Listen implements constant.InboundListener func (h *HTTP) Listen(tunnel C.Tunnel) error { for _, addr := range strings.Split(h.RawAddress(), ",") { - l, err := http.NewWithAuthenticator(addr, tunnel, h.config.Users.GetAuthStore(), h.Additions()...) + l, err := http.NewWithConfig( + LC.AuthServer{ + Enable: true, + Listen: addr, + AuthStore: h.config.Users.GetAuthStore(), + Certificate: h.config.Certificate, + PrivateKey: h.config.PrivateKey, + RealityConfig: h.config.RealityConfig.Build(), + }, + tunnel, + h.Additions()..., + ) if err != nil { return err } diff --git a/listener/inbound/mixed.go b/listener/inbound/mixed.go index 0d285ce7..20c61f2e 100644 --- a/listener/inbound/mixed.go +++ b/listener/inbound/mixed.go @@ -6,16 +6,19 @@ import ( "strings" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/log" - + LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/mixed" "github.com/metacubex/mihomo/listener/socks" + "github.com/metacubex/mihomo/log" ) type MixedOption struct { BaseOption - Users AuthUsers `inbound:"users,omitempty"` - UDP bool `inbound:"udp,omitempty"` + Users AuthUsers `inbound:"users,omitempty"` + UDP bool `inbound:"udp,omitempty"` + Certificate string `inbound:"certificate,omitempty"` + PrivateKey string `inbound:"private-key,omitempty"` + RealityConfig RealityConfig `inbound:"reality-config,omitempty"` } func (o MixedOption) Equal(config C.InboundConfig) bool { @@ -59,7 +62,18 @@ func (m *Mixed) Address() string { // Listen implements constant.InboundListener func (m *Mixed) Listen(tunnel C.Tunnel) error { for _, addr := range strings.Split(m.RawAddress(), ",") { - l, err := mixed.NewWithAuthenticator(addr, tunnel, m.config.Users.GetAuthStore(), m.Additions()...) + l, err := mixed.NewWithConfig( + LC.AuthServer{ + Enable: true, + Listen: addr, + AuthStore: m.config.Users.GetAuthStore(), + Certificate: m.config.Certificate, + PrivateKey: m.config.PrivateKey, + RealityConfig: m.config.RealityConfig.Build(), + }, + tunnel, + m.Additions()..., + ) if err != nil { return err } diff --git a/listener/inbound/socks.go b/listener/inbound/socks.go index fc7735f2..6cb9782c 100644 --- a/listener/inbound/socks.go +++ b/listener/inbound/socks.go @@ -6,14 +6,18 @@ import ( "strings" C "github.com/metacubex/mihomo/constant" + LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/socks" "github.com/metacubex/mihomo/log" ) type SocksOption struct { BaseOption - Users AuthUsers `inbound:"users,omitempty"` - UDP bool `inbound:"udp,omitempty"` + Users AuthUsers `inbound:"users,omitempty"` + UDP bool `inbound:"udp,omitempty"` + Certificate string `inbound:"certificate,omitempty"` + PrivateKey string `inbound:"private-key,omitempty"` + RealityConfig RealityConfig `inbound:"reality-config,omitempty"` } func (o SocksOption) Equal(config C.InboundConfig) bool { @@ -78,7 +82,18 @@ func (s *Socks) Address() string { // Listen implements constant.InboundListener func (s *Socks) Listen(tunnel C.Tunnel) error { for _, addr := range strings.Split(s.RawAddress(), ",") { - stl, err := socks.NewWithAuthenticator(addr, tunnel, s.config.Users.GetAuthStore(), s.Additions()...) + stl, err := socks.NewWithConfig( + LC.AuthServer{ + Enable: true, + Listen: addr, + AuthStore: s.config.Users.GetAuthStore(), + Certificate: s.config.Certificate, + PrivateKey: s.config.PrivateKey, + RealityConfig: s.config.RealityConfig.Build(), + }, + tunnel, + s.Additions()..., + ) if err != nil { return err } diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index 5ac63011..6785b7fe 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -1,6 +1,8 @@ package mixed import ( + "crypto/tls" + "errors" "net" "github.com/metacubex/mihomo/adapter/inbound" @@ -8,7 +10,9 @@ import ( "github.com/metacubex/mihomo/component/auth" C "github.com/metacubex/mihomo/constant" authStore "github.com/metacubex/mihomo/listener/auth" + LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/http" + "github.com/metacubex/mihomo/listener/reality" "github.com/metacubex/mihomo/listener/socks" "github.com/metacubex/mihomo/transport/socks4" "github.com/metacubex/mihomo/transport/socks5" @@ -37,10 +41,10 @@ func (l *Listener) Close() error { } func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { - return NewWithAuthenticator(addr, tunnel, authStore.Default, additions...) + return NewWithConfig(LC.AuthServer{Enable: true, Listen: addr, AuthStore: authStore.Default}, tunnel, additions...) } -func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) (*Listener, error) { +func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { isDefault := false if len(additions) == 0 { isDefault = true @@ -50,14 +54,40 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad } } - l, err := inbound.Listen("tcp", addr) + l, err := inbound.Listen("tcp", config.Listen) if err != nil { return nil, err } + tlsConfig := &tls.Config{} + var realityBuilder *reality.Builder + + if config.Certificate != "" && config.PrivateKey != "" { + cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path) + if err != nil { + return nil, err + } + tlsConfig.Certificates = []tls.Certificate{cert} + } + if config.RealityConfig.PrivateKey != "" { + if tlsConfig.Certificates != nil { + return nil, errors.New("certificate is unavailable in reality") + } + realityBuilder, err = config.RealityConfig.Build() + if err != nil { + return nil, err + } + } + + if realityBuilder != nil { + l = realityBuilder.NewListener(l) + } else if len(tlsConfig.Certificates) > 0 { + l = tls.NewListener(l, tlsConfig) + } + ml := &Listener{ listener: l, - addr: addr, + addr: config.Listen, } go func() { for { @@ -68,7 +98,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad } continue } - store := store + store := config.AuthStore if isDefault || store == authStore.Default { // only apply on default listener if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { _ = c.Close() diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index cc66613e..55153607 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -1,6 +1,8 @@ package socks import ( + "crypto/tls" + "errors" "io" "net" @@ -9,6 +11,8 @@ import ( "github.com/metacubex/mihomo/component/auth" C "github.com/metacubex/mihomo/constant" authStore "github.com/metacubex/mihomo/listener/auth" + LC "github.com/metacubex/mihomo/listener/config" + "github.com/metacubex/mihomo/listener/reality" "github.com/metacubex/mihomo/transport/socks4" "github.com/metacubex/mihomo/transport/socks5" ) @@ -36,10 +40,10 @@ func (l *Listener) Close() error { } func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { - return NewWithAuthenticator(addr, tunnel, authStore.Default, additions...) + return NewWithConfig(LC.AuthServer{Enable: true, Listen: addr, AuthStore: authStore.Default}, tunnel, additions...) } -func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) (*Listener, error) { +func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { isDefault := false if len(additions) == 0 { isDefault = true @@ -49,14 +53,40 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad } } - l, err := inbound.Listen("tcp", addr) + l, err := inbound.Listen("tcp", config.Listen) if err != nil { return nil, err } + tlsConfig := &tls.Config{} + var realityBuilder *reality.Builder + + if config.Certificate != "" && config.PrivateKey != "" { + cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path) + if err != nil { + return nil, err + } + tlsConfig.Certificates = []tls.Certificate{cert} + } + if config.RealityConfig.PrivateKey != "" { + if tlsConfig.Certificates != nil { + return nil, errors.New("certificate is unavailable in reality") + } + realityBuilder, err = config.RealityConfig.Build() + if err != nil { + return nil, err + } + } + + if realityBuilder != nil { + l = realityBuilder.NewListener(l) + } else if len(tlsConfig.Certificates) > 0 { + l = tls.NewListener(l, tlsConfig) + } + sl := &Listener{ listener: l, - addr: addr, + addr: config.Listen, } go func() { for { @@ -67,7 +97,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad } continue } - store := store + store := config.AuthStore if isDefault || store == authStore.Default { // only apply on default listener if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { _ = c.Close()