mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 20:54:14 +00:00
✨ Add custom SNI for proxy
This commit is contained in:
parent
326d027f6c
commit
337ecc44c3
4 changed files with 39 additions and 13 deletions
|
|
@ -1703,7 +1703,8 @@ export class Inbound extends XrayCommonClass {
|
||||||
|
|
||||||
static applyExternalProxyTLSParams(externalProxy, params, security) {
|
static applyExternalProxyTLSParams(externalProxy, params, security) {
|
||||||
if (!externalProxy || security !== 'tls') return;
|
if (!externalProxy || security !== 'tls') return;
|
||||||
if (externalProxy.dest?.length > 0) params.set("sni", externalProxy.dest);
|
const sni = externalProxy.sni?.length > 0 ? externalProxy.sni : externalProxy.dest;
|
||||||
|
if (sni?.length > 0) params.set("sni", sni);
|
||||||
if (externalProxy.fingerprint?.length > 0) params.set("fp", externalProxy.fingerprint);
|
if (externalProxy.fingerprint?.length > 0) params.set("fp", externalProxy.fingerprint);
|
||||||
const alpn = Inbound.externalProxyAlpn(externalProxy.alpn);
|
const alpn = Inbound.externalProxyAlpn(externalProxy.alpn);
|
||||||
if (alpn.length > 0) params.set("alpn", alpn);
|
if (alpn.length > 0) params.set("alpn", alpn);
|
||||||
|
|
@ -1711,7 +1712,8 @@ export class Inbound extends XrayCommonClass {
|
||||||
|
|
||||||
static applyExternalProxyTLSObj(externalProxy, obj, security) {
|
static applyExternalProxyTLSObj(externalProxy, obj, security) {
|
||||||
if (!externalProxy || !obj || security !== 'tls') return;
|
if (!externalProxy || !obj || security !== 'tls') return;
|
||||||
if (externalProxy.dest?.length > 0) obj.sni = externalProxy.dest;
|
const sni = externalProxy.sni?.length > 0 ? externalProxy.sni : externalProxy.dest;
|
||||||
|
if (sni?.length > 0) obj.sni = sni;
|
||||||
if (externalProxy.fingerprint?.length > 0) obj.fp = externalProxy.fingerprint;
|
if (externalProxy.fingerprint?.length > 0) obj.fp = externalProxy.fingerprint;
|
||||||
const alpn = Inbound.externalProxyAlpn(externalProxy.alpn);
|
const alpn = Inbound.externalProxyAlpn(externalProxy.alpn);
|
||||||
if (alpn.length > 0) obj.alpn = alpn;
|
if (alpn.length > 0) obj.alpn = alpn;
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,7 @@ const externalProxy = computed({
|
||||||
dest: window.location.hostname,
|
dest: window.location.hostname,
|
||||||
port: inbound.value.port,
|
port: inbound.value.port,
|
||||||
remark: '',
|
remark: '',
|
||||||
|
sni: '',
|
||||||
fingerprint: '',
|
fingerprint: '',
|
||||||
alpn: [],
|
alpn: [],
|
||||||
}];
|
}];
|
||||||
|
|
@ -1685,7 +1686,7 @@ watch(() => inbound.value?.protocol, () => stampAdvancedTextFor('stream'));
|
||||||
<a-form-item label="External Proxy">
|
<a-form-item label="External Proxy">
|
||||||
<a-switch v-model:checked="externalProxy" />
|
<a-switch v-model:checked="externalProxy" />
|
||||||
<a-button v-if="externalProxy" size="small" type="primary" :style="{ marginLeft: '10px' }"
|
<a-button v-if="externalProxy" size="small" type="primary" :style="{ marginLeft: '10px' }"
|
||||||
@click="inbound.stream.externalProxy.push({ forceTls: 'same', dest: '', port: 443, remark: '', fingerprint: '', alpn: [] })">
|
@click="inbound.stream.externalProxy.push({ forceTls: 'same', dest: '', port: 443, remark: '', sni: '', fingerprint: '', alpn: [] })">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -1712,11 +1713,12 @@ watch(() => inbound.value?.protocol, () => stampAdvancedTextFor('stream'));
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
<a-input-group v-if="row.forceTls === 'tls'" compact :style="{ marginTop: '6px' }">
|
<a-input-group v-if="row.forceTls === 'tls'" compact :style="{ marginTop: '6px' }">
|
||||||
<a-select v-model:value="row.fingerprint" :style="{ width: '35%' }" placeholder="Fingerprint">
|
<a-input v-model:value="row.sni" :style="{ width: '30%' }" placeholder="SNI (defaults to host)" />
|
||||||
|
<a-select v-model:value="row.fingerprint" :style="{ width: '30%' }" placeholder="Fingerprint">
|
||||||
<a-select-option value="">Default</a-select-option>
|
<a-select-option value="">Default</a-select-option>
|
||||||
<a-select-option v-for="fp in FINGERPRINTS" :key="fp" :value="fp">{{ fp }}</a-select-option>
|
<a-select-option v-for="fp in FINGERPRINTS" :key="fp" :value="fp">{{ fp }}</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
<a-select v-model:value="row.alpn" mode="multiple" :style="{ width: '65%' }" placeholder="ALPN">
|
<a-select v-model:value="row.alpn" mode="multiple" :style="{ width: '40%' }" placeholder="ALPN">
|
||||||
<a-select-option v-for="alpn in ALPNS" :key="alpn" :value="alpn">{{ alpn }}</a-select-option>
|
<a-select-option v-for="alpn in ALPNS" :key="alpn" :value="alpn">{{ alpn }}</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
|
|
|
||||||
|
|
@ -867,8 +867,8 @@ func applyExternalProxyTLSObj(ep map[string]any, obj map[string]any, security st
|
||||||
if security != "tls" {
|
if security != "tls" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if dest, ok := ep["dest"].(string); ok && dest != "" {
|
if sni, ok := externalProxySNI(ep); ok {
|
||||||
obj["sni"] = dest
|
obj["sni"] = sni
|
||||||
}
|
}
|
||||||
if fp, ok := ep["fingerprint"].(string); ok && fp != "" {
|
if fp, ok := ep["fingerprint"].(string); ok && fp != "" {
|
||||||
obj["fp"] = fp
|
obj["fp"] = fp
|
||||||
|
|
@ -882,8 +882,8 @@ func applyExternalProxyTLSParams(ep map[string]any, params map[string]string, se
|
||||||
if security != "tls" {
|
if security != "tls" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if dest, ok := ep["dest"].(string); ok && dest != "" {
|
if sni, ok := externalProxySNI(ep); ok {
|
||||||
params["sni"] = dest
|
params["sni"] = sni
|
||||||
}
|
}
|
||||||
if fp, ok := ep["fingerprint"].(string); ok && fp != "" {
|
if fp, ok := ep["fingerprint"].(string); ok && fp != "" {
|
||||||
params["fp"] = fp
|
params["fp"] = fp
|
||||||
|
|
@ -902,8 +902,8 @@ func applyExternalProxyTLSToStream(ep map[string]any, stream map[string]any, sec
|
||||||
tlsSettings = map[string]any{}
|
tlsSettings = map[string]any{}
|
||||||
stream["tlsSettings"] = tlsSettings
|
stream["tlsSettings"] = tlsSettings
|
||||||
}
|
}
|
||||||
if dest, ok := ep["dest"].(string); ok && dest != "" {
|
if sni, ok := externalProxySNI(ep); ok {
|
||||||
tlsSettings["serverName"] = dest
|
tlsSettings["serverName"] = sni
|
||||||
}
|
}
|
||||||
if fp, ok := ep["fingerprint"].(string); ok && fp != "" {
|
if fp, ok := ep["fingerprint"].(string); ok && fp != "" {
|
||||||
tlsSettings["fingerprint"] = fp
|
tlsSettings["fingerprint"] = fp
|
||||||
|
|
@ -919,6 +919,16 @@ func applyExternalProxyTLSToStream(ep map[string]any, stream map[string]any, sec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func externalProxySNI(ep map[string]any) (string, bool) {
|
||||||
|
if sni, ok := ep["sni"].(string); ok && sni != "" {
|
||||||
|
return sni, true
|
||||||
|
}
|
||||||
|
if dest, ok := ep["dest"].(string); ok && dest != "" {
|
||||||
|
return dest, true
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
func externalProxyALPN(value any) (string, bool) {
|
func externalProxyALPN(value any) (string, bool) {
|
||||||
switch v := value.(type) {
|
switch v := value.(type) {
|
||||||
case string:
|
case string:
|
||||||
|
|
|
||||||
|
|
@ -449,14 +449,15 @@ func TestApplyExternalProxyTLSParams_UsesProxyDomainAndOverrides(t *testing.T) {
|
||||||
}
|
}
|
||||||
ep := map[string]any{
|
ep := map[string]any{
|
||||||
"dest": "proxy.example.com",
|
"dest": "proxy.example.com",
|
||||||
|
"sni": "tls.example.com",
|
||||||
"fingerprint": "chrome",
|
"fingerprint": "chrome",
|
||||||
"alpn": []any{"h3", "h2"},
|
"alpn": []any{"h3", "h2"},
|
||||||
}
|
}
|
||||||
|
|
||||||
applyExternalProxyTLSParams(ep, params, "tls")
|
applyExternalProxyTLSParams(ep, params, "tls")
|
||||||
|
|
||||||
if params["sni"] != "proxy.example.com" {
|
if params["sni"] != "tls.example.com" {
|
||||||
t.Fatalf("sni = %q, want proxy.example.com", params["sni"])
|
t.Fatalf("sni = %q, want tls.example.com", params["sni"])
|
||||||
}
|
}
|
||||||
if params["fp"] != "chrome" {
|
if params["fp"] != "chrome" {
|
||||||
t.Fatalf("fp = %q, want chrome", params["fp"])
|
t.Fatalf("fp = %q, want chrome", params["fp"])
|
||||||
|
|
@ -466,6 +467,17 @@ func TestApplyExternalProxyTLSParams_UsesProxyDomainAndOverrides(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApplyExternalProxyTLSParams_FallsBackToDestSNI(t *testing.T) {
|
||||||
|
params := map[string]string{"security": "tls"}
|
||||||
|
ep := map[string]any{"dest": "proxy.example.com"}
|
||||||
|
|
||||||
|
applyExternalProxyTLSParams(ep, params, "tls")
|
||||||
|
|
||||||
|
if params["sni"] != "proxy.example.com" {
|
||||||
|
t.Fatalf("sni = %q, want proxy.example.com", params["sni"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestApplyExternalProxyTLSParams_DoesNotApplyForNone(t *testing.T) {
|
func TestApplyExternalProxyTLSParams_DoesNotApplyForNone(t *testing.T) {
|
||||||
params := map[string]string{
|
params := map[string]string{
|
||||||
"security": "none",
|
"security": "none",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue