fix: fix Shadowsocks support for Xray kernel
This commit is contained in:
@@ -152,28 +152,30 @@ func buildRoutes(panelRoutes []panel.RouteRule, custom []map[string]any) M {
|
|||||||
rules = append(rules, M(cr))
|
rules = append(rules, M(cr))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standard blocks for private/loopback
|
// Standard blocks for private IPv4 and IPv6 ranges to prevent SSRF.
|
||||||
// rules = append(rules, M{
|
rules = append(rules,
|
||||||
// "outbound": "block",
|
M{
|
||||||
// "ip_cidr": []string{
|
"outbound": "block",
|
||||||
// "10.0.0.0/8",
|
"ip_cidr": []string{
|
||||||
// "100.64.0.0/10",
|
"10.0.0.0/8",
|
||||||
// "127.0.0.0/8",
|
"100.64.0.0/10",
|
||||||
// "169.254.0.0/16",
|
"127.0.0.0/8",
|
||||||
// "172.16.0.0/12",
|
"169.254.0.0/16",
|
||||||
// "192.0.0.0/24",
|
"172.16.0.0/12",
|
||||||
// "192.168.0.0/16",
|
"192.0.0.0/24",
|
||||||
// "198.18.0.0/15",
|
"192.168.0.0/16",
|
||||||
// "fc00::/7",
|
"198.18.0.0/15",
|
||||||
// "fe80::/10",
|
},
|
||||||
// "::1/128",
|
},
|
||||||
// },
|
M{
|
||||||
// }, M{
|
"outbound": "block",
|
||||||
// "outbound": "block",
|
"ip_cidr": []string{
|
||||||
// "domain": []string{
|
"fc00::/7",
|
||||||
// "geoip:private",
|
"fe80::/10",
|
||||||
// },
|
"::1/128",
|
||||||
// })
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
// Panel-defined routes (usually specific blocks/proxies)
|
// Panel-defined routes (usually specific blocks/proxies)
|
||||||
for _, pr := range panelRoutes {
|
for _, pr := range panelRoutes {
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ func TestBuildInbound_VMess_WithGRPC(t *testing.T) {
|
|||||||
ServerPort: 443,
|
ServerPort: 443,
|
||||||
Network: "grpc",
|
Network: "grpc",
|
||||||
NetworkSettings: map[string]interface{}{
|
NetworkSettings: map[string]interface{}{
|
||||||
"service_name": "mygrpc",
|
"serviceName": "mygrpc",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
inbound := buildInbound(nc, testUsers, "", "")
|
inbound := buildInbound(nc, testUsers, "", "")
|
||||||
@@ -245,9 +245,13 @@ func TestBuildInbound_Trojan_NoTLS(t *testing.T) {
|
|||||||
TLS: 0,
|
TLS: 0,
|
||||||
}
|
}
|
||||||
inbound := buildInbound(nc, testUsers, "", "")
|
inbound := buildInbound(nc, testUsers, "", "")
|
||||||
if _, exists := inbound["tls"]; exists {
|
// Trojan requires TLS; buildTrojan should force-enable self-signed TLS
|
||||||
t.Error("trojan with tls=0 should not have TLS config")
|
// even when the panel sends tls=0.
|
||||||
|
tls, exists := inbound["tls"].(M)
|
||||||
|
if !exists {
|
||||||
|
t.Fatal("trojan with tls=0 should still get TLS (force-enabled)")
|
||||||
}
|
}
|
||||||
|
assertMapValue(t, tls, "enabled", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildInbound_Trojan_WithTLS(t *testing.T) {
|
func TestBuildInbound_Trojan_WithTLS(t *testing.T) {
|
||||||
|
|||||||
@@ -311,42 +311,45 @@ var ss2022Methods = map[string]ss2022Config{
|
|||||||
func buildShadowsocks(base M, nc *panel.NodeConfig, users []panel.User) M {
|
func buildShadowsocks(base M, nc *panel.NodeConfig, users []panel.User) M {
|
||||||
ss2022, isSS2022 := ss2022Methods[nc.Cipher]
|
ss2022, isSS2022 := ss2022Methods[nc.Cipher]
|
||||||
|
|
||||||
// Single-user mode for traditional ciphers
|
clients := make([]M, 0, len(users))
|
||||||
if !isSS2022 {
|
|
||||||
if len(users) > 0 {
|
if isSS2022 {
|
||||||
base["settings"] = M{
|
// SS2022: server key at top level, per-user key must be Base64 of fixed-length raw bytes.
|
||||||
"method": nc.Cipher,
|
// Only blake3-aes-* supports multi-user in Xray; chacha20 variant is single-user only.
|
||||||
"password": users[0].UUID,
|
rawBuf := make([]byte, ss2022.size)
|
||||||
"email": userEmail(users[0].ID),
|
for i := range users {
|
||||||
"network": "tcp,udp",
|
u := &users[i]
|
||||||
|
for j := range rawBuf {
|
||||||
|
rawBuf[j] = 0
|
||||||
}
|
}
|
||||||
|
copy(rawBuf, u.UUID)
|
||||||
|
clients = append(clients, M{
|
||||||
|
"password": base64.StdEncoding.EncodeToString(rawBuf),
|
||||||
|
"email": userEmail(u.ID),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return base
|
base["settings"] = M{
|
||||||
}
|
"method": nc.Cipher,
|
||||||
|
"password": nc.ServerKey,
|
||||||
// Multi-user mode for 2022-blake3 ciphers
|
"clients": clients,
|
||||||
clients := make([]M, len(users))
|
"network": "tcp,udp",
|
||||||
rawBuf := make([]byte, ss2022.size)
|
|
||||||
|
|
||||||
for i := range users {
|
|
||||||
u := &users[i]
|
|
||||||
// SS2022 User Key must be Base64 of fixed-length raw bytes
|
|
||||||
for j := range rawBuf {
|
|
||||||
rawBuf[j] = 0
|
|
||||||
}
|
}
|
||||||
copy(rawBuf, u.UUID)
|
} else {
|
||||||
|
// Traditional ciphers: multi-user via clients array.
|
||||||
clients[i] = M{
|
// Each entry must carry its own "method" field for Xray to parse correctly.
|
||||||
"password": base64.StdEncoding.EncodeToString(rawBuf),
|
for i := range users {
|
||||||
"email": userEmail(u.ID),
|
u := &users[i]
|
||||||
|
clients = append(clients, M{
|
||||||
|
"method": nc.Cipher,
|
||||||
|
"password": u.UUID,
|
||||||
|
"email": userEmail(u.ID),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
base["settings"] = M{
|
||||||
|
"method": nc.Cipher,
|
||||||
|
"clients": clients,
|
||||||
|
"network": "tcp,udp",
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
base["settings"] = M{
|
|
||||||
"method": nc.Cipher,
|
|
||||||
"password": nc.ServerKey,
|
|
||||||
"clients": clients,
|
|
||||||
"network": "tcp,udp",
|
|
||||||
}
|
}
|
||||||
return base
|
return base
|
||||||
}
|
}
|
||||||
@@ -426,7 +429,7 @@ func applyStreamSettings(base M, nc *panel.NodeConfig, certFile, keyFile string)
|
|||||||
case "grpc":
|
case "grpc":
|
||||||
grpcSettings := M{}
|
grpcSettings := M{}
|
||||||
if nc.NetworkSettings != nil {
|
if nc.NetworkSettings != nil {
|
||||||
if v, ok := nc.NetworkSettings["service_name"]; ok {
|
if v, ok := nc.NetworkSettings["serviceName"]; ok {
|
||||||
grpcSettings["serviceName"] = v
|
grpcSettings["serviceName"] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ func TestBuildConfig_StatsEnabled(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildConfig_Shadowsocks_SingleUser(t *testing.T) {
|
func TestBuildConfig_Shadowsocks_MultiUserTraditional(t *testing.T) {
|
||||||
nc := panel.NodeConfig{
|
nc := panel.NodeConfig{
|
||||||
Protocol: "shadowsocks",
|
Protocol: "shadowsocks",
|
||||||
ServerPort: 8388,
|
ServerPort: 8388,
|
||||||
@@ -377,12 +377,19 @@ func TestBuildConfig_Shadowsocks_SingleUser(t *testing.T) {
|
|||||||
ib := inbounds[0].(map[string]interface{})
|
ib := inbounds[0].(map[string]interface{})
|
||||||
settings := ib["settings"].(map[string]interface{})
|
settings := ib["settings"].(map[string]interface{})
|
||||||
|
|
||||||
// Single-user mode: password directly, no clients array
|
|
||||||
if settings["method"] != "aes-128-gcm" {
|
if settings["method"] != "aes-128-gcm" {
|
||||||
t.Errorf("expected method aes-128-gcm, got %v", settings["method"])
|
t.Errorf("expected method aes-128-gcm, got %v", settings["method"])
|
||||||
}
|
}
|
||||||
if settings["password"] != testUsers[0].UUID {
|
clients := settings["clients"].([]interface{})
|
||||||
t.Errorf("expected password %s, got %v", testUsers[0].UUID, settings["password"])
|
if len(clients) != len(testUsers) {
|
||||||
|
t.Fatalf("expected %d clients, got %d", len(testUsers), len(clients))
|
||||||
|
}
|
||||||
|
c0 := clients[0].(map[string]interface{})
|
||||||
|
if c0["method"] != "aes-128-gcm" {
|
||||||
|
t.Errorf("expected per-user method aes-128-gcm, got %v", c0["method"])
|
||||||
|
}
|
||||||
|
if c0["password"] != testUsers[0].UUID {
|
||||||
|
t.Errorf("expected password %s, got %v", testUsers[0].UUID, c0["password"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,7 +434,7 @@ func TestBuildConfig_SocksStats(t *testing.T) {
|
|||||||
ib := inbounds[0].(map[string]interface{})
|
ib := inbounds[0].(map[string]interface{})
|
||||||
settings := ib["settings"].(map[string]interface{})
|
settings := ib["settings"].(map[string]interface{})
|
||||||
accounts := settings["accounts"].([]interface{})
|
accounts := settings["accounts"].([]interface{})
|
||||||
|
|
||||||
if len(accounts) == 0 {
|
if len(accounts) == 0 {
|
||||||
t.Fatal("no accounts in socks config")
|
t.Fatal("no accounts in socks config")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user