feat: socks5/http/mixed inbound support setting tls in listeners

This commit is contained in:
wwqgtxx 2025-02-28 13:13:53 +08:00
parent 938ab7f44d
commit 136d114196
8 changed files with 188 additions and 28 deletions

View File

@ -1104,6 +1104,9 @@ listeners:
# users: # 如果不填写users项则遵从全局authentication设置如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: [] # users: # 如果不填写users项则遵从全局authentication设置如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: []
# - username: aaa # - username: aaa
# password: aaa # password: aaa
# 下面两项如果填写则开启 tls需要同时填写
# certificate: ./server.crt
# private-key: ./server.key
- name: http-in-1 - name: http-in-1
type: http type: http
@ -1114,6 +1117,9 @@ listeners:
# users: # 如果不填写users项则遵从全局authentication设置如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: [] # users: # 如果不填写users项则遵从全局authentication设置如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: []
# - username: aaa # - username: aaa
# password: aaa # password: aaa
# 下面两项如果填写则开启 tls需要同时填写
# certificate: ./server.crt
# private-key: ./server.key
- name: mixed-in-1 - name: mixed-in-1
type: mixed # HTTP(S) 和 SOCKS 代理混合 type: mixed # HTTP(S) 和 SOCKS 代理混合
@ -1125,6 +1131,9 @@ listeners:
# users: # 如果不填写users项则遵从全局authentication设置如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: [] # users: # 如果不填写users项则遵从全局authentication设置如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: []
# - username: aaa # - username: aaa
# password: aaa # password: aaa
# 下面两项如果填写则开启 tls需要同时填写
# certificate: ./server.crt
# private-key: ./server.key
- name: reidr-in-1 - name: reidr-in-1
type: redir type: redir

16
listener/config/auth.go Normal file
View File

@ -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
}

View File

@ -1,12 +1,16 @@
package http package http
import ( import (
"crypto/tls"
"errors"
"net" "net"
"github.com/metacubex/mihomo/adapter/inbound" "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" C "github.com/metacubex/mihomo/constant"
authStore "github.com/metacubex/mihomo/listener/auth" authStore "github.com/metacubex/mihomo/listener/auth"
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/listener/reality"
) )
type Listener struct { type Listener struct {
@ -32,7 +36,7 @@ func (l *Listener) Close() error {
} }
func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, 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 // 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) { func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additions ...inbound.Addition) (*Listener, error) {
store := authStore.Default store := authStore.Default
if !authenticate { 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 isDefault := false
if len(additions) == 0 { if len(additions) == 0 {
isDefault = true 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 { if err != nil {
return nil, err 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{ hl := &Listener{
listener: l, listener: l,
addr: addr, addr: config.Listen,
} }
go func() { go func() {
for { for {
conn, err := hl.listener.Accept() conn, err := hl.listener.Accept()
@ -74,7 +105,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad
continue continue
} }
store := store store := config.AuthStore
if isDefault || store == authStore.Default { // only apply on default listener if isDefault || store == authStore.Default { // only apply on default listener
if !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) { if !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) {
_ = conn.Close() _ = conn.Close()

View File

@ -6,13 +6,17 @@ import (
"strings" "strings"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/listener/http" "github.com/metacubex/mihomo/listener/http"
"github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/log"
) )
type HTTPOption struct { type HTTPOption struct {
BaseOption 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 { func (o HTTPOption) Equal(config C.InboundConfig) bool {
@ -53,7 +57,18 @@ func (h *HTTP) Address() string {
// Listen implements constant.InboundListener // Listen implements constant.InboundListener
func (h *HTTP) Listen(tunnel C.Tunnel) error { func (h *HTTP) Listen(tunnel C.Tunnel) error {
for _, addr := range strings.Split(h.RawAddress(), ",") { 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 { if err != nil {
return err return err
} }

View File

@ -6,16 +6,19 @@ import (
"strings" "strings"
C "github.com/metacubex/mihomo/constant" 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/mixed"
"github.com/metacubex/mihomo/listener/socks" "github.com/metacubex/mihomo/listener/socks"
"github.com/metacubex/mihomo/log"
) )
type MixedOption struct { type MixedOption struct {
BaseOption BaseOption
Users AuthUsers `inbound:"users,omitempty"` Users AuthUsers `inbound:"users,omitempty"`
UDP bool `inbound:"udp,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 { func (o MixedOption) Equal(config C.InboundConfig) bool {
@ -59,7 +62,18 @@ func (m *Mixed) Address() string {
// Listen implements constant.InboundListener // Listen implements constant.InboundListener
func (m *Mixed) Listen(tunnel C.Tunnel) error { func (m *Mixed) Listen(tunnel C.Tunnel) error {
for _, addr := range strings.Split(m.RawAddress(), ",") { 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 { if err != nil {
return err return err
} }

View File

@ -6,14 +6,18 @@ import (
"strings" "strings"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/listener/socks" "github.com/metacubex/mihomo/listener/socks"
"github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/log"
) )
type SocksOption struct { type SocksOption struct {
BaseOption BaseOption
Users AuthUsers `inbound:"users,omitempty"` Users AuthUsers `inbound:"users,omitempty"`
UDP bool `inbound:"udp,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 { func (o SocksOption) Equal(config C.InboundConfig) bool {
@ -78,7 +82,18 @@ func (s *Socks) Address() string {
// Listen implements constant.InboundListener // Listen implements constant.InboundListener
func (s *Socks) Listen(tunnel C.Tunnel) error { func (s *Socks) Listen(tunnel C.Tunnel) error {
for _, addr := range strings.Split(s.RawAddress(), ",") { 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 { if err != nil {
return err return err
} }

View File

@ -1,6 +1,8 @@
package mixed package mixed
import ( import (
"crypto/tls"
"errors"
"net" "net"
"github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/adapter/inbound"
@ -8,7 +10,9 @@ import (
"github.com/metacubex/mihomo/component/auth" "github.com/metacubex/mihomo/component/auth"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
authStore "github.com/metacubex/mihomo/listener/auth" 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/http"
"github.com/metacubex/mihomo/listener/reality"
"github.com/metacubex/mihomo/listener/socks" "github.com/metacubex/mihomo/listener/socks"
"github.com/metacubex/mihomo/transport/socks4" "github.com/metacubex/mihomo/transport/socks4"
"github.com/metacubex/mihomo/transport/socks5" "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) { 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 isDefault := false
if len(additions) == 0 { if len(additions) == 0 {
isDefault = true 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 { if err != nil {
return nil, err 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{ ml := &Listener{
listener: l, listener: l,
addr: addr, addr: config.Listen,
} }
go func() { go func() {
for { for {
@ -68,7 +98,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad
} }
continue continue
} }
store := store store := config.AuthStore
if isDefault || store == authStore.Default { // only apply on default listener if isDefault || store == authStore.Default { // only apply on default listener
if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) {
_ = c.Close() _ = c.Close()

View File

@ -1,6 +1,8 @@
package socks package socks
import ( import (
"crypto/tls"
"errors"
"io" "io"
"net" "net"
@ -9,6 +11,8 @@ import (
"github.com/metacubex/mihomo/component/auth" "github.com/metacubex/mihomo/component/auth"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
authStore "github.com/metacubex/mihomo/listener/auth" 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/socks4"
"github.com/metacubex/mihomo/transport/socks5" "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) { 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 isDefault := false
if len(additions) == 0 { if len(additions) == 0 {
isDefault = true 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 { if err != nil {
return nil, err 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{ sl := &Listener{
listener: l, listener: l,
addr: addr, addr: config.Listen,
} }
go func() { go func() {
for { for {
@ -67,7 +97,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad
} }
continue continue
} }
store := store store := config.AuthStore
if isDefault || store == authStore.Default { // only apply on default listener if isDefault || store == authStore.Default { // only apply on default listener
if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) {
_ = c.Close() _ = c.Close()