feat: add external-controller-cors can config allow-origins and allow-private-network

This commit is contained in:
wwqgtxx 2024-09-29 17:13:43 +08:00
parent 264713571d
commit fc9d5cfee9
6 changed files with 56 additions and 26 deletions

View File

@ -107,6 +107,12 @@ type Controller struct {
ExternalUI string
ExternalDohServer string
Secret string
Cors Cors
}
type Cors struct {
AllowOrigins []string
AllowPrivateNetwork bool
}
// Experimental config
@ -191,6 +197,11 @@ type Config struct {
TLS *TLS
}
type RawCors struct {
AllowOrigins []string `yaml:"allow-origins" json:"allow-origins"`
AllowPrivateNetwork bool `yaml:"allow-private-network" json:"allow-private-network"`
}
type RawDNS struct {
Enable bool `yaml:"enable" json:"enable"`
PreferH3 bool `yaml:"prefer-h3" json:"prefer-h3"`
@ -368,6 +379,7 @@ type RawConfig struct {
ExternalControllerPipe string `yaml:"external-controller-pipe" json:"external-controller-pipe"`
ExternalControllerUnix string `yaml:"external-controller-unix" json:"external-controller-unix"`
ExternalControllerTLS string `yaml:"external-controller-tls" json:"external-controller-tls"`
ExternalControllerCors RawCors `yaml:"external-controller-cors" json:"external-controller-cors"`
ExternalUI string `yaml:"external-ui" json:"external-ui"`
ExternalUIURL string `yaml:"external-ui-url" json:"external-ui-url"`
ExternalUIName string `yaml:"external-ui-name" json:"external-ui-name"`
@ -541,6 +553,10 @@ func DefaultRawConfig() *RawConfig {
OverrideDest: true,
},
ExternalUIURL: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip",
ExternalControllerCors: RawCors{
AllowOrigins: []string{"*"},
AllowPrivateNetwork: true,
},
}
}
@ -775,6 +791,10 @@ func parseController(cfg *RawConfig) (*Controller, error) {
ExternalControllerUnix: cfg.ExternalControllerUnix,
ExternalControllerTLS: cfg.ExternalControllerTLS,
ExternalDohServer: cfg.ExternalDohServer,
Cors: Cors{
AllowOrigins: cfg.ExternalControllerCors.AllowOrigins,
AllowPrivateNetwork: cfg.ExternalControllerCors.AllowPrivateNetwork,
},
}, nil
}

View File

@ -58,6 +58,12 @@ external-controller: 0.0.0.0:9093 # RESTful API 监听地址
external-controller-tls: 0.0.0.0:9443 # RESTful API HTTPS 监听地址,需要配置 tls 部分配置文件
# secret: "123456" # `Authorization:Bearer ${secret}`
# RESTful API CORS标头配置
external-controller-cors:
allow-origins:
- *
allow-private-network: true
# RESTful API Unix socket 监听地址( windows版本大于17063也可以使用即大于等于1803/RS4版本即可使用
# !!!注意: 从Unix socket访问api接口不会验证secret 如果开启请自行保证安全问题
# 测试方法: curl -v --unix-socket "mihomo.sock" http://localhost/

2
go.mod
View File

@ -8,7 +8,6 @@ require (
github.com/coreos/go-iptables v0.7.0
github.com/dlclark/regexp2 v1.11.4
github.com/go-chi/chi/v5 v5.1.0
github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.3
github.com/gobwas/ws v1.4.0
github.com/gofrs/uuid/v5 v5.3.0
@ -37,6 +36,7 @@ require (
github.com/openacid/low v0.1.21
github.com/oschwald/maxminddb-golang v1.12.0
github.com/puzpuzpuz/xsync/v3 v3.4.0
github.com/sagernet/cors v1.2.1
github.com/sagernet/fswatch v0.1.1
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a
github.com/sagernet/sing v0.5.0-alpha.13

4
go.sum
View File

@ -42,8 +42,6 @@ github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXb
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
@ -160,6 +158,8 @@ github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs=
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=

View File

@ -59,6 +59,10 @@ func applyRoute(cfg *config.Config) {
PrivateKey: cfg.TLS.PrivateKey,
DohServer: cfg.Controller.ExternalDohServer,
IsDebug: cfg.General.LogLevel == log.DEBUG,
Cors: route.Cors{
AllowOrigins: cfg.Controller.Cors.AllowOrigins,
AllowPrivateNetwork: cfg.Controller.Cors.AllowPrivateNetwork,
},
})
}

View File

@ -23,10 +23,10 @@ import (
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
"github.com/go-chi/render"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil"
"github.com/sagernet/cors"
)
var (
@ -58,6 +58,22 @@ type Config struct {
PrivateKey string
DohServer string
IsDebug bool
Cors Cors
}
type Cors struct {
AllowOrigins []string
AllowPrivateNetwork bool
}
func (c Cors) Apply(r chi.Router) {
r.Use(cors.New(cors.Options{
AllowedOrigins: c.AllowOrigins,
AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"},
AllowedHeaders: []string{"Content-Type", "Authorization"},
AllowPrivateNetwork: c.AllowPrivateNetwork,
MaxAge: 300,
}).Handler)
}
func ReCreateServer(cfg *Config) {
@ -73,16 +89,9 @@ func SetUIPath(path string) {
uiPath = C.Path.Resolve(path)
}
func router(isDebug bool, secret string, dohServer string) *chi.Mux {
func router(isDebug bool, secret string, dohServer string, cors Cors) *chi.Mux {
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(setPrivateNetworkAccess)
r.Use(corsM.Handler)
cors.Apply(r)
if isDebug {
r.Mount("/debug", func() http.Handler {
r := chi.NewRouter()
@ -151,7 +160,7 @@ func start(cfg *Config) {
log.Infoln("RESTful API listening at: %s", l.Addr().String())
server := &http.Server{
Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer),
Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer, cfg.Cors),
}
httpServer = server
if err = server.Serve(l); err != nil {
@ -183,7 +192,7 @@ func startTLS(cfg *Config) {
log.Infoln("RESTful API tls listening at: %s", l.Addr().String())
server := &http.Server{
Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer),
Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer, cfg.Cors),
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{c},
},
@ -232,7 +241,7 @@ func startUnix(cfg *Config) {
log.Infoln("RESTful API unix listening at: %s", l.Addr().String())
server := &http.Server{
Handler: router(cfg.IsDebug, "", cfg.DohServer),
Handler: router(cfg.IsDebug, "", cfg.DohServer, cfg.Cors),
}
unixServer = server
if err = server.Serve(l); err != nil {
@ -263,7 +272,7 @@ func startPipe(cfg *Config) {
log.Infoln("RESTful API pipe listening at: %s", l.Addr().String())
server := &http.Server{
Handler: router(cfg.IsDebug, "", cfg.DohServer),
Handler: router(cfg.IsDebug, "", cfg.DohServer, cfg.Cors),
}
pipeServer = server
if err = server.Serve(l); err != nil {
@ -272,15 +281,6 @@ func startPipe(cfg *Config) {
}
}
func setPrivateNetworkAccess(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" {
w.Header().Add("Access-Control-Allow-Private-Network", "true")
}
next.ServeHTTP(w, r)
})
}
func safeEuqal(a, b string) bool {
aBuf := utils.ImmutableBytesFromString(a)
bBuf := utils.ImmutableBytesFromString(b)