mirror of
https://github.com/alireza0/x-ui.git
synced 2026-03-14 05:23:09 +00:00
tls options
This commit is contained in:
@@ -24,6 +24,9 @@ const SSMethods = {
|
||||
CHACHA20_POLY1305: 'chacha20-poly1305',
|
||||
AES_256_GCM: 'aes-256-gcm',
|
||||
AES_128_GCM: 'aes-128-gcm',
|
||||
BLAKE3_AES_128_GCM: '2022-blake3-aes-128-gcm',
|
||||
BLAKE3_AES_256_GCM: '2022-blake3-aes-256-gcm',
|
||||
BLAKE3_CHACHA20_POLY1305: '2022-blake3-chacha20-poly1305',
|
||||
};
|
||||
|
||||
const RULE_IP = {
|
||||
@@ -40,17 +43,64 @@ const RULE_DOMAIN = {
|
||||
SPEEDTEST: 'geosite:speedtest',
|
||||
};
|
||||
|
||||
const FLOW_CONTROL = {
|
||||
const XTLS_FLOW_CONTROL = {
|
||||
ORIGIN: "xtls-rprx-origin",
|
||||
DIRECT: "xtls-rprx-direct",
|
||||
};
|
||||
|
||||
const TLS_FLOW_CONTROL = {
|
||||
VISION: "xtls-rprx-vision",
|
||||
};
|
||||
|
||||
const TLS_VERSION_OPTION = {
|
||||
TLS10: "1.0",
|
||||
TLS11: "1.1",
|
||||
TLS12: "1.2",
|
||||
TLS13: "1.3",
|
||||
}
|
||||
|
||||
const TLS_CIPHER_OPTION = {
|
||||
RSA_AES_128_CBC: "TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||
RSA_AES_256_CBC: "TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||
RSA_AES_128_GCM: "TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||
RSA_AES_256_GCM: "TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||
AES_128_GCM: "TLS_AES_128_GCM_SHA256",
|
||||
AES_256_GCM: "TLS_AES_256_GCM_SHA384",
|
||||
CHACHA20_POLY1305: "TLS_CHACHA20_POLY1305_SHA256",
|
||||
ECDHE_ECDSA_AES_128_CBC: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||
ECDHE_ECDSA_AES_256_CBC: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||
ECDHE_RSA_AES_128_CBC: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
ECDHE_RSA_AES_256_CBC: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
ECDHE_ECDSA_AES_128_GCM: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
ECDHE_ECDSA_AES_256_GCM: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
ECDHE_RSA_AES_128_GCM: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
ECDHE_RSA_AES_256_GCM: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
ECDHE_ECDSA_CHACHA20_POLY1305: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||
ECDHE_RSA_CHACHA20_POLY1305: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||
};
|
||||
|
||||
const UTLS_FINGERPRINT = {
|
||||
UTLS_CHROME: "chrome",
|
||||
UTLS_FIREFOX: "firefox",
|
||||
UTLS_SAFARI: "safari",
|
||||
UTLS_IOS: "ios",
|
||||
UTLS_android: "android",
|
||||
UTLS_EDGE: "edge",
|
||||
UTLS_360: "360",
|
||||
UTLS_QQ: "qq",
|
||||
UTLS_RANDOM: "random",
|
||||
UTLS_RANDOMIZED: "randomized",
|
||||
};
|
||||
|
||||
Object.freeze(Protocols);
|
||||
Object.freeze(VmessMethods);
|
||||
Object.freeze(SSMethods);
|
||||
Object.freeze(RULE_IP);
|
||||
Object.freeze(RULE_DOMAIN);
|
||||
Object.freeze(FLOW_CONTROL);
|
||||
Object.freeze(XTLS_FLOW_CONTROL);
|
||||
Object.freeze(TLS_FLOW_CONTROL);
|
||||
Object.freeze(TLS_VERSION_OPTION);
|
||||
Object.freeze(TLS_CIPHER_OPTION);
|
||||
|
||||
class XrayCommonClass {
|
||||
|
||||
@@ -417,9 +467,16 @@ class GrpcStreamSettings extends XrayCommonClass {
|
||||
|
||||
class TlsStreamSettings extends XrayCommonClass {
|
||||
constructor(serverName='',
|
||||
certificates=[new TlsStreamSettings.Cert()], alpn=[]) {
|
||||
minVersion = TLS_VERSION_OPTION.TLS12,
|
||||
maxVersion = TLS_VERSION_OPTION.TLS13,
|
||||
cipherSuites = '',
|
||||
certificates=[new TlsStreamSettings.Cert()],
|
||||
alpn=["h2", "http/1.1"]) {
|
||||
super();
|
||||
this.server = serverName;
|
||||
this.minVersion = minVersion;
|
||||
this.maxVersion = maxVersion;
|
||||
this.cipherSuites = cipherSuites;
|
||||
this.certs = certificates;
|
||||
this.alpn = alpn;
|
||||
}
|
||||
@@ -440,6 +497,9 @@ class TlsStreamSettings extends XrayCommonClass {
|
||||
|
||||
return new TlsStreamSettings(
|
||||
json.serverName,
|
||||
json.minVersion,
|
||||
json.maxVersion,
|
||||
json.cipherSuites,
|
||||
certs,
|
||||
json.alpn
|
||||
);
|
||||
@@ -448,6 +508,9 @@ class TlsStreamSettings extends XrayCommonClass {
|
||||
toJson() {
|
||||
return {
|
||||
serverName: this.server,
|
||||
minVersion: this.minVersion,
|
||||
maxVersion: this.maxVersion,
|
||||
cipherSuites: this.cipherSuites,
|
||||
certificates: TlsStreamSettings.toJsonArray(this.certs),
|
||||
alpn: this.alpn
|
||||
};
|
||||
@@ -710,7 +773,7 @@ class Inbound extends XrayCommonClass {
|
||||
case Protocols.VLESS:
|
||||
return this.settings.vlesses[0].flow;
|
||||
case Protocols.TROJAN:
|
||||
return this.settings.clients[0].flow;
|
||||
return this.settings.trojans[0].flow;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
@@ -741,7 +804,7 @@ class Inbound extends XrayCommonClass {
|
||||
get password() {
|
||||
switch (this.protocol) {
|
||||
case Protocols.TROJAN:
|
||||
return this.settings.clients[0].password;
|
||||
return this.settings.trojans[0].password;
|
||||
case Protocols.SHADOWSOCKS:
|
||||
return this.settings.password;
|
||||
case Protocols.SOCKS:
|
||||
@@ -857,6 +920,19 @@ class Inbound extends XrayCommonClass {
|
||||
}
|
||||
}
|
||||
|
||||
//this is used for xtls-rprx-vison
|
||||
canEnableTlsFlow() {
|
||||
if ((this.stream.security === 'tls') && (this.network === "tcp")) {
|
||||
switch (this.protocol) {
|
||||
case Protocols.VLESS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
canSetTls() {
|
||||
return this.canEnableTls();
|
||||
}
|
||||
@@ -876,6 +952,7 @@ class Inbound extends XrayCommonClass {
|
||||
switch (this.protocol) {
|
||||
case Protocols.VMESS:
|
||||
case Protocols.VLESS:
|
||||
case Protocols.TROJAN:
|
||||
case Protocols.SHADOWSOCKS:
|
||||
return true;
|
||||
default:
|
||||
@@ -1032,6 +1109,7 @@ class Inbound extends XrayCommonClass {
|
||||
address = this.stream.tls.server;
|
||||
params.set("sni", address);
|
||||
}
|
||||
params.set("flow", this.settings.vlesses[clientIndex].flow);
|
||||
}
|
||||
|
||||
if (this.xtls) {
|
||||
@@ -1053,13 +1131,87 @@ class Inbound extends XrayCommonClass {
|
||||
if (!ObjectUtil.isEmpty(server)) {
|
||||
address = server;
|
||||
}
|
||||
return 'ss://' + safeBase64(settings.method + ':' + settings.password + '@' + address + ':' + this.port)
|
||||
+ '#' + encodeURIComponent(remark);
|
||||
if (settings.method == SSMethods.BLAKE3_AES_128_GCM || settings.method == SSMethods.BLAKE3_AES_256_GCM || settings.method == SSMethods.BLAKE3_CHACHA20_POLY1305) {
|
||||
return `ss://${settings.method}:${settings.password}@${address}:${this.port}#${encodeURIComponent(remark)}`;
|
||||
} else {
|
||||
return 'ss://' + safeBase64(settings.method + ':' + settings.password + '@' + address + ':' + this.port)
|
||||
+ '#' + encodeURIComponent(remark);
|
||||
}
|
||||
}
|
||||
|
||||
genTrojanLink(address='', remark='', clientIndex=0) {
|
||||
genTrojanLink(address = '', remark = '', clientIndex = 0) {
|
||||
let settings = this.settings;
|
||||
return `trojan://${settings.trojans[clientIndex].password}@${address}:${this.port}#${encodeURIComponent(remark)}`;
|
||||
const port = this.port;
|
||||
const type = this.stream.network;
|
||||
const params = new Map();
|
||||
params.set("type", this.stream.network);
|
||||
if (this.xtls) {
|
||||
params.set("security", "xtls");
|
||||
} else {
|
||||
params.set("security", this.stream.security);
|
||||
}
|
||||
switch (type) {
|
||||
case "tcp":
|
||||
const tcp = this.stream.tcp;
|
||||
if (tcp.type === 'http') {
|
||||
const request = tcp.request;
|
||||
params.set("path", request.path.join(','));
|
||||
const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host');
|
||||
if (index >= 0) {
|
||||
const host = request.headers[index].value;
|
||||
params.set("host", host);
|
||||
}
|
||||
params.set("headerType", 'http');
|
||||
}
|
||||
break;
|
||||
case "kcp":
|
||||
const kcp = this.stream.kcp;
|
||||
params.set("headerType", kcp.type);
|
||||
params.set("seed", kcp.seed);
|
||||
break;
|
||||
case "ws":
|
||||
const ws = this.stream.ws;
|
||||
params.set("path", ws.path);
|
||||
const index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host');
|
||||
if (index >= 0) {
|
||||
const host = ws.headers[index].value;
|
||||
params.set("host", host);
|
||||
}
|
||||
break;
|
||||
case "http":
|
||||
const http = this.stream.http;
|
||||
params.set("path", http.path);
|
||||
params.set("host", http.host);
|
||||
break;
|
||||
case "quic":
|
||||
const quic = this.stream.quic;
|
||||
params.set("quicSecurity", quic.security);
|
||||
params.set("key", quic.key);
|
||||
params.set("headerType", quic.type);
|
||||
break;
|
||||
case "grpc":
|
||||
const grpc = this.stream.grpc;
|
||||
params.set("serviceName", grpc.serviceName);
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.stream.security === 'tls') {
|
||||
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
|
||||
address = this.stream.tls.server;
|
||||
params.set("sni", address);
|
||||
}
|
||||
params.set("flow", this.settings.trojans[clientIndex].flow);
|
||||
}
|
||||
if (this.xtls) {
|
||||
params.set("flow", this.settings.trojans[clientIndex].flow);
|
||||
}
|
||||
const link = `trojan://${settings.trojans[clientIndex].password}@${address}:${this.port}#${encodeURIComponent(remark)}`;
|
||||
const url = new URL(link);
|
||||
for (const [key, value] of params) {
|
||||
url.searchParams.set(key, value)
|
||||
}
|
||||
url.hash = encodeURIComponent(remark);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
genLink(address='', remark='', clientIndex=0) {
|
||||
@@ -1279,12 +1431,13 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
|
||||
};
|
||||
Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
|
||||
|
||||
constructor(id=RandomUtil.randomUUID(), flow=FLOW_CONTROL.DIRECT, email=RandomUtil.randomText(), totalGB=0, expiryTime='') {
|
||||
constructor(id=RandomUtil.randomUUID(), flow='', email=RandomUtil.randomText(), totalGB=0, fingerprint = UTLS_FINGERPRINT.UTLS_CHROME, expiryTime='') {
|
||||
super();
|
||||
this.id = id;
|
||||
this.flow = flow;
|
||||
this.email = email;
|
||||
this.totalGB = totalGB;
|
||||
this.fingerprint = fingerprint;
|
||||
this.expiryTime = expiryTime;
|
||||
|
||||
}
|
||||
@@ -1295,6 +1448,7 @@ Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
|
||||
json.flow,
|
||||
json.email,
|
||||
json.totalGB,
|
||||
json.fingerprint,
|
||||
json.expiryTime,
|
||||
|
||||
);
|
||||
@@ -1393,7 +1547,7 @@ Inbound.TrojanSettings = class extends Inbound.Settings {
|
||||
}
|
||||
};
|
||||
Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
|
||||
constructor(password=RandomUtil.randomSeq(10), flow=FLOW_CONTROL.DIRECT, email=RandomUtil.randomText(), totalGB=0, expiryTime='') {
|
||||
constructor(password=RandomUtil.randomSeq(10), flow='', email=RandomUtil.randomText(), totalGB=0, expiryTime='') {
|
||||
super();
|
||||
this.password = password;
|
||||
this.flow = flow;
|
||||
@@ -1488,8 +1642,8 @@ Inbound.TrojanSettings.Fallback = class extends XrayCommonClass {
|
||||
|
||||
Inbound.ShadowsocksSettings = class extends Inbound.Settings {
|
||||
constructor(protocol,
|
||||
method=SSMethods.AES_256_GCM,
|
||||
password=RandomUtil.randomSeq(10),
|
||||
method=SSMethods.BLAKE3_AES_256_GCM,
|
||||
password=RandomUtil.randomSeq(44),
|
||||
network='tcp,udp'
|
||||
) {
|
||||
super(protocol);
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<a-form-item v-if="inbound.xtls" label="flow">
|
||||
<a-select v-model="trojan.flow" style="width: 150px">
|
||||
<a-select-option value="">{{ i18n "none" }}</a-select-option>
|
||||
<a-select-option v-for="key in FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
@@ -68,7 +68,7 @@
|
||||
</a-tooltip>
|
||||
<a-tag color="blue">[[ sizeFormat(getUpStats(trojan.email)) ]] / [[ sizeFormat(getDownStats(trojan.email)) ]]</a-tag>
|
||||
<a-tag v-if="trojan._totalGB > 0" color="red">used : [[ sizeFormat(getUpStats(trojan.email) + getDownStats(trojan.email)) ]]</a-tag>
|
||||
<a-tag v-show="inbound.settings.vmesses.length > 1" @click="removeClient(index, inbound.settings.vmesses)">
|
||||
<a-tag v-show="inbound.settings.trojans.length > 1" @click="removeClient(index, inbound.settings.trojans)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22" width="22" height="22" class="mt-2 cursor-pointer">
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path fill="#EC4899"
|
||||
@@ -79,7 +79,7 @@
|
||||
</a-form>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
<a-tag @click="addClient(inbound.protocol, inbound.settings.vmesses)">
|
||||
<a-tag @click="addClient(inbound.protocol, inbound.settings.trojans)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" class="ml-2 cursor-pointer">
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path fill="green"
|
||||
|
||||
@@ -27,12 +27,23 @@
|
||||
<a-form-item label="id">
|
||||
<a-input v-model.trim="vless.id"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="inbound.xtls" label="flow">
|
||||
<a-select v-model="vless.flow" style="width: 150px">
|
||||
<a-select-option value="">{{ i18n "none" }}</a-select-option>
|
||||
<a-select-option v-for="key in FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||
<a-form-item v-if="inbound.xtls" label="flow">
|
||||
<a-select v-model="inbound.settings.vlesses[index].flow" style="width: 150px">
|
||||
<a-select-option value="" selected>none</a-select-option>
|
||||
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item v-else-if="inbound.canEnableTlsFlow()" label="flow" layout="inline">
|
||||
<a-select v-model="inbound.settings.vlesses[index].flow" style="width: 150px">
|
||||
<a-select-option value="" selected>none</a-select-option>
|
||||
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="inbound.tls" label="utls" layout="inline">
|
||||
<a-select v-model="inbound.settings.vlesses[index].fingerprint" label="utls" style="width: 150px">
|
||||
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
<span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
||||
@@ -70,7 +81,7 @@
|
||||
<a-tag color="blue">[[ sizeFormat(getUpStats(vless.email)) ]] / [[ sizeFormat(getDownStats(vless.email)) ]]</a-tag>
|
||||
<a-tag v-if="vless._totalGB > 0" color="red">used : [[ sizeFormat(getUpStats(vless.email) + getDownStats(vless.email)) ]]</a-tag>
|
||||
|
||||
<a-tag v-show="inbound.settings.vmesses.length > 1" @click="removeClient(index, inbound.settings.vmesses)">
|
||||
<a-tag v-show="inbound.settings.vlesses.length > 1" @click="removeClient(index, inbound.settings.vlesses)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22" width="22" height="22" class="mt-2 cursor-pointer">
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path fill="#EC4899"
|
||||
@@ -81,7 +92,7 @@
|
||||
</a-form>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
<a-tag @click="addClient(inbound.protocol, inbound.settings.vmesses)">
|
||||
<a-tag @click="addClient(inbound.protocol, inbound.settings.vlesses)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" class="ml-2 cursor-pointer">
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path fill="green"
|
||||
|
||||
@@ -11,8 +11,23 @@
|
||||
</a-form>
|
||||
|
||||
<!-- tls settings -->
|
||||
<a-form v-if="inbound.tls || inbound.xtls"
|
||||
layout="inline">
|
||||
<a-form v-if="inbound.tls || inbound.xtls" layout="inline">
|
||||
<a-form-item label="MinVersion">
|
||||
<a-select v-model="inbound.stream.tls.minVersion" style="width: 60px">
|
||||
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="MaxVersion">
|
||||
<a-select v-model="inbound.stream.tls.maxVersion" style="width: 60px">
|
||||
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="CipherSuites">
|
||||
<a-select v-model="inbound.stream.tls.cipherSuites" style="width: 300px">
|
||||
<a-select-option value="">auto</a-select-option>
|
||||
<a-select-option v-for="key in TLS_CIPHER_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "domainName" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tls.server"></a-input>
|
||||
</a-form-item>
|
||||
@@ -20,8 +35,7 @@
|
||||
<a-input v-model.trim="inbound.stream.tls.alpn"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "certificate" }}'>
|
||||
<a-radio-group v-model="inbound.stream.tls.certs[0].useFile"
|
||||
button-style="solid">
|
||||
<a-radio-group v-model="inbound.stream.tls.certs[0].useFile" button-style="solid">
|
||||
<a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button>
|
||||
<a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
@@ -36,18 +50,11 @@
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.publicKeyContent" }}'>
|
||||
<a-input type="textarea" :rows="2"
|
||||
v-model="inbound.stream.tls.certs[0].cert"></a-input>
|
||||
<a-input type="textarea" :rows="2" v-model="inbound.stream.tls.certs[0].cert"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.keyContent" }}'>
|
||||
<a-input type="textarea" :rows="2"
|
||||
v-model="inbound.stream.tls.certs[0].key"></a-input>
|
||||
<a-input type="textarea" :rows="2" v-model="inbound.stream.tls.certs[0].key"></a-input>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</a-form>
|
||||
<a-form layout="inline" v-else-if = "inbound.stream.network === 'tcp' ">
|
||||
<a-form-item label="tcp-acceptProxyProtocol">
|
||||
<a-switch v-model="inbound.stream.tcp.acceptProxyProtocol"></a-switch>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user