feat(xray): add hysteria v2 inbound and VLESS encryption support

This commit is contained in:
xboard
2026-03-30 16:55:02 +08:00
parent 19aa20be87
commit f7ce8f22d4
5 changed files with 75 additions and 4 deletions
+4
View File
@@ -460,6 +460,10 @@ func buildVMess(base M, nc *panel.NodeConfig, users []panel.User, certFile, keyF
} }
func buildVLESS(base M, nc *panel.NodeConfig, users []panel.User, certFile, keyFile string) M { func buildVLESS(base M, nc *panel.NodeConfig, users []panel.User, certFile, keyFile string) M {
if nc.Decryption != "" && nc.Decryption != "none" {
nlog.Core().Warn("sing-box does not support VLESS encryption (decryption), use xray kernel for this feature")
}
base["type"] = "vless" base["type"] = "vless"
userList := make([]M, 0, len(users)) userList := make([]M, 0, len(users))
+68 -2
View File
@@ -72,7 +72,7 @@ func buildConfig(kcfg config.KernelConfig, nc *panel.NodeConfig, users []panel.U
} else { } else {
nlog.Core().Warn("xray: unsupported protocol, no inbound configured — node will not accept connections", nlog.Core().Warn("xray: unsupported protocol, no inbound configured — node will not accept connections",
"protocol", nc.Protocol, "protocol", nc.Protocol,
"supported", "vmess, vless, trojan, shadowsocks, socks, http") "supported", "vmess, vless, trojan, shadowsocks, hysteria, socks, http")
} }
// Merge panel routes and static config routes // Merge panel routes and static config routes
@@ -225,6 +225,8 @@ func buildInbound(nc *panel.NodeConfig, users []panel.User, certFile, keyFile st
return buildSocks(base, users) return buildSocks(base, users)
case "http": case "http":
return buildHTTP(base, nc, users, certFile, keyFile) return buildHTTP(base, nc, users, certFile, keyFile)
case "hysteria":
return buildHysteria(base, nc, users, certFile, keyFile)
default: default:
return nil return nil
} }
@@ -263,9 +265,13 @@ func buildVLESS(base M, nc *panel.NodeConfig, users []panel.User, certFile, keyF
} }
clients = append(clients, client) clients = append(clients, client)
} }
decryption := "none"
if nc.Decryption != "" {
decryption = nc.Decryption
}
base["settings"] = M{ base["settings"] = M{
"clients": clients, "clients": clients,
"decryption": "none", "decryption": decryption,
} }
applyStreamSettings(base, nc, certFile, keyFile) applyStreamSettings(base, nc, certFile, keyFile)
@@ -392,6 +398,66 @@ func buildHTTP(base M, nc *panel.NodeConfig, users []panel.User, certFile, keyFi
return base return base
} }
// buildHysteria creates a Hysteria v2 inbound for xray-core.
func buildHysteria(base M, nc *panel.NodeConfig, users []panel.User, certFile, keyFile string) M {
if nc.Version != 2 {
nlog.Core().Warn("xray: only supports hysteria v2, skipping v1 inbound")
return nil
}
clients := make([]M, 0, len(users))
for _, u := range users {
clients = append(clients, M{
"auth": u.UUID,
"email": userEmail(u.ID),
})
}
base["settings"] = M{"clients": clients}
ss := M{
"network": "hysteria",
"hysteriaSettings": M{
"version": nc.Version,
"udpIdleTimeout": 60,
},
}
if nc.UpMbps > 0 || nc.DownMbps > 0 {
quicParams := M{}
if nc.UpMbps > 0 {
quicParams["brutalUp"] = fmt.Sprintf("%d mbps", nc.UpMbps)
}
if nc.DownMbps > 0 {
quicParams["brutalDown"] = fmt.Sprintf("%d mbps", nc.DownMbps)
}
ss["finalMask"] = M{
"quicParams": quicParams,
}
}
if certFile != "" && keyFile != "" {
ss["security"] = "tls"
ss["tlsSettings"] = M{
"certificates": []M{
{
"certificateFile": certFile,
"keyFile": keyFile,
},
},
"alpn": []string{"h3"},
}
} else {
nlog.Core().Warn("hysteria requires TLS certificate files; configure cert_mode (self, file, http, dns, or content)")
}
if nc.GetProxyProtocol() {
ss["sockopt"] = M{"acceptProxyProtocol": true}
}
base["streamSettings"] = ss
return base
}
func applyStreamSettings(base M, nc *panel.NodeConfig, certFile, keyFile string) { func applyStreamSettings(base M, nc *panel.NodeConfig, certFile, keyFile string) {
ss := M{} ss := M{}
+1 -1
View File
@@ -85,7 +85,7 @@ func (x *Xray) Name() string { return "xray" }
func (x *Xray) Protocols() []string { func (x *Xray) Protocols() []string {
return []string{ return []string{
"vmess", "vless", "trojan", "shadowsocks", "vmess", "vless", "trojan", "shadowsocks",
"socks", "http", "dokodemo-door", "hysteria", "socks", "http", "dokodemo-door",
} }
} }
+1
View File
@@ -92,6 +92,7 @@ type NodeConfig struct {
// VMess / VLESS // VMess / VLESS
TLS int `json:"tls,omitempty"` TLS int `json:"tls,omitempty"`
Flow string `json:"flow,omitempty"` Flow string `json:"flow,omitempty"`
Decryption string `json:"decryption,omitempty"`
TLSSettings map[string]interface{} `json:"tls_settings,omitempty"` TLSSettings map[string]interface{} `json:"tls_settings,omitempty"`
// Trojan // Trojan
+1 -1
View File
@@ -192,7 +192,7 @@ func (w *WSClient) connect(ctx context.Context) error {
q.Set("node_id", strconv.Itoa(w.nodeID)) q.Set("node_id", strconv.Itoa(w.nodeID))
u.RawQuery = q.Encode() u.RawQuery = q.Encode()
nlog.Core().Debug("ws connecting", "url", u.Host) nlog.Core().Debug("ws connecting", "url", u.String())
dialer := websocket.Dialer{ dialer := websocket.Dialer{
HandshakeTimeout: w.cfg.HandshakeTimeout, HandshakeTimeout: w.cfg.HandshakeTimeout,