mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-23 19:37:13 +00:00
fix(sub): include native WireGuard clients in Clash and JSON subscriptions (#5676)
The Clash (buildProxy) and JSON (getConfig) subscription generators had no WireGuard branch, so a native WireGuard inbound's clients were silently dropped: buildProxy hit its default nil case, and getConfig emitted a config with no proxy outbound. Only the raw subscription (genWireguardLink) and external-link Clash path handled WireGuard. Add a WireGuard case to both generators, mirroring genWireguardLink: the peer public key is derived from the inbound secretKey, while the private key, tunnel address (mihomo ip/ipv6, Xray settings.address), pre-shared key and keep-alive come from the client. The peer routes the full tunnel (0.0.0.0/0, ::/0), which both mihomo and Xray also default to. Field names verified against the mihomo WireGuardOption source (private-key, public-key, pre-shared-key, persistent-keepalive, ip, ipv6, mtu, dns) and the Xray wireguard outbound schema (secretKey, address, peers[].publicKey/endpoint/ preSharedKey/keepAlive/allowedIPs, mtu).
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
yaml "github.com/goccy/go-yaml"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
||||
wgutil "github.com/mhsanaei/3x-ui/v3/internal/util/wireguard"
|
||||
)
|
||||
|
||||
type SubClashService struct {
|
||||
@@ -214,6 +215,9 @@ func (s *SubClashService) buildProxy(subReq *SubService, inbound *model.Inbound,
|
||||
if inbound.Protocol == model.Hysteria {
|
||||
return s.buildHysteriaProxy(subReq, inbound, client, ep)
|
||||
}
|
||||
if inbound.Protocol == model.WireGuard {
|
||||
return s.buildWireguardProxy(subReq, inbound, client, ep)
|
||||
}
|
||||
|
||||
network, _ := stream["network"].(string)
|
||||
|
||||
@@ -361,6 +365,67 @@ func (s *SubClashService) buildHysteriaProxy(subReq *SubService, inbound *model.
|
||||
return proxy
|
||||
}
|
||||
|
||||
// buildWireguardProxy produces a mihomo-compatible Clash entry for a native
|
||||
// WireGuard inbound, mirroring genWireguardLink: the peer public key is derived
|
||||
// from the inbound secretKey, while the private key, tunnel address, and
|
||||
// pre-shared key come from the client. Returns nil when the client has no key.
|
||||
func (s *SubClashService) buildWireguardProxy(subReq *SubService, inbound *model.Inbound, client model.Client, ep map[string]any) map[string]any {
|
||||
if client.PrivateKey == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var inboundSettings map[string]any
|
||||
_ = json.Unmarshal([]byte(inbound.Settings), &inboundSettings)
|
||||
secretKey, _ := inboundSettings["secretKey"].(string)
|
||||
|
||||
proxy := map[string]any{
|
||||
"name": subReq.endpointRemark(inbound, client.Email, ep, ""),
|
||||
"type": "wireguard",
|
||||
"server": inbound.Listen,
|
||||
"port": inbound.Port,
|
||||
"udp": true,
|
||||
"private-key": client.PrivateKey,
|
||||
}
|
||||
if secretKey != "" {
|
||||
if pub, err := wgutil.PublicKeyFromPrivate(secretKey); err == nil {
|
||||
proxy["public-key"] = pub
|
||||
}
|
||||
}
|
||||
if client.PreSharedKey != "" {
|
||||
proxy["pre-shared-key"] = client.PreSharedKey
|
||||
}
|
||||
if client.KeepAlive > 0 {
|
||||
proxy["persistent-keepalive"] = client.KeepAlive
|
||||
}
|
||||
for _, addr := range client.AllowedIPs {
|
||||
ip := stripCIDR(addr)
|
||||
if ip == "" {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(ip, ":") {
|
||||
proxy["ipv6"] = ip
|
||||
} else {
|
||||
proxy["ip"] = ip
|
||||
}
|
||||
}
|
||||
if mtu, ok := inboundSettings["mtu"].(float64); ok && mtu > 0 {
|
||||
proxy["mtu"] = int(mtu)
|
||||
}
|
||||
if dns, _ := inboundSettings["dns"].(string); dns != "" {
|
||||
servers := make([]string, 0)
|
||||
for _, server := range strings.Split(dns, ",") {
|
||||
if server = strings.TrimSpace(server); server != "" {
|
||||
servers = append(servers, server)
|
||||
}
|
||||
}
|
||||
if len(servers) > 0 {
|
||||
proxy["dns"] = servers
|
||||
}
|
||||
}
|
||||
|
||||
return proxy
|
||||
}
|
||||
|
||||
// buildXhttpClashOpts converts xhttpSettings from 3x-ui's camelCase JSON
|
||||
// storage into the kebab-case map that Mihomo expects under xhttp-opts.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user