mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-12 17:05:01 +03:00
fix(sub): default VLESS client encryption to none when the inbound has none stored (#6781)
VLESS inbounds whose settings carry no "encryption" key (rows saved by
panels before the field existed, or inbounds added over the API with only
"decryption":"none", as the API docs example does) produced a JSON
subscription outbound with "encryption": "". Xray refuses to load that
config ("VLESS users: please add/set \"encryption\":\"none\" for every
user"), so every VLESS entry of the JSON subscription was unusable. The
share link left the parameter out instead.
Treat a missing or empty value as "none" for both outputs, matching the
frontend inbound schema default and the docs link generator.
This commit is contained in:
@@ -885,15 +885,11 @@ func (s *SubJsonService) genVless(subReq *SubService, inbound *model.Inbound, st
|
||||
}
|
||||
outbound.StreamSettings = streamSettings
|
||||
|
||||
// Add encryption for VLESS outbound from inbound settings
|
||||
inboundSettings := subReq.linkSettings(inbound)
|
||||
encryption, _ := inboundSettings["encryption"].(string)
|
||||
|
||||
settings := map[string]any{
|
||||
"address": inbound.Listen,
|
||||
"port": inbound.Port,
|
||||
"id": client.ID,
|
||||
"encryption": encryption,
|
||||
"encryption": vlessClientEncryption(subReq.linkSettings(inbound)),
|
||||
"level": 8,
|
||||
}
|
||||
if client.Flow != "" && !inbound.DisableFlow {
|
||||
|
||||
@@ -2,6 +2,7 @@ package sub
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@@ -231,6 +232,38 @@ func TestSubJsonServiceVlessFlattened(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// VLESS inbounds stored without settings.encryption (pre-2.6.8 rows, API adds)
|
||||
// must still give clients "none": xray refuses a VLESS outbound with "".
|
||||
func TestSubVlessEncryptionDefaultsToNone(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
settings string
|
||||
want string
|
||||
}{
|
||||
{"missing", `{"clients":[{"id":"uuid-1","email":"user"}],"decryption":"none"}`, "none"},
|
||||
{"empty", `{"clients":[{"id":"uuid-1","email":"user"}],"decryption":"none","encryption":""}`, "none"},
|
||||
{"vlessenc kept", `{"clients":[{"id":"uuid-1","email":"user"}],"decryption":"mlkem768x25519plus.native.600s.d","encryption":"mlkem768x25519plus.native.0rtt.e"}`, "mlkem768x25519plus.native.0rtt.e"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
inbound := &model.Inbound{Listen: "1.2.3.4", Port: 443, Protocol: model.VLESS, Settings: tc.settings, StreamSettings: `{"network":"tcp","security":"none"}`}
|
||||
|
||||
settings := outboundSettings(t, NewSubJsonService("", "", "", "", nil).genVless(&SubService{}, inbound, nil, model.Client{ID: "uuid-1"}, ""))
|
||||
if settings["encryption"] != tc.want {
|
||||
t.Fatalf("json outbound encryption = %#v, want %q", settings["encryption"], tc.want)
|
||||
}
|
||||
|
||||
link, err := url.Parse((&SubService{}).genVlessLink(inbound, "user"))
|
||||
if err != nil {
|
||||
t.Fatalf("parse link: %v", err)
|
||||
}
|
||||
if got := link.Query().Get("encryption"); got != tc.want {
|
||||
t.Fatalf("share link encryption = %q, want %q (%s)", got, tc.want, link)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubJsonServiceVlessFlowSuppressedByDisableFlow(t *testing.T) {
|
||||
inbound := &model.Inbound{Listen: "1.2.3.4", Port: 443, Protocol: model.VLESS, Settings: `{"encryption":"none"}`, DisableFlow: true}
|
||||
client := model.Client{ID: "uuid-1", Flow: "xtls-rprx-vision"}
|
||||
|
||||
+10
-4
@@ -1193,6 +1193,15 @@ func vlessEncryptionEnabled(settings map[string]any) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// vlessClientEncryption reads the client-side VLESS encryption from inbound settings. Inbounds
|
||||
// stored without the field (older panels, API adds) mean "none", as the frontend schema does.
|
||||
func vlessClientEncryption(settings map[string]any) string {
|
||||
if encryption, _ := settings["encryption"].(string); encryption != "" {
|
||||
return encryption
|
||||
}
|
||||
return "none"
|
||||
}
|
||||
|
||||
// vlessFlowAllowed mirrors inboundCanEnableTlsFlow in internal/web/service: Vision runs over
|
||||
// TLS/REALITY on raw TCP, or inside VLESS encryption on any transport.
|
||||
func vlessFlowAllowed(network, security string, settings map[string]any) bool {
|
||||
@@ -1218,11 +1227,8 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||
params := make(map[string]string)
|
||||
params["type"] = streamNetwork
|
||||
|
||||
// Add encryption parameter for VLESS from inbound settings
|
||||
settings := s.linkSettings(inbound)
|
||||
if encryption, ok := settings["encryption"].(string); ok {
|
||||
params["encryption"] = encryption
|
||||
}
|
||||
params["encryption"] = vlessClientEncryption(settings)
|
||||
|
||||
applyShareNetworkParams(stream, streamNetwork, params)
|
||||
if finalmask, ok := stream["finalmask"].(map[string]any); ok {
|
||||
|
||||
Reference in New Issue
Block a user