fix(core,helper): only let the owner drive the elevated Core
Changelog: The elevated Core only answers its owner
This commit is contained in:
+17
-5
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"`
|
||||
|
||||
Reference in New Issue
Block a user