mirror of
https://gitclone.com/github.com/MetaCubeX/Clash.Meta
synced 2024-11-14 21:31:16 +08:00
chore: add route.ApplyConfig
for CMFA
This commit is contained in:
parent
f6164ac195
commit
6306c6b580
@ -77,7 +77,7 @@ func ParseWithBytes(buf []byte) (*config.Config, error) {
|
|||||||
return config.Parse(buf)
|
return config.Parse(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyConfig dispatch configure to all parts
|
// ApplyConfig dispatch configure to all parts without ExternalController
|
||||||
func ApplyConfig(cfg *config.Config, force bool) {
|
func ApplyConfig(cfg *config.Config, force bool) {
|
||||||
mux.Lock()
|
mux.Lock()
|
||||||
defer mux.Unlock()
|
defer mux.Unlock()
|
||||||
|
46
hub/hub.go
46
hub/hub.go
@ -1,7 +1,10 @@
|
|||||||
package hub
|
package hub
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/config"
|
"github.com/metacubex/mihomo/config"
|
||||||
|
"github.com/metacubex/mihomo/constant/features"
|
||||||
"github.com/metacubex/mihomo/hub/executor"
|
"github.com/metacubex/mihomo/hub/executor"
|
||||||
"github.com/metacubex/mihomo/hub/route"
|
"github.com/metacubex/mihomo/hub/route"
|
||||||
"github.com/metacubex/mihomo/log"
|
"github.com/metacubex/mihomo/log"
|
||||||
@ -33,6 +36,33 @@ func WithSecret(secret string) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyConfig dispatch configure to all parts include ExternalController
|
||||||
|
func ApplyConfig(cfg *config.Config) {
|
||||||
|
applyRoute(cfg)
|
||||||
|
executor.ApplyConfig(cfg, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyRoute(cfg *config.Config) {
|
||||||
|
if features.CMFA && strings.HasSuffix(cfg.Controller.ExternalUI, ":0") {
|
||||||
|
// CMFA have set its default override value to end with ":0" for security.
|
||||||
|
// so we direct return at here
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cfg.Controller.ExternalUI != "" {
|
||||||
|
route.SetUIPath(cfg.Controller.ExternalUI)
|
||||||
|
}
|
||||||
|
route.ReCreateServer(&route.Config{
|
||||||
|
Addr: cfg.Controller.ExternalController,
|
||||||
|
TLSAddr: cfg.Controller.ExternalControllerTLS,
|
||||||
|
UnixAddr: cfg.Controller.ExternalControllerUnix,
|
||||||
|
Secret: cfg.Controller.Secret,
|
||||||
|
Certificate: cfg.TLS.Certificate,
|
||||||
|
PrivateKey: cfg.TLS.PrivateKey,
|
||||||
|
DohServer: cfg.Controller.ExternalDohServer,
|
||||||
|
IsDebug: cfg.General.LogLevel == log.DEBUG,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Parse call at the beginning of mihomo
|
// Parse call at the beginning of mihomo
|
||||||
func Parse(options ...Option) error {
|
func Parse(options ...Option) error {
|
||||||
cfg, err := executor.Parse()
|
cfg, err := executor.Parse()
|
||||||
@ -44,20 +74,6 @@ func Parse(options ...Option) error {
|
|||||||
option(cfg)
|
option(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Controller.ExternalUI != "" {
|
ApplyConfig(cfg)
|
||||||
route.SetUIPath(cfg.Controller.ExternalUI)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Controller.ExternalController != "" {
|
|
||||||
go route.Start(cfg.Controller.ExternalController, cfg.Controller.ExternalControllerTLS,
|
|
||||||
cfg.Controller.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey, cfg.Controller.ExternalDohServer,
|
|
||||||
cfg.General.LogLevel == log.DEBUG)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Controller.ExternalControllerUnix != "" {
|
|
||||||
go route.StartUnix(cfg.Controller.ExternalControllerUnix, cfg.Controller.ExternalDohServer, cfg.General.LogLevel == log.DEBUG)
|
|
||||||
}
|
|
||||||
|
|
||||||
executor.ApplyConfig(cfg, true)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -30,10 +30,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
serverSecret = ""
|
|
||||||
serverAddr = ""
|
|
||||||
|
|
||||||
uiPath = ""
|
uiPath = ""
|
||||||
|
|
||||||
|
httpServer *http.Server
|
||||||
|
tlsServer *http.Server
|
||||||
|
unixServer *http.Server
|
||||||
)
|
)
|
||||||
|
|
||||||
type Traffic struct {
|
type Traffic struct {
|
||||||
@ -46,11 +47,28 @@ type Memory struct {
|
|||||||
OSLimit uint64 `json:"oslimit"` // maybe we need it in the future
|
OSLimit uint64 `json:"oslimit"` // maybe we need it in the future
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Addr string
|
||||||
|
TLSAddr string
|
||||||
|
UnixAddr string
|
||||||
|
Secret string
|
||||||
|
Certificate string
|
||||||
|
PrivateKey string
|
||||||
|
DohServer string
|
||||||
|
IsDebug bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReCreateServer(cfg *Config) {
|
||||||
|
go start(cfg)
|
||||||
|
go startTLS(cfg)
|
||||||
|
go startUnix(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
func SetUIPath(path string) {
|
func SetUIPath(path string) {
|
||||||
uiPath = C.Path.Resolve(path)
|
uiPath = C.Path.Resolve(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func router(isDebug bool, withAuth bool, dohServer string) *chi.Mux {
|
func router(isDebug bool, secret string, dohServer string) *chi.Mux {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
corsM := cors.New(cors.Options{
|
corsM := cors.New(cors.Options{
|
||||||
AllowedOrigins: []string{"*"},
|
AllowedOrigins: []string{"*"},
|
||||||
@ -72,8 +90,8 @@ func router(isDebug bool, withAuth bool, dohServer string) *chi.Mux {
|
|||||||
}())
|
}())
|
||||||
}
|
}
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
if withAuth {
|
if secret != "" {
|
||||||
r.Use(authentication)
|
r.Use(authentication(secret))
|
||||||
}
|
}
|
||||||
r.Get("/", hello)
|
r.Get("/", hello)
|
||||||
r.Get("/logs", getLogs)
|
r.Get("/logs", getLogs)
|
||||||
@ -111,88 +129,111 @@ func router(isDebug bool, withAuth bool, dohServer string) *chi.Mux {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(addr string, tlsAddr string, secret string,
|
func start(cfg *Config) {
|
||||||
certificate, privateKey string, dohServer string, isDebug bool) {
|
// first stop existing server
|
||||||
if serverAddr != "" {
|
if httpServer != nil {
|
||||||
return
|
_ = httpServer.Close()
|
||||||
|
httpServer = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
serverAddr = addr
|
// handle addr
|
||||||
serverSecret = secret
|
if len(cfg.Addr) > 0 {
|
||||||
|
l, err := inbound.Listen("tcp", cfg.Addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("External controller listen error: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Infoln("RESTful API listening at: %s", l.Addr().String())
|
||||||
|
|
||||||
if len(tlsAddr) > 0 {
|
server := &http.Server{
|
||||||
go func() {
|
Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer),
|
||||||
c, err := CN.ParseCert(certificate, privateKey, C.Path)
|
}
|
||||||
if err != nil {
|
if err = server.Serve(l); err != nil {
|
||||||
log.Errorln("External controller tls listen error: %s", err)
|
log.Errorln("External controller serve error: %s", err)
|
||||||
return
|
}
|
||||||
}
|
httpServer = server
|
||||||
|
|
||||||
l, err := inbound.Listen("tcp", tlsAddr)
|
|
||||||
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)
|
|
||||||
tlsServe := &http.Server{
|
|
||||||
Handler: router(isDebug, true, dohServer),
|
|
||||||
TLSConfig: &tls.Config{
|
|
||||||
Certificates: []tls.Certificate{c},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
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 {
|
|
||||||
log.Errorln("External controller listen error: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
serverAddr = l.Addr().String()
|
|
||||||
log.Infoln("RESTful API listening at: %s", serverAddr)
|
|
||||||
|
|
||||||
if err = http.Serve(l, router(isDebug, true, dohServer)); err != nil {
|
|
||||||
log.Errorln("External controller serve error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartUnix(addr string, dohServer string, isDebug bool) {
|
func startTLS(cfg *Config) {
|
||||||
addr = C.Path.Resolve(addr)
|
// first stop existing server
|
||||||
|
if tlsServer != nil {
|
||||||
|
_ = tlsServer.Close()
|
||||||
|
tlsServer = nil
|
||||||
|
}
|
||||||
|
|
||||||
dir := filepath.Dir(addr)
|
// handle tlsAddr
|
||||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
if len(cfg.TLSAddr) > 0 {
|
||||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
c, err := CN.ParseCert(cfg.Certificate, cfg.PrivateKey, C.Path)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("External controller tls listen error: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := inbound.Listen("tcp", cfg.TLSAddr)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("External controller tls listen error: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infoln("RESTful API tls listening at: %s", l.Addr().String())
|
||||||
|
server := &http.Server{
|
||||||
|
Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer),
|
||||||
|
TLSConfig: &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{c},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err = server.ServeTLS(l, "", ""); err != nil {
|
||||||
|
log.Errorln("External controller tls serve error: %s", err)
|
||||||
|
}
|
||||||
|
tlsServer = server
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func startUnix(cfg *Config) {
|
||||||
|
// first stop existing server
|
||||||
|
if unixServer != nil {
|
||||||
|
_ = unixServer.Close()
|
||||||
|
unixServer = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle addr
|
||||||
|
if len(cfg.UnixAddr) > 0 {
|
||||||
|
addr := C.Path.Resolve(cfg.UnixAddr)
|
||||||
|
|
||||||
|
dir := filepath.Dir(addr)
|
||||||
|
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||||
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||||
|
log.Errorln("External controller unix listen error: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/
|
||||||
|
//
|
||||||
|
// Note: As mentioned above in the ‘security’ section, when a socket binds a socket to a valid pathname address,
|
||||||
|
// a socket file is created within the filesystem. On Linux, the application is expected to unlink
|
||||||
|
// (see the notes section in the man page for AF_UNIX) before any other socket can be bound to the same address.
|
||||||
|
// The same applies to Windows unix sockets, except that, DeleteFile (or any other file delete API)
|
||||||
|
// should be used to delete the socket file prior to calling bind with the same path.
|
||||||
|
_ = syscall.Unlink(addr)
|
||||||
|
|
||||||
|
l, err := inbound.Listen("unix", addr)
|
||||||
|
if err != nil {
|
||||||
log.Errorln("External controller unix listen error: %s", err)
|
log.Errorln("External controller unix listen error: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Infoln("RESTful API unix listening at: %s", l.Addr().String())
|
||||||
|
|
||||||
|
server := &http.Server{
|
||||||
|
Handler: router(cfg.IsDebug, "", cfg.DohServer),
|
||||||
|
}
|
||||||
|
if err = server.Serve(l); err != nil {
|
||||||
|
log.Errorln("External controller unix serve error: %s", err)
|
||||||
|
}
|
||||||
|
unixServer = server
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/
|
|
||||||
//
|
|
||||||
// Note: As mentioned above in the ‘security’ section, when a socket binds a socket to a valid pathname address,
|
|
||||||
// a socket file is created within the filesystem. On Linux, the application is expected to unlink
|
|
||||||
// (see the notes section in the man page for AF_UNIX) before any other socket can be bound to the same address.
|
|
||||||
// The same applies to Windows unix sockets, except that, DeleteFile (or any other file delete API)
|
|
||||||
// should be used to delete the socket file prior to calling bind with the same path.
|
|
||||||
_ = syscall.Unlink(addr)
|
|
||||||
|
|
||||||
l, err := inbound.Listen("unix", addr)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln("External controller unix listen error: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
serverAddr = l.Addr().String()
|
|
||||||
log.Infoln("RESTful API unix listening at: %s", serverAddr)
|
|
||||||
|
|
||||||
if err = http.Serve(l, router(isDebug, false, dohServer)); err != nil {
|
|
||||||
log.Errorln("External controller unix serve error: %s", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setPrivateNetworkAccess(next http.Handler) http.Handler {
|
func setPrivateNetworkAccess(next http.Handler) http.Handler {
|
||||||
@ -210,38 +251,35 @@ func safeEuqal(a, b string) bool {
|
|||||||
return subtle.ConstantTimeCompare(aBuf, bBuf) == 1
|
return subtle.ConstantTimeCompare(aBuf, bBuf) == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func authentication(next http.Handler) http.Handler {
|
func authentication(secret string) func(http.Handler) http.Handler {
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
return func(next http.Handler) http.Handler {
|
||||||
if serverSecret == "" {
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
next.ServeHTTP(w, r)
|
// Browser websocket not support custom header
|
||||||
return
|
if r.Header.Get("Upgrade") == "websocket" && r.URL.Query().Get("token") != "" {
|
||||||
}
|
token := r.URL.Query().Get("token")
|
||||||
|
if !safeEuqal(token, secret) {
|
||||||
|
render.Status(r, http.StatusUnauthorized)
|
||||||
|
render.JSON(w, r, ErrUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Browser websocket not support custom header
|
header := r.Header.Get("Authorization")
|
||||||
if r.Header.Get("Upgrade") == "websocket" && r.URL.Query().Get("token") != "" {
|
bearer, token, found := strings.Cut(header, " ")
|
||||||
token := r.URL.Query().Get("token")
|
|
||||||
if !safeEuqal(token, serverSecret) {
|
hasInvalidHeader := bearer != "Bearer"
|
||||||
|
hasInvalidSecret := !found || !safeEuqal(token, secret)
|
||||||
|
if hasInvalidHeader || hasInvalidSecret {
|
||||||
render.Status(r, http.StatusUnauthorized)
|
render.Status(r, http.StatusUnauthorized)
|
||||||
render.JSON(w, r, ErrUnauthorized)
|
render.JSON(w, r, ErrUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
return http.HandlerFunc(fn)
|
||||||
header := r.Header.Get("Authorization")
|
|
||||||
bearer, token, found := strings.Cut(header, " ")
|
|
||||||
|
|
||||||
hasInvalidHeader := bearer != "Bearer"
|
|
||||||
hasInvalidSecret := !found || !safeEuqal(token, serverSecret)
|
|
||||||
if hasInvalidHeader || hasInvalidSecret {
|
|
||||||
render.Status(r, http.StatusUnauthorized)
|
|
||||||
render.JSON(w, r, ErrUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
}
|
}
|
||||||
return http.HandlerFunc(fn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func hello(w http.ResponseWriter, r *http.Request) {
|
func hello(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build android && !cmfa
|
||||||
|
|
||||||
package sing_tun
|
package sing_tun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//go:build !android
|
//go:build !android || cmfa
|
||||||
|
|
||||||
package sing_tun
|
package sing_tun
|
||||||
|
|
||||||
|
2
main.go
2
main.go
@ -135,7 +135,7 @@ func main() {
|
|||||||
return
|
return
|
||||||
case <-hupSign:
|
case <-hupSign:
|
||||||
if cfg, err := executor.ParseWithPath(C.Path.Config()); err == nil {
|
if cfg, err := executor.ParseWithPath(C.Path.Config()); err == nil {
|
||||||
executor.ApplyConfig(cfg, true)
|
hub.ApplyConfig(cfg)
|
||||||
} else {
|
} else {
|
||||||
log.Errorln("Parse config error: %s", err.Error())
|
log.Errorln("Parse config error: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user