mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-25 23:35:00 +03:00
feat(inbounds): deploy AmneziaWG, TUIC and MTProto inbounds to nodes
The node gate assumed a node-assigned sidecar row would never converge,
because the master's reconcile loops only read node_id IS NULL rows. But
a pushed row is local on the node's own panel, whose AmneziaWG, TUIC and
mtg loops run it like any other; node-adopted rows of these protocols
already worked. Only creating or cloning them from the master was blocked.
Open the three protocols on both lists and fix what assumed the master's
host for a node row:
- A node older than the release that introduced the protocol (MTProto
v3.5.0, AmneziaWG v3.7.0, TUIC v3.8.0) would hand it to Xray as-is, so
add and protocol-changing update refuse it, and a node that has not
reported its version yet. Dev builds ("dev+<sha>") track main and pass.
- MTProto's routeXrayPort is a loopback port on the host running mtg.
The master no longer allocates one, nor forwards a cloned source's, for
a node row; the node allocates its own and node sync adopts it back.
- AmneziaWG forwardedPorts were checked against the master's inbounds,
web port and id-derived relay ports. A node row is now checked against
its own node's inbounds; the node re-checks what only it knows.
UpdateInbound restores the stored nodeId before that check.
Verified on a docker master+node pair: AWG, routed MTProto and TUIC
created and cloned from the master start on the node (interface, mtg,
tuic-server); an AWG client added and an MTProto client edited on the
master apply on the node; the node-chosen egress port survives edits.
Closes #6306
This commit is contained in:
@@ -2,8 +2,13 @@ package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/util/common"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/util/version"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// inboundShadowsocksMethod extracts settings.method for Shadowsocks inbounds so
|
||||
@@ -53,10 +58,8 @@ func inboundCanEnableTlsFlow(protocol, streamSettings, settings string) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// nodeEligibleProtocols mirrors the frontend's NODE_ELIGIBLE_PROTOCOLS. The
|
||||
// sidecar-managed protocols are absent because their reconcile loops only query
|
||||
// NodeID IS NULL rows, so a node-assigned one would never be reconciled at all.
|
||||
// A new protocol defaults to ineligible until added here, as on the frontend.
|
||||
// nodeEligibleProtocols mirrors the frontend's NODE_ELIGIBLE_PROTOCOLS. A sidecar
|
||||
// protocol's row is local on the node it is pushed to, so that panel runs it.
|
||||
var nodeEligibleProtocols = map[model.Protocol]bool{
|
||||
model.VLESS: true,
|
||||
model.VMESS: true,
|
||||
@@ -64,11 +67,43 @@ var nodeEligibleProtocols = map[model.Protocol]bool{
|
||||
model.Shadowsocks: true,
|
||||
model.Hysteria: true,
|
||||
model.WireGuard: true,
|
||||
model.MTProto: true,
|
||||
model.AmneziaWG: true,
|
||||
model.TUIC: true,
|
||||
}
|
||||
|
||||
// isNodeEligibleProtocol reports whether protocol may be assigned to a node.
|
||||
func isNodeEligibleProtocol(protocol model.Protocol) bool {
|
||||
return nodeEligibleProtocols[protocol]
|
||||
// nodeProtocolFirstRelease is the panel release that introduced each protocol
|
||||
// newer than node support itself; an older node would hand it to Xray as-is.
|
||||
var nodeProtocolFirstRelease = map[model.Protocol]string{
|
||||
model.MTProto: "v3.5.0",
|
||||
model.AmneziaWG: "v3.7.0",
|
||||
model.TUIC: "v3.8.0",
|
||||
}
|
||||
|
||||
// checkNodeCanHostProtocol refuses assigning protocol to nodeID unless the
|
||||
// protocol may live on a node and that node's panel is new enough to run it.
|
||||
func checkNodeCanHostProtocol(db *gorm.DB, nodeID int, protocol model.Protocol) error {
|
||||
if !nodeEligibleProtocols[protocol] {
|
||||
return common.NewErrorf("%s inbounds cannot be assigned to a node", protocol)
|
||||
}
|
||||
firstRelease, ok := nodeProtocolFirstRelease[protocol]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
var node model.Node
|
||||
if err := db.Select("id", "name", "panel_version").First(&node, nodeID).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.TrimSpace(node.PanelVersion) == "" {
|
||||
return common.NewErrorf("node %q has not reported its panel version yet; %s inbounds need %s or newer",
|
||||
node.Name, protocol, firstRelease)
|
||||
}
|
||||
// A dev build reports "dev+<sha>": it tracks main, which carries every protocol.
|
||||
if cmp, ok := version.Compare(node.PanelVersion, firstRelease); ok && cmp < 0 {
|
||||
return common.NewErrorf("node %q runs panel %s; %s inbounds need %s or newer",
|
||||
node.Name, node.PanelVersion, protocol, firstRelease)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// vlessEncryptionEnabled reports whether a VLESS inbound has VLESS-level
|
||||
|
||||
Reference in New Issue
Block a user