diff --git a/config/config.go b/config/config.go index f5efc936..39b75e54 100644 --- a/config/config.go +++ b/config/config.go @@ -80,6 +80,9 @@ type Controller struct { ExternalController string `json:"-"` ExternalUI string `json:"-"` Secret string `json:"-"` + TLSPort int `json:"-"` + Cert string `json:"-"` + PrivateKey string `json:"-"` } // DNS config @@ -230,6 +233,9 @@ type Sniffer struct { // Experimental config type Experimental struct { Fingerprints []string `yaml:"fingerprints"` + TLSPort int `yaml:"tls-port,omitempty"` + Cert string `yaml:"cert,omitempty"` + PrivateKey string `yaml:"private-key,omitempty"` } // Config is clash config manager @@ -669,6 +675,9 @@ func parseGeneral(cfg *RawConfig) (*General, error) { ExternalController: cfg.ExternalController, ExternalUI: cfg.ExternalUI, Secret: cfg.Secret, + TLSPort: cfg.Experimental.TLSPort, + Cert: cfg.Experimental.Cert, + PrivateKey: cfg.Experimental.PrivateKey, }, UnifiedDelay: cfg.UnifiedDelay, Mode: cfg.Mode, diff --git a/hub/hub.go b/hub/hub.go index 471fdb5e..216a0c68 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -1,6 +1,8 @@ package hub import ( + "errors" + "github.com/Dreamacro/clash/config" "github.com/Dreamacro/clash/hub/executor" "github.com/Dreamacro/clash/hub/route" @@ -42,7 +44,12 @@ func Parse(options ...Option) error { } if cfg.General.ExternalController != "" { - go route.Start(cfg.General.ExternalController, cfg.General.Secret) + if cfg.General.TLSPort != 0 && (len(cfg.General.PrivateKey) == 0 || len(cfg.General.Cert) == 0) { + return errors.New("Must be provided certificates and keys, for tls controller") + } + + go route.Start(cfg.General.ExternalController, cfg.General.Secret, cfg.General.TLSPort, + cfg.General.Cert, cfg.General.PrivateKey) } executor.ApplyConfig(cfg, true) diff --git a/hub/route/server.go b/hub/route/server.go index 2d533496..2b449c2d 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -2,8 +2,11 @@ package route import ( "bytes" + "crypto/tls" "encoding/json" + "net" "net/http" + "strconv" "strings" "time" @@ -40,7 +43,7 @@ func SetUIPath(path string) { uiPath = C.Path.Resolve(path) } -func Start(addr string, secret string) { +func Start(addr string, secret string, tlsPort int, cert string, privateKey string) { if serverAddr != "" { return } @@ -49,18 +52,16 @@ func Start(addr string, secret string) { serverSecret = secret r := chi.NewRouter() - corsM := cors.New(cors.Options{ AllowedOrigins: []string{"*"}, AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"}, AllowedHeaders: []string{"Content-Type", "Authorization"}, MaxAge: 300, }) - + r.Use() r.Use(corsM.Handler) r.Group(func(r chi.Router) { r.Use(authentication) - r.Get("/", hello) r.Get("/logs", getLogs) r.Get("/traffic", traffic) @@ -84,6 +85,34 @@ func Start(addr string, secret string) { }) }) } + if tlsPort >0 { + go func() { + if host, _, err := net.SplitHostPort(addr); err != nil { + log.Errorln("External controller tls serve error,%s", err) + } else { + l, err := inbound.Listen("tcp", net.JoinHostPort(host, strconv.Itoa(tlsPort))) + if err != nil { + log.Errorln("External controller tls listen error: %s", err) + return + } + serverAddr = l.Addr().String() + log.Infoln("RESTful API tls listening at: %s", serverAddr) + certificate, err := tls.X509KeyPair([]byte(cert), []byte(privateKey)) + if err != nil { + log.Errorln("External controller tls sevre error,%s", err) + } + tlsServe := &http.Server{ + Handler: r, + TLSConfig: &tls.Config{ + Certificates: []tls.Certificate{certificate}, + }, + } + if err = tlsServe.ServeTLS(l, "", ""); err != nil { + log.Errorln("External controller tls serve error: %s", err) + } + } + }() + } l, err := inbound.Listen("tcp", addr) if err != nil { @@ -92,9 +121,12 @@ func Start(addr string, secret string) { } serverAddr = l.Addr().String() log.Infoln("RESTful API listening at: %s", serverAddr) + if err = http.Serve(l, r); err != nil { log.Errorln("External controller serve error: %s", err) } + + } func authentication(next http.Handler) http.Handler {