mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-07 18:57:14 +00:00
59dff059c1
Hard cutover, part 3: everything that only ever existed to drive the kernel-module (DKMS) + awg-quick + TPROXY architecture is gone now that internal/amneziawgnet's embedded path is wired in as the real thing. internal/amneziawg/manager.go -> instance.go (renamed, ~90% smaller): kept InstanceFromInbound and its direct helpers (interfaceNameForID, serverAddress, serverAddressV6) plus the exported FirstIPv4 (still used by server.go's access-log email index) -- all pure, protocol-shape-only code with no OS dependency, reused by both the old and new paths historically. Deleted the old Manager (GetManager/Ensure/Reconcile/StopAll/CollectTraffic/ the fingerprint methods), generateServerConfig and everything under it (writeObfuscation, defaultPostUpDown, appendOrTrue, detectDefaultInterface), and process control (interfaceUp/Down, syncConfig, getPeerStats, IsAwgInstalled). route_egress.go deleted entirely (the TPROXY bridge's port/fwmark/table constants and rule-rendering, fully superseded by internal/amneziawgnet's SOCKSPortForInbound/SocksPassword). portfwd.go trimmed to just the parsing/validation half (ForwardedPortsInclude, still used for save-time conflict checks); the iptables DNAT rendering half is gone -- per-client port-forwarding has no equivalent under the embedded path yet (tracked as Phase 3.6). install.sh: removed install_ndppd, enable_ipv6_forwarding, enable_tproxy_support, should/install_amneziawg, and check_secure_boot (and their call sites) -- roughly 265 lines. No more DKMS build, PPA/keyring setup, TPROXY kernel module loading, or Secure Boot warning: the embedded path needs none of it. Not in this commit (tracked as an explicit follow-up, not silently dropped): the frontend's routeThroughXray toggle is now vestigial (the field stays in the Go/JSON schema for backward compat with existing stored settings, see types.go) but its UI/schema removal needs the frontend type-regen + openapi.json hand-patch dance this fork always does for a settings-shape change, which is its own separate pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
123 lines
4.1 KiB
Go
123 lines
4.1 KiB
Go
package amneziawg
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
)
|
|
|
|
func mkInboundSettings(t *testing.T, server *ServerSettings, clients []model.Client) string {
|
|
t.Helper()
|
|
bs, err := json.Marshal(InboundSettings{Server: server, Clients: clients})
|
|
if err != nil {
|
|
t.Fatalf("marshal settings: %v", err)
|
|
}
|
|
return string(bs)
|
|
}
|
|
|
|
func validServer() *ServerSettings {
|
|
return &ServerSettings{
|
|
PrivateKey: "serverPriv",
|
|
PublicKey: "serverPub",
|
|
SubnetIP: "10.8.1.0",
|
|
SubnetCIDR: 24,
|
|
}
|
|
}
|
|
|
|
func TestInstanceFromInboundParsesEnabledPeers(t *testing.T) {
|
|
settings := mkInboundSettings(t, validServer(), []model.Client{
|
|
{Email: "a@x", Enable: true, PublicKey: "pubA", PreSharedKey: "pskA", AllowedIPs: []string{"10.8.1.2/32"}},
|
|
{Email: "b@x", Enable: false, PublicKey: "pubB", AllowedIPs: []string{"10.8.1.3/32"}},
|
|
{Email: "c@x", Enable: true, PublicKey: "", AllowedIPs: []string{"10.8.1.4/32"}}, // no key: skipped
|
|
{Email: "d@x", Enable: true, PublicKey: "pubD", AllowedIPs: nil}, // no address: skipped
|
|
})
|
|
ib := &model.Inbound{Id: 7, Tag: "awg-tag", Protocol: model.AmneziaWG, Port: 51820, Settings: settings}
|
|
|
|
inst, ok := InstanceFromInbound(ib)
|
|
if !ok {
|
|
t.Fatal("expected a usable instance")
|
|
}
|
|
if inst.Id != 7 || inst.Tag != "awg-tag" || inst.ListenPort != 51820 {
|
|
t.Fatalf("instance identity not carried over: %+v", inst)
|
|
}
|
|
if inst.InterfaceName != "awg7" {
|
|
t.Fatalf("InterfaceName = %q, want awg7", inst.InterfaceName)
|
|
}
|
|
if len(inst.Address) != 1 || inst.Address[0] != "10.8.1.1/24" {
|
|
t.Fatalf("Address = %v, want [10.8.1.1/24]", inst.Address)
|
|
}
|
|
if len(inst.Peers) != 1 {
|
|
t.Fatalf("Peers = %+v, want exactly 1 (only a@x qualifies)", inst.Peers)
|
|
}
|
|
p := inst.Peers[0]
|
|
if p.Email != "a@x" || p.PublicKey != "pubA" || p.PresharedKey != "pskA" || len(p.AllowedIPs) != 1 || p.AllowedIPs[0] != "10.8.1.2/32" {
|
|
t.Fatalf("peer mismatch: %+v", p)
|
|
}
|
|
}
|
|
|
|
func TestInstanceFromInboundRejectsWrongProtocol(t *testing.T) {
|
|
settings := mkInboundSettings(t, validServer(), []model.Client{
|
|
{Email: "a@x", Enable: true, PublicKey: "pubA", AllowedIPs: []string{"10.8.1.2/32"}},
|
|
})
|
|
ib := &model.Inbound{Id: 1, Protocol: model.VLESS, Settings: settings}
|
|
if _, ok := InstanceFromInbound(ib); ok {
|
|
t.Fatal("non-AmneziaWG inbound must be rejected")
|
|
}
|
|
}
|
|
|
|
func TestInstanceFromInboundRejectsNil(t *testing.T) {
|
|
if _, ok := InstanceFromInbound(nil); ok {
|
|
t.Fatal("nil inbound must be rejected")
|
|
}
|
|
}
|
|
|
|
func TestInstanceFromInboundRejectsMissingServer(t *testing.T) {
|
|
ib := &model.Inbound{Id: 1, Protocol: model.AmneziaWG, Settings: `{"clients":[]}`}
|
|
if _, ok := InstanceFromInbound(ib); ok {
|
|
t.Fatal("settings with no server block must be rejected")
|
|
}
|
|
}
|
|
|
|
func TestInstanceFromInboundRejectsUnparseableSettings(t *testing.T) {
|
|
ib := &model.Inbound{Id: 1, Protocol: model.AmneziaWG, Settings: `not json`}
|
|
if _, ok := InstanceFromInbound(ib); ok {
|
|
t.Fatal("unparseable settings must be rejected")
|
|
}
|
|
}
|
|
|
|
func TestInstanceFromInboundEmptyWhenNoEnabledPeers(t *testing.T) {
|
|
settings := mkInboundSettings(t, validServer(), []model.Client{
|
|
{Email: "a@x", Enable: false, PublicKey: "pubA", AllowedIPs: []string{"10.8.1.2/32"}},
|
|
})
|
|
ib := &model.Inbound{Id: 1, Protocol: model.AmneziaWG, Settings: settings}
|
|
if _, ok := InstanceFromInbound(ib); ok {
|
|
t.Fatal("an inbound with zero enabled peers must be skipped, like mtproto.InstanceFromInbound")
|
|
}
|
|
}
|
|
|
|
func TestServerAddress(t *testing.T) {
|
|
cases := []struct {
|
|
subnet string
|
|
cidr int
|
|
want string
|
|
}{
|
|
{"10.8.1.0", 24, "10.8.1.1/24"},
|
|
{"10.8.1.0", 0, "10.8.1.1/24"}, // cidr <= 0 defaults to /24
|
|
{"10.8.1.5", 24, "10.8.1.1/24"}, // non-network base: must not collide with peer allocation starting at .2
|
|
{"10.8.1.254", 24, "10.8.1.1/24"},
|
|
{"192.168.5.10", 32, "192.168.5.10/32"}, // /32 has no host bits: used as-is
|
|
}
|
|
for _, c := range cases {
|
|
if got := serverAddress(c.subnet, c.cidr); got != c.want {
|
|
t.Errorf("serverAddress(%q, %d) = %q, want %q", c.subnet, c.cidr, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestInterfaceNameForID(t *testing.T) {
|
|
if got := interfaceNameForID(42); got != "awg42" {
|
|
t.Errorf("interfaceNameForID(42) = %q, want awg42", got)
|
|
}
|
|
}
|