mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-17 23:57:15 +00:00
feat(amneziawg): retire the kernel-module OS-shellout code and install.sh path
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>
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
// Package amneziawg holds the AmneziaWG protocol's shared, DB-backed shapes
|
||||
// (Instance, Peer, Obfuscation20, ServerSettings/InboundSettings) and the
|
||||
// pure functions that derive an Instance from a stored inbound row. It no
|
||||
// longer manages any OS-level interface itself: that was the kernel-module
|
||||
// (DKMS) + awg-quick + TPROXY architecture this fork shipped originally,
|
||||
// retired in favor of an embedded, pure-Go one (amneziawg-go over a gVisor
|
||||
// netstack, see internal/amneziawgnet) in a hard cutover. This package's
|
||||
// remaining code is deliberately protocol-shape-only, with no OS dependency
|
||||
// at all, so both the (now-removed) kernel-module path and the embedded
|
||||
// path could read -- and, historically, did read -- it identically.
|
||||
package amneziawg
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
||||
)
|
||||
|
||||
// InstanceFromInbound derives a desired Instance from an AmneziaWG inbound,
|
||||
// building one peer per active client. Returns false when the inbound is not
|
||||
// a usable AmneziaWG inbound (wrong protocol, unparseable settings, or no
|
||||
// server block) or has no enabled peer to serve — mirroring
|
||||
// mtproto.InstanceFromInbound, which skips the sidecar entirely rather than
|
||||
// run it with nothing to serve.
|
||||
func InstanceFromInbound(ib *model.Inbound) (Instance, bool) {
|
||||
if ib == nil || ib.Protocol != model.AmneziaWG {
|
||||
return Instance{}, false
|
||||
}
|
||||
var parsed InboundSettings
|
||||
if err := json.Unmarshal([]byte(ib.Settings), &parsed); err != nil || parsed.Server == nil {
|
||||
return Instance{}, false
|
||||
}
|
||||
server := parsed.Server
|
||||
|
||||
peers := make([]Peer, 0, len(parsed.Clients))
|
||||
for _, c := range parsed.Clients {
|
||||
if !c.Enable || c.PublicKey == "" || len(c.AllowedIPs) == 0 {
|
||||
continue
|
||||
}
|
||||
peers = append(peers, Peer{
|
||||
Email: c.Email,
|
||||
PublicKey: c.PublicKey,
|
||||
PresharedKey: c.PreSharedKey,
|
||||
AllowedIPs: c.AllowedIPs,
|
||||
ForwardedPorts: c.ForwardedPorts,
|
||||
})
|
||||
}
|
||||
if len(peers) == 0 {
|
||||
return Instance{}, false
|
||||
}
|
||||
|
||||
addresses := []string{serverAddress(server.SubnetIP, server.SubnetCIDR)}
|
||||
if server.IPv6Enabled {
|
||||
if v6, ok := serverAddressV6(server.IPv6Subnet); ok {
|
||||
addresses = append(addresses, v6)
|
||||
}
|
||||
}
|
||||
|
||||
return Instance{
|
||||
Id: ib.Id,
|
||||
Tag: ib.Tag,
|
||||
InterfaceName: interfaceNameForID(ib.Id),
|
||||
ListenPort: ib.Port,
|
||||
PrivateKey: server.PrivateKey,
|
||||
PublicKey: server.PublicKey,
|
||||
Address: addresses,
|
||||
MTU: server.MTU,
|
||||
Obfuscation: server.Obfuscation(),
|
||||
Peers: peers,
|
||||
ExternalInterface: server.ExternalInterface,
|
||||
IPv6Enabled: server.IPv6Enabled,
|
||||
IPv6ExternalInterface: server.IPv6ExternalInterface,
|
||||
RouteThroughXray: server.RouteThroughXray,
|
||||
}, true
|
||||
}
|
||||
|
||||
// interfaceNameForID derives the OS-level interface name for an inbound, e.g.
|
||||
// "awg42". Kept even though the embedded path has no real kernel interface
|
||||
// of its own: internal/amneziawgnet still uses the same name as a purely
|
||||
// cosmetic/log-friendly label, so an existing peer's identity/history
|
||||
// doesn't shift across the cutover.
|
||||
func interfaceNameForID(id int) string {
|
||||
return fmt.Sprintf("awg%d", id)
|
||||
}
|
||||
|
||||
// serverAddress returns the server's own tunnel address for a subnet base,
|
||||
// e.g. "10.8.1.1/24" for base "10.8.1.0" or "10.8.1.5". The server always
|
||||
// holds the first usable host of the network subnetIP/cidr actually
|
||||
// describes -- derived via netip rather than assuming subnetIP already ends
|
||||
// in ".0", so a subnetIP that isn't a bare network address (a typo, or a
|
||||
// manually edited value) can never collide with peer addresses, which are
|
||||
// allocated starting from the network's second host upward (see
|
||||
// allocateWireguardAddress). Falls back to the previous literal behavior
|
||||
// only if subnetIP/cidr doesn't parse as an IPv4 network at all -- normal
|
||||
// saves never reach that path since ValidateSubnetIPv4 already rejects it.
|
||||
func serverAddress(subnetIP string, cidr int) string {
|
||||
if cidr <= 0 {
|
||||
cidr = 24
|
||||
}
|
||||
// A /32 has no host bits at all -- "first usable host" is meaningless,
|
||||
// and Next() would step outside the block entirely -- so a single-host
|
||||
// base is used exactly as given, same as before this fix.
|
||||
prefix, err := netip.ParsePrefix(fmt.Sprintf("%s/%d", subnetIP, cidr))
|
||||
if err != nil || !prefix.Addr().Is4() || cidr >= 32 {
|
||||
return fmt.Sprintf("%s/%d", subnetIP, cidr)
|
||||
}
|
||||
host := prefix.Masked().Addr().Next()
|
||||
return fmt.Sprintf("%s/%d", host, cidr)
|
||||
}
|
||||
|
||||
// serverAddressV6 returns the server's own IPv6 tunnel address for a subnet
|
||||
// CIDR (e.g. "fd86:ea04:1115::1/64" for "fd86:ea04:1115::/64"), the first
|
||||
// usable host in the prefix. ok is false when subnetCIDR is empty or not a
|
||||
// valid IPv6 prefix.
|
||||
func serverAddressV6(subnetCIDR string) (addr string, ok bool) {
|
||||
prefix, err := netip.ParsePrefix(subnetCIDR)
|
||||
if err != nil || !prefix.Addr().Is6() {
|
||||
return "", false
|
||||
}
|
||||
host := prefix.Masked().Addr().Next()
|
||||
return fmt.Sprintf("%s/%d", host, prefix.Bits()), true
|
||||
}
|
||||
|
||||
// FirstIPv4 returns the first IPv4 address (mask stripped) among allowedIPs,
|
||||
// or "" if none — used by internal/web/service/server.go's
|
||||
// amneziawgEmailIndex to derive a peer's tunnel IPv4 address for the panel's
|
||||
// access-log viewer.
|
||||
func FirstIPv4(allowedIPs []string) string {
|
||||
for _, a := range allowedIPs {
|
||||
if prefix, err := netip.ParsePrefix(a); err == nil {
|
||||
if prefix.Addr().Is4() {
|
||||
return prefix.Addr().String()
|
||||
}
|
||||
continue
|
||||
}
|
||||
if addr, err := netip.ParseAddr(a); err == nil && addr.Is4() {
|
||||
return addr.String()
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user