fix(inbounds): reject incompatible VLESS fallbacks and prevent Xray crashes (#7003)

Xray refuses a VLESS inbound with server decryption whenever fallbacks is non-nil, even an empty list, so an API or imported inbound with decryption and fallbacks: [] took every inbound down. Empty fallback arrays are now normalized away on add, update, config build and node push; non-empty ones are refused with decryption, enabling decryption over stored fallback rules is refused, and a legacy conflicting row holds back the running core instead of stopping it. The form, API docs and locales follow. Closes #7001.
This commit is contained in:
Hamed
2026-11-03 18:22:30 +03:30
committed by GitHub
parent fee7b1621d
commit 19f2f21d81
31 changed files with 853 additions and 79 deletions
+17 -2
View File
@@ -1297,6 +1297,9 @@ func (s *InboundService) normalizeMtprotoXrayPort(inbound *model.Inbound, oldSet
func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, bool, error) {
defer InvalidateSubData()
inbound.Id = 0
if err := normalizeVlessFallbacks(inbound); err != nil {
return nil, false, err
}
if err := normalizeTuicSettings(inbound); err != nil {
return inbound, false, err
}
@@ -2055,6 +2058,9 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
// Restore the stored NodeID before any host-scoped check so a node inbound
// stays scoped to its own node (the payload's nodeId is unreliable, often absent).
inbound.NodeID = oldInbound.NodeID
if err := normalizeVlessFallbacks(inbound); err != nil {
return nil, false, err
}
if err := normalizeTuicSettings(inbound); err != nil {
return inbound, false, err
}
@@ -2126,6 +2132,9 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
if err := s.validateUpdatedInboundClients(inbound, stored); err != nil {
return err
}
if err := validateVlessManagedFallbacks(tx, inbound); err != nil {
return err
}
conflict, cErr := checkPortConflictTx(tx, inbound, inbound.Id)
if cErr != nil {
return cErr
@@ -2433,12 +2442,18 @@ func (s *InboundService) buildInboundForNodePush(tx *gorm.DB, inbound *model.Inb
}
built := *inbound
if err := normalizeVlessFallbacks(&built); err != nil {
return nil, err
}
if err := validateVlessManagedFallbacks(tx, &built); err != nil {
return nil, err
}
settings := map[string]any{}
if err := json.Unmarshal([]byte(inbound.Settings), &settings); err != nil {
if err := json.Unmarshal([]byte(built.Settings), &settings); err != nil {
return nil, err
}
if !inboundCanHostFallbacks(inbound) {
if !inboundCanHostFallbacks(&built) {
return &built, nil
}
fallbacks, err := s.fallbackService.BuildFallbacksJSON(tx, inbound.Id)