diff --git a/internal/web/service/xray.go b/internal/web/service/xray.go index ecb1d9060..3a94b2b53 100644 --- a/internal/web/service/xray.go +++ b/internal/web/service/xray.go @@ -179,6 +179,16 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) { } settings := map[string]any{} _ = json.Unmarshal([]byte(inbound.Settings), &settings) + var wireguardClientsByEmail map[string]model.Client + if inbound.Protocol == model.WireGuard { + inboundClients, _ := ParseInboundSettingsClients(inbound.Settings) + if len(inboundClients) > 0 { + wireguardClientsByEmail = make(map[string]model.Client, len(inboundClients)) + for _, client := range inboundClients { + wireguardClientsByEmail[strings.ToLower(strings.TrimSpace(client.Email))] = client + } + } + } dbClients, listErr := s.inboundService.clientService.ListForInbound(nil, inbound.Id) if listErr != nil { @@ -244,6 +254,10 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) { entry["auth"] = c.Auth } case model.WireGuard: + if inboundClient, ok := wireguardClientsByEmail[strings.ToLower(strings.TrimSpace(c.Email))]; ok { + c.AllowedIPs = inboundClient.AllowedIPs + c.PreSharedKey = inboundClient.PreSharedKey + } wgPeers = append(wgPeers, model.WireguardPeerFromClient(c)) continue } diff --git a/internal/web/service/xray_wireguard_config_test.go b/internal/web/service/xray_wireguard_config_test.go index 1cc5922ff..dc19504a3 100644 --- a/internal/web/service/xray_wireguard_config_test.go +++ b/internal/web/service/xray_wireguard_config_test.go @@ -55,6 +55,59 @@ func seedWGInbound(t *testing.T, tag string, port int, clients []model.Client) { } } +func seedDualTunnelClient(t *testing.T, enabled bool) string { + t.Helper() + setupSettingTestDB(t) + db := database.GetDB() + + const email = "dual@wg.test" + wgClient := model.Client{ + Email: email, + Enable: true, + PublicKey: "pub-dual", + AllowedIPs: []string{"10.0.0.5/32"}, + PreSharedKey: "wg-psk", + } + awgClient := wgClient + awgClient.AllowedIPs = []string{"10.8.1.5/32"} + awgClient.PreSharedKey = "awg-psk" + + wgSettings, err := json.Marshal(map[string]any{ + "secretKey": wgTestSecretKey(), + "mtu": 1420, + "clients": []model.Client{wgClient}, + }) + if err != nil { + t.Fatalf("marshal wg settings: %v", err) + } + awgSettings, err := json.Marshal(map[string]any{ + "server": map[string]any{"subnetIp": "10.8.1.0", "subnetCidr": 24}, + "clients": []model.Client{awgClient}, + }) + if err != nil { + t.Fatalf("marshal awg settings: %v", err) + } + + wgInbound := &model.Inbound{Tag: "wg-dual", Enable: true, Port: 51823, Protocol: model.WireGuard, Settings: string(wgSettings)} + awgInbound := &model.Inbound{Tag: "awg-dual", Enable: true, Port: 51824, Protocol: model.AmneziaWG, Settings: string(awgSettings)} + if err := db.Create(wgInbound).Error; err != nil { + t.Fatalf("create wg inbound: %v", err) + } + if err := db.Create(awgInbound).Error; err != nil { + t.Fatalf("create awg inbound: %v", err) + } + + svc := ClientService{} + if err := svc.SyncInbound(nil, wgInbound.Id, []model.Client{wgClient}); err != nil { + t.Fatalf("SyncInbound(wg): %v", err) + } + awgClient.Enable = enabled + if err := svc.SyncInbound(nil, awgInbound.Id, []model.Client{awgClient}); err != nil { + t.Fatalf("SyncInbound(awg): %v", err) + } + return email +} + func wgPeerList(t *testing.T, settings map[string]any) []map[string]any { t.Helper() if _, ok := settings["clients"]; ok { @@ -137,6 +190,39 @@ func TestGetXrayConfigWireGuardDisabledClientExcluded(t *testing.T) { } } +func TestGetXrayConfigWireGuardUsesInboundLocalTunnelFields(t *testing.T) { + email := seedDualTunnelClient(t, true) + + var shared model.ClientRecord + if err := database.GetDB().Where("email = ?", email).First(&shared).Error; err != nil { + t.Fatalf("read shared client: %v", err) + } + if shared.AllowedIPs != "10.8.1.5/32" || shared.PreSharedKey != "awg-psk" { + t.Fatalf("test setup did not persist AmneziaWG last: allowedIPs=%q preSharedKey=%q", shared.AllowedIPs, shared.PreSharedKey) + } + + peers := wgPeerList(t, wgInboundEmittedSettings(t, "wg-dual")) + if len(peers) != 1 { + t.Fatalf("expected 1 peer, got %d: %v", len(peers), peers) + } + allowed, ok := peers[0]["allowedIPs"].([]any) + if !ok || len(allowed) != 1 || allowed[0] != "10.0.0.5/32" { + t.Fatalf("WireGuard peer allowedIPs = %v, want [10.0.0.5/32]", peers[0]["allowedIPs"]) + } + if peers[0]["preSharedKey"] != "wg-psk" { + t.Fatalf("WireGuard peer preSharedKey = %v, want wg-psk", peers[0]["preSharedKey"]) + } +} + +func TestGetXrayConfigWireGuardDisabledDualProtocolClientExcluded(t *testing.T) { + seedDualTunnelClient(t, false) + + peers := wgPeerList(t, wgInboundEmittedSettings(t, "wg-dual")) + if len(peers) != 0 { + t.Fatalf("expected disabled dual-protocol client to be excluded, got %v", peers) + } +} + func TestGetXrayConfigWireGuardNoClientsEmitsEmptyPeers(t *testing.T) { seedWGInbound(t, "wg-empty", 51822, nil)