service_tunnel_hosts_test.go 3.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. package sub
  2. import (
  3. "fmt"
  4. "strings"
  5. "testing"
  6. "github.com/mhsanaei/3x-ui/v3/internal/database"
  7. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  8. )
  9. func seedTunnelSubInbound(t *testing.T, protocol model.Protocol, tag, subID, email, settings string, port int) *model.Inbound {
  10. t.Helper()
  11. db := database.GetDB()
  12. ib := &model.Inbound{
  13. UserId: 1, Tag: tag, Enable: true, Listen: "203.0.113.5", Port: port,
  14. Protocol: protocol, Remark: tag, Settings: settings,
  15. }
  16. if err := db.Create(ib).Error; err != nil {
  17. t.Fatalf("create %s: %v", tag, err)
  18. }
  19. rec := &model.ClientRecord{Email: email, SubID: subID, Enable: true}
  20. if err := db.Create(rec).Error; err != nil {
  21. t.Fatalf("create client: %v", err)
  22. }
  23. if err := db.Create(&model.ClientInbound{ClientId: rec.Id, InboundId: ib.Id}).Error; err != nil {
  24. t.Fatalf("link client: %v", err)
  25. }
  26. return ib
  27. }
  28. // A Host on a WireGuard inbound must replace the advertised endpoint; the raw
  29. // generator used to ignore it and always emit the panel's own address.
  30. func TestGetSubs_WireGuardAdvertisesHostEndpoints(t *testing.T) {
  31. initSubDB(t)
  32. serverPriv, _ := mustWireguardKeypair(t)
  33. clientPriv, _ := mustWireguardKeypair(t)
  34. const email, subID = "alice@wg", "sub-wg-hosts"
  35. settings := fmt.Sprintf(`{"secretKey":%q,"clients":[{"email":%q,"privateKey":%q,"allowedIPs":["10.0.0.2/32"],"enable":true}]}`,
  36. serverPriv, email, clientPriv)
  37. ib := seedTunnelSubInbound(t, model.WireGuard, "wg-in", subID, email, settings, 51820)
  38. seedHost(t, &model.Host{InboundId: ib.Id, SortOrder: 2, Remark: "CDN-B", Address: "wg2.example.com"})
  39. seedHost(t, &model.Host{InboundId: ib.Id, SortOrder: 1, Remark: "CDN-A", Address: "wg.example.com", Port: 443})
  40. links, _, _, _, err := NewSubService("").GetSubs(subID, "sub.example.com")
  41. if err != nil {
  42. t.Fatalf("GetSubs: %v", err)
  43. }
  44. parts := splitLinkLines(strings.Join(links, "\n"))
  45. want := []struct{ host, remark string }{
  46. {"wg.example.com:443", "wg-in-CDN-A-" + email},
  47. {"wg2.example.com:51820", "wg-in-CDN-B-" + email},
  48. }
  49. if len(parts) != len(want) {
  50. t.Fatalf("links = %d, want %d: %v", len(parts), len(want), parts)
  51. }
  52. for i, w := range want {
  53. u := parseWireguardSubLink(t, parts[i])
  54. if u.Host != w.host || u.Fragment != w.remark {
  55. t.Fatalf("link %d = %s#%s, want %s#%s", i, u.Host, u.Fragment, w.host, w.remark)
  56. }
  57. if u.User.Username() != clientPriv {
  58. t.Fatalf("link %d private key = %q, want the client's", i, u.User.Username())
  59. }
  60. }
  61. }
  62. // The AmneziaWG vpn:// payload carries the endpoint inside its .conf text, so a
  63. // Host must reach the Endpoint line and the remark comment, not only the URL.
  64. func TestGetSubs_AmneziaWGAdvertisesHostEndpoint(t *testing.T) {
  65. initSubDB(t)
  66. serverPriv, serverPub := mustWireguardKeypair(t)
  67. clientPriv, _ := mustWireguardKeypair(t)
  68. const email, subID = "alice@awg", "sub-awg-hosts"
  69. settings := fmt.Sprintf(`{"server":{"privateKey":%q,"publicKey":%q,"mtu":1420},"clients":[{"email":%q,"privateKey":%q,"allowedIPs":["10.8.0.2/32"],"enable":true}]}`,
  70. serverPriv, serverPub, email, clientPriv)
  71. ib := seedTunnelSubInbound(t, model.AmneziaWG, "awg-in", subID, email, settings, 51821)
  72. seedHost(t, &model.Host{InboundId: ib.Id, SortOrder: 1, Remark: "CDN", Address: "awg.example.com", Port: 8443})
  73. links, _, _, _, err := NewSubService("").GetSubs(subID, "sub.example.com")
  74. if err != nil {
  75. t.Fatalf("GetSubs: %v", err)
  76. }
  77. parts := splitLinkLines(strings.Join(links, "\n"))
  78. if len(parts) != 1 {
  79. t.Fatalf("links = %d, want 1: %v", len(parts), parts)
  80. }
  81. conf := decodeAmneziaWGSubLink(t, parts[0])
  82. for _, line := range []string{"Endpoint = awg.example.com:8443", "# awg-in-CDN-" + email, "PrivateKey = " + clientPriv} {
  83. if !strings.Contains(conf, line) {
  84. t.Fatalf("config missing %q\n%s", line, conf)
  85. }
  86. }
  87. if strings.Contains(conf, "203.0.113.5") {
  88. t.Fatalf("config still advertises the inbound address\n%s", conf)
  89. }
  90. }