diff --git a/frontend/src/lib/xray/inbound-link.ts b/frontend/src/lib/xray/inbound-link.ts index c7509f4b4..005edd075 100644 --- a/frontend/src/lib/xray/inbound-link.ts +++ b/frontend/src/lib/xray/inbound-link.ts @@ -822,8 +822,8 @@ export function genWireguardLink(input: GenWireguardLinkInput): string { ? Wireguard.generateKeypair(settings.secretKey).publicKey : ''; if (pubKey.length > 0) url.searchParams.set('publickey', pubKey); - if (peer.allowedIPs.length > 0 && peer.allowedIPs[0]) { - url.searchParams.set('address', peer.allowedIPs[0]); + if (peer.allowedIPs.length > 0) { + url.searchParams.set('address', peer.allowedIPs.join(',')); } if (typeof settings.mtu === 'number' && settings.mtu > 0) { url.searchParams.set('mtu', String(settings.mtu)); @@ -850,7 +850,7 @@ export function genWireguardConfig(input: GenWireguardLinkInput): string { let txt = `[Interface]\n`; txt += `PrivateKey = ${peer.privateKey ?? ''}\n`; - txt += `Address = ${peer.allowedIPs[0] ?? ''}\n`; + txt += `Address = ${peer.allowedIPs.join(', ')}\n`; txt += `DNS = ${settings.dns || '1.1.1.1, 1.0.0.1'}\n`; if (typeof settings.mtu === 'number' && settings.mtu > 0) { txt += `MTU = ${settings.mtu}\n`; diff --git a/frontend/src/test/inbound-link.test.ts b/frontend/src/test/inbound-link.test.ts index 4935b2b77..97f56f046 100644 --- a/frontend/src/test/inbound-link.test.ts +++ b/frontend/src/test/inbound-link.test.ts @@ -308,6 +308,43 @@ describe('genWireguardLink + genWireguardConfig', () => { } }); +describe('genWireguardLink + genWireguardConfig multi allowedIPs', () => { + const settings = { + secretKey: '', + mtu: 1280, + dns: '', + peers: [ + { + privateKey: 'cLI', + allowedIPs: ['10.0.0.2/32', 'fd00::2/128'], + }, + ], + } as unknown as WireguardInboundSettings; + + it('joins every allowed IP into the share-link address param', () => { + const link = genWireguardLink({ + settings, + address: 'wg.example.test', + port: 51820, + remark: 'dual-stack', + peerIndex: 0, + }); + const u = new URL(link); + expect(u.searchParams.get('address')).toBe('10.0.0.2/32,fd00::2/128'); + }); + + it('joins every allowed IP into the .conf Address line', () => { + const config = genWireguardConfig({ + settings, + address: 'wg.example.test', + port: 51820, + remark: 'dual-stack', + peerIndex: 0, + }); + expect(config).toContain('Address = 10.0.0.2/32, fd00::2/128\n'); + }); +}); + describe('resolveAddr precedence', () => { const baseInbound = { listen: '', diff --git a/internal/sub/service.go b/internal/sub/service.go index 7a8712b8d..6bc436601 100644 --- a/internal/sub/service.go +++ b/internal/sub/service.go @@ -644,8 +644,8 @@ func (s *SubService) genWireguardLink(inbound *model.Inbound, email string) stri params["publickey"] = pub } } - if len(client.AllowedIPs) > 0 && client.AllowedIPs[0] != "" { - params["address"] = client.AllowedIPs[0] + if joined := strings.Join(client.AllowedIPs, ","); joined != "" { + params["address"] = joined } if mtu, ok := settings["mtu"].(float64); ok && mtu > 0 { params["mtu"] = strconv.Itoa(int(mtu)) diff --git a/internal/sub/service_wireguard_test.go b/internal/sub/service_wireguard_test.go index 582f356a3..a071e3ec9 100644 --- a/internal/sub/service_wireguard_test.go +++ b/internal/sub/service_wireguard_test.go @@ -55,6 +55,36 @@ func TestGenWireguardLinkFields(t *testing.T) { } } +func TestGenWireguardLinkMultiAllowedIPs(t *testing.T) { + serverPriv, _, err := wgutil.GenerateWireguardKeypair() + if err != nil { + t.Fatalf("keypair: %v", err) + } + clientPriv, _, err := wgutil.GenerateWireguardKeypair() + if err != nil { + t.Fatalf("client keypair: %v", err) + } + + inbound := &model.Inbound{ + Listen: "203.0.113.7", + Port: 51820, + Protocol: model.WireGuard, + Remark: "wg-sub", + Settings: `{"secretKey":"` + serverPriv + `","clients":[{"email":"user","privateKey":"` + clientPriv + `","allowedIPs":["10.0.0.2/32","fd00::2/128"]}]}`, + } + + s := &SubService{} + link := s.genWireguardLink(inbound, "user") + + u, err := url.Parse(link) + if err != nil { + t.Fatalf("link does not parse: %v\n got: %s", err, link) + } + if got, want := u.Query().Get("address"), "10.0.0.2/32,fd00::2/128"; got != want { + t.Fatalf("address = %q, want %q (all allowed IPs joined, not just the first)", got, want) + } +} + func TestGenWireguardLinkWrongProtocol(t *testing.T) { s := &SubService{} vless := &model.Inbound{Protocol: model.VLESS, Settings: `{"clients":[{"email":"user"}]}`}