fix(core,helper): only let the owner drive the elevated Core

Changelog: The elevated Core only answers its owner
This commit is contained in:
chen08209
2026-10-05 08:24:15 +08:00
parent 4b59eca853
commit d780cb863d
40 changed files with 1561 additions and 153 deletions
+17 -5
View File
@@ -308,11 +308,7 @@ func updateConfig(params *UpdateParams) error {
if params.Tun != nil {
patchTun(&general.Tun, params.Tun)
}
if params.ExternalController != nil &&
*params.ExternalController != currentConfig.Controller.ExternalController {
currentConfig.Controller.ExternalController = *params.ExternalController
route.ReCreateServer(routeConfig(currentConfig))
}
patchController(params)
if params.Authentication != nil {
applyAuthentication(currentConfig, *params.Authentication)
}
@@ -328,6 +324,22 @@ func updateConfig(params *UpdateParams) error {
return nil
}
func patchController(params *UpdateParams) {
controller := currentConfig.Controller
address, secret := controller.ExternalController, controller.Secret
if params.ExternalController != nil {
address = *params.ExternalController
}
if params.Secret != nil {
secret = *params.Secret
}
if address == controller.ExternalController && secret == controller.Secret {
return
}
controller.ExternalController, controller.Secret = address, secret
route.ReCreateServer(routeConfig(currentConfig))
}
func applyAuthentication(cfg *config.Config, authentication []string) {
users := make([]auth.AuthUser, 0, len(authentication))
for _, line := range authentication {
+59
View File
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"net"
"net/http"
"net/netip"
"os"
"path/filepath"
@@ -16,6 +17,7 @@ import (
"github.com/metacubex/mihomo/component/updater"
"github.com/metacubex/mihomo/config"
"github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/hub/route"
authStore "github.com/metacubex/mihomo/listener/auth"
"github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/tunnel"
@@ -242,6 +244,63 @@ func TestUpdateConfigLeavesTheControllerAloneWhenTheAddressIsUnchanged(t *testin
}
}
func TestUpdateConfigReplacesTheSecretTheProfileCarried(t *testing.T) {
withCurrentConfig(t, &config.Config{
General: &config.General{},
Controller: &config.Controller{Secret: "from-the-profile"},
})
closed, secret := "", "generated"
if err := updateConfig(&UpdateParams{ExternalController: &closed, Secret: &secret}); err != nil {
t.Fatalf("updateConfig error: %v", err)
}
if currentConfig.Controller.Secret != secret {
t.Errorf("Secret = %q, want %q", currentConfig.Controller.Secret, secret)
}
}
func TestUpdateConfigServesTheControllerBehindTheSecretItWasGiven(t *testing.T) {
probe, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("reserve a port: %v", err)
}
address := probe.Addr().String()
_ = probe.Close()
withCurrentConfig(t, &config.Config{General: &config.General{}, Controller: &config.Controller{}})
t.Cleanup(func() { route.ReCreateServer(&route.Config{}) })
secret := "generated"
if err := updateConfig(&UpdateParams{ExternalController: &address, Secret: &secret}); err != nil {
t.Fatalf("updateConfig error: %v", err)
}
status := func(authorization string) int {
request, _ := http.NewRequest(http.MethodGet, "http://"+address+"/version", nil)
if authorization != "" {
request.Header.Set("Authorization", authorization)
}
deadline := time.Now().Add(3 * time.Second)
for {
response, err := http.DefaultClient.Do(request)
if err == nil {
_ = response.Body.Close()
return response.StatusCode
}
if time.Now().After(deadline) {
t.Fatalf("the controller never came up: %v", err)
}
time.Sleep(50 * time.Millisecond)
}
}
if got := status(""); got != http.StatusUnauthorized {
t.Errorf("without the secret: status %d, want %d", got, http.StatusUnauthorized)
}
if got := status("Bearer " + secret); got != http.StatusOK {
t.Errorf("with the secret: status %d, want %d", got, http.StatusOK)
}
}
// geoUpdaterCalls counts the reconciliation the code under test asked for. The
// real calls spawn a goroutine that downloads the GEO databases, which is not
// something a unit test should reach for.
+1
View File
@@ -32,6 +32,7 @@ type UpdateParams struct {
IPv6 *bool `json:"ipv6"`
TCPConcurrent *bool `json:"tcp-concurrent"`
ExternalController *string `json:"external-controller"`
Secret *string `json:"secret"`
UnifiedDelay *bool `json:"unified-delay"`
Authentication *[]string `json:"authentication"`
GeoAutoUpdate *bool `json:"geo-auto-update"`