mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-25 22:06:09 +00:00
fix(sub): keep listen/bind IP out of subscription page URLs
The subscription page leaked an inbound's server-side Listen IP into the client-facing URLs when a bind address was set: - Per-config links: resolveInboundAddress returned the bind Listen IP (loopback/private/public alike) instead of the host the subscriber reached the panel on. It now returns the node address for node-managed inbounds, otherwise the subscriber host; the bind Listen is ignored (External Proxy remains the way to advertise a specific endpoint). - Subscription Copy URL (SUB/JSON/CLASH): BuildURLs composed the base differently from the panel's Client Information page and never normalized the request host, so a loopback/bind request leaked the raw IP. The composition is extracted into the shared SettingService.BuildSubURIBase, used by both the panel and the sub page so they render identically, and fed the already-normalized subscriber host.
This commit is contained in:
@@ -61,6 +61,44 @@ func TestIsRoutableHost(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveInboundAddress(t *testing.T) {
|
||||
const reqHost = "sub.example.com"
|
||||
|
||||
// A subscriber reaches the panel through reqHost; the inbound's own
|
||||
// bind Listen IP (loopback, private, or even a public secondary IP) is
|
||||
// a server-side detail and must never become the link's connect host.
|
||||
t.Run("bind listen IP must not leak into the link host", func(t *testing.T) {
|
||||
s := &SubService{address: reqHost}
|
||||
for _, listen := range []string{"127.0.0.1", "10.0.0.5", "192.168.1.10", "1.2.3.4", "0.0.0.0", "::", "::0", ""} {
|
||||
ib := &model.Inbound{Listen: listen}
|
||||
if got := s.resolveInboundAddress(ib); got != reqHost {
|
||||
t.Fatalf("listen %q: address = %q, want %q (subscriber host, not bind IP)", listen, got, reqHost)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("node-managed inbound uses the node address", func(t *testing.T) {
|
||||
id := 7
|
||||
s := &SubService{
|
||||
address: reqHost,
|
||||
nodesByID: map[int]*model.Node{7: {Id: 7, Address: "node7.example.com"}},
|
||||
}
|
||||
ib := &model.Inbound{NodeID: &id, Listen: "1.2.3.4"}
|
||||
if got := s.resolveInboundAddress(ib); got != "node7.example.com" {
|
||||
t.Fatalf("node-managed address = %q, want node7.example.com", got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("node id with no known node falls back to subscriber host", func(t *testing.T) {
|
||||
id := 9
|
||||
s := &SubService{address: reqHost, nodesByID: map[int]*model.Node{}}
|
||||
ib := &model.Inbound{NodeID: &id, Listen: "10.0.0.1"}
|
||||
if got := s.resolveInboundAddress(ib); got != reqHost {
|
||||
t.Fatalf("unknown-node address = %q, want subscriber host %q", got, reqHost)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestUnmarshalStreamSettings(t *testing.T) {
|
||||
got := unmarshalStreamSettings(`{"network":"ws","wsSettings":{"path":"/api"}}`)
|
||||
if got["network"] != "ws" {
|
||||
|
||||
Reference in New Issue
Block a user