Skip to content

Commit 136d114

Browse files
committed
feat: socks5/http/mixed inbound support setting tls in listeners
1 parent 938ab7f commit 136d114

File tree

8 files changed

+188
-28
lines changed

8 files changed

+188
-28
lines changed

docs/config.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,9 @@ listeners:
11041104
# users: # 如果不填写users项,则遵从全局authentication设置,如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: []
11051105
# - username: aaa
11061106
# password: aaa
1107+
# 下面两项如果填写则开启 tls(需要同时填写)
1108+
# certificate: ./server.crt
1109+
# private-key: ./server.key
11071110

11081111
- name: http-in-1
11091112
type: http
@@ -1114,6 +1117,9 @@ listeners:
11141117
# users: # 如果不填写users项,则遵从全局authentication设置,如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: []
11151118
# - username: aaa
11161119
# password: aaa
1120+
# 下面两项如果填写则开启 tls(需要同时填写)
1121+
# certificate: ./server.crt
1122+
# private-key: ./server.key
11171123

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

11291138
- name: reidr-in-1
11301139
type: redir

listener/config/auth.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package config
2+
3+
import (
4+
"github.com/metacubex/mihomo/component/auth"
5+
"github.com/metacubex/mihomo/listener/reality"
6+
)
7+
8+
// AuthServer for http/socks/mixed server
9+
type AuthServer struct {
10+
Enable bool
11+
Listen string
12+
AuthStore auth.AuthStore
13+
Certificate string
14+
PrivateKey string
15+
RealityConfig reality.Config
16+
}

listener/http/server.go

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package http
22

33
import (
4+
"crypto/tls"
5+
"errors"
46
"net"
57

68
"github.com/metacubex/mihomo/adapter/inbound"
7-
"github.com/metacubex/mihomo/component/auth"
9+
N "github.com/metacubex/mihomo/common/net"
810
C "github.com/metacubex/mihomo/constant"
911
authStore "github.com/metacubex/mihomo/listener/auth"
12+
LC "github.com/metacubex/mihomo/listener/config"
13+
"github.com/metacubex/mihomo/listener/reality"
1014
)
1115

1216
type Listener struct {
@@ -32,20 +36,20 @@ func (l *Listener) Close() error {
3236
}
3337

3438
func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
35-
return NewWithAuthenticator(addr, tunnel, authStore.Default, additions...)
39+
return NewWithConfig(LC.AuthServer{Enable: true, Listen: addr, AuthStore: authStore.Default}, tunnel, additions...)
3640
}
3741

3842
// NewWithAuthenticate
3943
// never change type traits because it's used in CMFA
4044
func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additions ...inbound.Addition) (*Listener, error) {
4145
store := authStore.Default
4246
if !authenticate {
43-
store = authStore.Default
47+
store = authStore.Nil
4448
}
45-
return NewWithAuthenticator(addr, tunnel, store, additions...)
49+
return NewWithConfig(LC.AuthServer{Enable: true, Listen: addr, AuthStore: store}, tunnel, additions...)
4650
}
4751

48-
func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) (*Listener, error) {
52+
func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
4953
isDefault := false
5054
if len(additions) == 0 {
5155
isDefault = true
@@ -55,15 +59,42 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad
5559
}
5660
}
5761

58-
l, err := inbound.Listen("tcp", addr)
62+
l, err := inbound.Listen("tcp", config.Listen)
5963
if err != nil {
6064
return nil, err
6165
}
6266

67+
tlsConfig := &tls.Config{}
68+
var realityBuilder *reality.Builder
69+
70+
if config.Certificate != "" && config.PrivateKey != "" {
71+
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
72+
if err != nil {
73+
return nil, err
74+
}
75+
tlsConfig.Certificates = []tls.Certificate{cert}
76+
}
77+
if config.RealityConfig.PrivateKey != "" {
78+
if tlsConfig.Certificates != nil {
79+
return nil, errors.New("certificate is unavailable in reality")
80+
}
81+
realityBuilder, err = config.RealityConfig.Build()
82+
if err != nil {
83+
return nil, err
84+
}
85+
}
86+
87+
if realityBuilder != nil {
88+
l = realityBuilder.NewListener(l)
89+
} else if len(tlsConfig.Certificates) > 0 {
90+
l = tls.NewListener(l, tlsConfig)
91+
}
92+
6393
hl := &Listener{
6494
listener: l,
65-
addr: addr,
95+
addr: config.Listen,
6696
}
97+
6798
go func() {
6899
for {
69100
conn, err := hl.listener.Accept()
@@ -74,7 +105,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad
74105
continue
75106
}
76107

77-
store := store
108+
store := config.AuthStore
78109
if isDefault || store == authStore.Default { // only apply on default listener
79110
if !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) {
80111
_ = conn.Close()

listener/inbound/http.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@ import (
66
"strings"
77

88
C "github.com/metacubex/mihomo/constant"
9+
LC "github.com/metacubex/mihomo/listener/config"
910
"github.com/metacubex/mihomo/listener/http"
1011
"github.com/metacubex/mihomo/log"
1112
)
1213

1314
type HTTPOption struct {
1415
BaseOption
15-
Users AuthUsers `inbound:"users,omitempty"`
16+
Users AuthUsers `inbound:"users,omitempty"`
17+
Certificate string `inbound:"certificate,omitempty"`
18+
PrivateKey string `inbound:"private-key,omitempty"`
19+
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
1620
}
1721

1822
func (o HTTPOption) Equal(config C.InboundConfig) bool {
@@ -53,7 +57,18 @@ func (h *HTTP) Address() string {
5357
// Listen implements constant.InboundListener
5458
func (h *HTTP) Listen(tunnel C.Tunnel) error {
5559
for _, addr := range strings.Split(h.RawAddress(), ",") {
56-
l, err := http.NewWithAuthenticator(addr, tunnel, h.config.Users.GetAuthStore(), h.Additions()...)
60+
l, err := http.NewWithConfig(
61+
LC.AuthServer{
62+
Enable: true,
63+
Listen: addr,
64+
AuthStore: h.config.Users.GetAuthStore(),
65+
Certificate: h.config.Certificate,
66+
PrivateKey: h.config.PrivateKey,
67+
RealityConfig: h.config.RealityConfig.Build(),
68+
},
69+
tunnel,
70+
h.Additions()...,
71+
)
5772
if err != nil {
5873
return err
5974
}

listener/inbound/mixed.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,19 @@ import (
66
"strings"
77

88
C "github.com/metacubex/mihomo/constant"
9-
"github.com/metacubex/mihomo/log"
10-
9+
LC "github.com/metacubex/mihomo/listener/config"
1110
"github.com/metacubex/mihomo/listener/mixed"
1211
"github.com/metacubex/mihomo/listener/socks"
12+
"github.com/metacubex/mihomo/log"
1313
)
1414

1515
type MixedOption struct {
1616
BaseOption
17-
Users AuthUsers `inbound:"users,omitempty"`
18-
UDP bool `inbound:"udp,omitempty"`
17+
Users AuthUsers `inbound:"users,omitempty"`
18+
UDP bool `inbound:"udp,omitempty"`
19+
Certificate string `inbound:"certificate,omitempty"`
20+
PrivateKey string `inbound:"private-key,omitempty"`
21+
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
1922
}
2023

2124
func (o MixedOption) Equal(config C.InboundConfig) bool {
@@ -59,7 +62,18 @@ func (m *Mixed) Address() string {
5962
// Listen implements constant.InboundListener
6063
func (m *Mixed) Listen(tunnel C.Tunnel) error {
6164
for _, addr := range strings.Split(m.RawAddress(), ",") {
62-
l, err := mixed.NewWithAuthenticator(addr, tunnel, m.config.Users.GetAuthStore(), m.Additions()...)
65+
l, err := mixed.NewWithConfig(
66+
LC.AuthServer{
67+
Enable: true,
68+
Listen: addr,
69+
AuthStore: m.config.Users.GetAuthStore(),
70+
Certificate: m.config.Certificate,
71+
PrivateKey: m.config.PrivateKey,
72+
RealityConfig: m.config.RealityConfig.Build(),
73+
},
74+
tunnel,
75+
m.Additions()...,
76+
)
6377
if err != nil {
6478
return err
6579
}

listener/inbound/socks.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@ import (
66
"strings"
77

88
C "github.com/metacubex/mihomo/constant"
9+
LC "github.com/metacubex/mihomo/listener/config"
910
"github.com/metacubex/mihomo/listener/socks"
1011
"github.com/metacubex/mihomo/log"
1112
)
1213

1314
type SocksOption struct {
1415
BaseOption
15-
Users AuthUsers `inbound:"users,omitempty"`
16-
UDP bool `inbound:"udp,omitempty"`
16+
Users AuthUsers `inbound:"users,omitempty"`
17+
UDP bool `inbound:"udp,omitempty"`
18+
Certificate string `inbound:"certificate,omitempty"`
19+
PrivateKey string `inbound:"private-key,omitempty"`
20+
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
1721
}
1822

1923
func (o SocksOption) Equal(config C.InboundConfig) bool {
@@ -78,7 +82,18 @@ func (s *Socks) Address() string {
7882
// Listen implements constant.InboundListener
7983
func (s *Socks) Listen(tunnel C.Tunnel) error {
8084
for _, addr := range strings.Split(s.RawAddress(), ",") {
81-
stl, err := socks.NewWithAuthenticator(addr, tunnel, s.config.Users.GetAuthStore(), s.Additions()...)
85+
stl, err := socks.NewWithConfig(
86+
LC.AuthServer{
87+
Enable: true,
88+
Listen: addr,
89+
AuthStore: s.config.Users.GetAuthStore(),
90+
Certificate: s.config.Certificate,
91+
PrivateKey: s.config.PrivateKey,
92+
RealityConfig: s.config.RealityConfig.Build(),
93+
},
94+
tunnel,
95+
s.Additions()...,
96+
)
8297
if err != nil {
8398
return err
8499
}

listener/mixed/mixed.go

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
package mixed
22

33
import (
4+
"crypto/tls"
5+
"errors"
46
"net"
57

68
"github.com/metacubex/mihomo/adapter/inbound"
79
N "github.com/metacubex/mihomo/common/net"
810
"github.com/metacubex/mihomo/component/auth"
911
C "github.com/metacubex/mihomo/constant"
1012
authStore "github.com/metacubex/mihomo/listener/auth"
13+
LC "github.com/metacubex/mihomo/listener/config"
1114
"github.com/metacubex/mihomo/listener/http"
15+
"github.com/metacubex/mihomo/listener/reality"
1216
"github.com/metacubex/mihomo/listener/socks"
1317
"github.com/metacubex/mihomo/transport/socks4"
1418
"github.com/metacubex/mihomo/transport/socks5"
@@ -37,10 +41,10 @@ func (l *Listener) Close() error {
3741
}
3842

3943
func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
40-
return NewWithAuthenticator(addr, tunnel, authStore.Default, additions...)
44+
return NewWithConfig(LC.AuthServer{Enable: true, Listen: addr, AuthStore: authStore.Default}, tunnel, additions...)
4145
}
4246

43-
func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) (*Listener, error) {
47+
func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
4448
isDefault := false
4549
if len(additions) == 0 {
4650
isDefault = true
@@ -50,14 +54,40 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad
5054
}
5155
}
5256

53-
l, err := inbound.Listen("tcp", addr)
57+
l, err := inbound.Listen("tcp", config.Listen)
5458
if err != nil {
5559
return nil, err
5660
}
5761

62+
tlsConfig := &tls.Config{}
63+
var realityBuilder *reality.Builder
64+
65+
if config.Certificate != "" && config.PrivateKey != "" {
66+
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
67+
if err != nil {
68+
return nil, err
69+
}
70+
tlsConfig.Certificates = []tls.Certificate{cert}
71+
}
72+
if config.RealityConfig.PrivateKey != "" {
73+
if tlsConfig.Certificates != nil {
74+
return nil, errors.New("certificate is unavailable in reality")
75+
}
76+
realityBuilder, err = config.RealityConfig.Build()
77+
if err != nil {
78+
return nil, err
79+
}
80+
}
81+
82+
if realityBuilder != nil {
83+
l = realityBuilder.NewListener(l)
84+
} else if len(tlsConfig.Certificates) > 0 {
85+
l = tls.NewListener(l, tlsConfig)
86+
}
87+
5888
ml := &Listener{
5989
listener: l,
60-
addr: addr,
90+
addr: config.Listen,
6191
}
6292
go func() {
6393
for {
@@ -68,7 +98,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad
6898
}
6999
continue
70100
}
71-
store := store
101+
store := config.AuthStore
72102
if isDefault || store == authStore.Default { // only apply on default listener
73103
if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) {
74104
_ = c.Close()

0 commit comments

Comments
 (0)