feat: add tls support for clash external controller api && v2ray gRPC api

This commit is contained in:
PuerNya 2024-06-17 16:50:52 +08:00
parent 3ae595dc12
commit b25107e6aa
11 changed files with 114 additions and 10 deletions

2
box.go
View File

@ -176,7 +176,7 @@ func New(options Options) (*Box, error) {
preServices2["clash api"] = clashServer
}
if needV2RayAPI {
v2rayServer, err := experimental.NewV2RayServer(logFactory.NewLogger("v2ray-api"), common.PtrValueOrDefault(experimentalOptions.V2RayAPI))
v2rayServer, err := experimental.NewV2RayServer(ctx, logFactory.NewLogger("v2ray-api"), common.PtrValueOrDefault(experimentalOptions.V2RayAPI))
if err != nil {
return nil, E.Cause(err, "create v2ray api server")
}

58
common/tls/listener.go Normal file
View File

@ -0,0 +1,58 @@
package tls
import (
"context"
"net"
"github.com/sagernet/sing/common/tls"
)
var _ net.Listener = (*Listener)(nil)
type Listener struct {
ctx context.Context
l net.Listener
tls tls.ServerConfig
}
func (t *Listener) Addr() net.Addr {
return t.l.Addr()
}
func (t *Listener) Accept() (net.Conn, error) {
if t.tls == nil {
return t.l.Accept()
}
for {
conn, err := t.l.Accept()
if err != nil {
return nil, err
}
conn, err = tls.ServerHandshake(t.ctx, conn, t.tls)
if err == nil {
return conn, nil
}
}
}
func (t *Listener) Close() error {
if err := t.l.Close(); err != nil {
return err
}
if t.tls != nil {
return t.tls.Close()
}
return nil
}
func NewListener(ctx context.Context, address string, config ServerConfig) (net.Listener, error) {
l, err := net.Listen("tcp", address)
if err != nil {
return nil, err
}
return &Listener{
ctx: ctx,
l: l,
tls: config,
}, nil
}

View File

@ -16,6 +16,7 @@
"external_ui_download_detour": "",
"secret": "",
"default_mode": "",
"tls": {},
// Deprecated

View File

@ -16,6 +16,7 @@
"external_ui_download_detour": "",
"secret": "",
"default_mode": "",
"tls": {},
// Deprecated

View File

@ -19,7 +19,8 @@
"users": [
"sekai"
]
}
},
"tls": {}
}
```

View File

@ -19,7 +19,8 @@
"users": [
"sekai"
]
}
},
"tls": {}
}
```

View File

@ -11,6 +11,7 @@ import (
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/tls"
"github.com/sagernet/sing-box/common/urltest"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental"
@ -47,6 +48,7 @@ type Server struct {
mode string
modeList []string
modeUpdateHook chan<- struct{}
tlsConfig tls.ServerConfig
externalController bool
externalUI string
@ -124,6 +126,13 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
})
})
}
if options.TLS != nil {
tlsConfig, err := tls.NewServer(ctx, server.logger, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}
server.tlsConfig = tlsConfig
}
return server, nil
}
@ -141,9 +150,15 @@ func (s *Server) PreStart() error {
}
func (s *Server) Start() error {
if s.tlsConfig != nil {
err := s.tlsConfig.Start()
if err != nil {
return E.Cause(err, "create TLS config")
}
}
if s.externalController {
s.checkAndDownloadExternalUI()
listener, err := net.Listen("tcp", s.httpServer.Addr)
listener, err := tls.NewListener(s.ctx, s.httpServer.Addr, s.tlsConfig)
if err != nil {
return E.Cause(err, "external controller listen error")
}

View File

@ -1,6 +1,7 @@
package experimental
import (
"context"
"os"
"github.com/sagernet/sing-box/adapter"
@ -8,7 +9,7 @@ import (
"github.com/sagernet/sing-box/option"
)
type V2RayServerConstructor = func(logger log.Logger, options option.V2RayAPIOptions) (adapter.V2RayServer, error)
type V2RayServerConstructor = func(ctx context.Context, logger log.Logger, options option.V2RayAPIOptions) (adapter.V2RayServer, error)
var v2rayServerConstructor V2RayServerConstructor
@ -16,9 +17,9 @@ func RegisterV2RayServerConstructor(constructor V2RayServerConstructor) {
v2rayServerConstructor = constructor
}
func NewV2RayServer(logger log.Logger, options option.V2RayAPIOptions) (adapter.V2RayServer, error) {
func NewV2RayServer(ctx context.Context, logger log.Logger, options option.V2RayAPIOptions) (adapter.V2RayServer, error) {
if v2rayServerConstructor == nil {
return nil, os.ErrInvalid
}
return v2rayServerConstructor(logger, options)
return v2rayServerConstructor(ctx, logger, options)
}

View File

@ -1,15 +1,18 @@
package v2rayapi
import (
"context"
"errors"
"net"
"net/http"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/tls"
"github.com/sagernet/sing-box/experimental"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
@ -22,30 +25,48 @@ func init() {
var _ adapter.V2RayServer = (*Server)(nil)
type Server struct {
ctx context.Context
logger log.Logger
listen string
tcpListener net.Listener
grpcServer *grpc.Server
statsService *StatsService
tlsConfig tls.ServerConfig
}
func NewServer(logger log.Logger, options option.V2RayAPIOptions) (adapter.V2RayServer, error) {
func NewServer(ctx context.Context, logger log.Logger, options option.V2RayAPIOptions) (adapter.V2RayServer, error) {
grpcServer := grpc.NewServer(grpc.Creds(insecure.NewCredentials()))
statsService := NewStatsService(common.PtrValueOrDefault(options.Stats))
if statsService != nil {
RegisterStatsServiceServer(grpcServer, statsService)
}
var tlsConfig tls.ServerConfig
if options.TLS != nil {
var err error
tlsConfig, err = tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}
}
server := &Server{
ctx: ctx,
logger: logger,
listen: options.Listen,
grpcServer: grpcServer,
statsService: statsService,
tlsConfig: tlsConfig,
}
return server, nil
}
func (s *Server) Start() error {
listener, err := net.Listen("tcp", s.listen)
if s.tlsConfig != nil {
err := s.tlsConfig.Start()
if err != nil {
return E.Cause(err, "create TLS config")
}
}
listener, err := tls.NewListener(s.ctx, s.listen, s.tlsConfig)
if err != nil {
return err
}

View File

@ -3,6 +3,8 @@
package include
import (
"context"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental"
"github.com/sagernet/sing-box/log"
@ -11,7 +13,7 @@ import (
)
func init() {
experimental.RegisterV2RayServerConstructor(func(logger log.Logger, options option.V2RayAPIOptions) (adapter.V2RayServer, error) {
experimental.RegisterV2RayServerConstructor(func(ctx context.Context, logger log.Logger, options option.V2RayAPIOptions) (adapter.V2RayServer, error) {
return nil, E.New(`v2ray api is not included in this build, rebuild with -tags with_v2ray_api`)
})
}

View File

@ -25,6 +25,8 @@ type ClashAPIOptions struct {
DefaultMode string `json:"default_mode,omitempty"`
ModeList []string `json:"-"`
InboundTLSOptionsContainer
// Deprecated: migrated to global cache file
CacheFile string `json:"cache_file,omitempty"`
// Deprecated: migrated to global cache file
@ -40,6 +42,7 @@ type ClashAPIOptions struct {
type V2RayAPIOptions struct {
Listen string `json:"listen,omitempty"`
Stats *V2RayStatsServiceOptions `json:"stats,omitempty"`
InboundTLSOptionsContainer
}
type V2RayStatsServiceOptions struct {