mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-09-12 21:20:07 +00:00
VlessEnc
This commit is contained in:
parent
9b9746ca6b
commit
a15c8f8bfe
5 changed files with 123 additions and 43 deletions
|
@ -1301,6 +1301,7 @@ class Inbound extends XrayCommonClass {
|
||||||
const security = forceTls == 'same' ? this.stream.security : forceTls;
|
const security = forceTls == 'same' ? this.stream.security : forceTls;
|
||||||
const params = new Map();
|
const params = new Map();
|
||||||
params.set("type", this.stream.network);
|
params.set("type", this.stream.network);
|
||||||
|
params.set("encryption", this.settings.encryption);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "tcp":
|
case "tcp":
|
||||||
const tcp = this.stream.tcp;
|
const tcp = this.stream.tcp;
|
||||||
|
@ -1859,13 +1860,15 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
|
||||||
constructor(
|
constructor(
|
||||||
protocol,
|
protocol,
|
||||||
vlesses = [new Inbound.VLESSSettings.VLESS()],
|
vlesses = [new Inbound.VLESSSettings.VLESS()],
|
||||||
decryption = 'none',
|
decryption = "none",
|
||||||
fallbacks = []
|
fallbacks = []
|
||||||
) {
|
) {
|
||||||
super(protocol);
|
super(protocol);
|
||||||
this.vlesses = vlesses;
|
this.vlesses = vlesses;
|
||||||
this.decryption = decryption;
|
this.decryption = decryption;
|
||||||
this.fallbacks = fallbacks;
|
this.fallbacks = fallbacks;
|
||||||
|
this.selectedAuth = "X25519, not Post-Quantum";
|
||||||
|
this.encryption = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
addFallback() {
|
addFallback() {
|
||||||
|
@ -1876,22 +1879,42 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
|
||||||
this.fallbacks.splice(index, 1);
|
this.fallbacks.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// decryption should be set to static value
|
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
return new Inbound.VLESSSettings(
|
const obj = new Inbound.VLESSSettings(
|
||||||
Protocols.VLESS,
|
Protocols.VLESS,
|
||||||
json.clients.map(client => Inbound.VLESSSettings.VLESS.fromJson(client)),
|
(json.clients || []).map(client => Inbound.VLESSSettings.VLESS.fromJson(client)),
|
||||||
json.decryption || 'none',
|
json.decryption || "none",
|
||||||
Inbound.VLESSSettings.Fallback.fromJson(json.fallbacks),);
|
Inbound.VLESSSettings.Fallback.fromJson(json.fallbacks || [])
|
||||||
|
);
|
||||||
|
|
||||||
|
obj.encryption = json.encryption || "";
|
||||||
|
obj.selectedAuth = json.selectedAuth || "X25519, not Post-Quantum";
|
||||||
|
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
toJson() {
|
toJson() {
|
||||||
return {
|
const json = {
|
||||||
clients: Inbound.VLESSSettings.toJsonArray(this.vlesses),
|
clients: Inbound.VLESSSettings.toJsonArray(this.vlesses),
|
||||||
decryption: this.decryption,
|
|
||||||
fallbacks: Inbound.VLESSSettings.toJsonArray(this.fallbacks),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this.decryption) {
|
||||||
|
json.decryption = this.decryption;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.encryption) {
|
||||||
|
json.encryption = this.encryption;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.fallbacks && this.fallbacks.length > 0) {
|
||||||
|
json.fallbacks = Inbound.VLESSSettings.toJsonArray(this.fallbacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
|
Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
|
||||||
|
|
|
@ -55,7 +55,7 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) {
|
||||||
g.POST("/getNewX25519Cert", a.getNewX25519Cert)
|
g.POST("/getNewX25519Cert", a.getNewX25519Cert)
|
||||||
g.POST("/getNewmldsa65", a.getNewmldsa65)
|
g.POST("/getNewmldsa65", a.getNewmldsa65)
|
||||||
g.POST("/getNewEchCert", a.getNewEchCert)
|
g.POST("/getNewEchCert", a.getNewEchCert)
|
||||||
g.POST("/getNewmlkem768", a.getNewmlkem768)
|
g.POST("/getNewVlessEnc", a.getNewVlessEnc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *ServerController) refreshStatus() {
|
func (a *ServerController) refreshStatus() {
|
||||||
|
@ -268,8 +268,8 @@ func (a *ServerController) getNewEchCert(c *gin.Context) {
|
||||||
jsonObj(c, cert, nil)
|
jsonObj(c, cert, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *ServerController) getNewmlkem768(c *gin.Context) {
|
func (a *ServerController) getNewVlessEnc(c *gin.Context) {
|
||||||
out, err := a.serverService.GetNewmlkem768()
|
out, err := a.serverService.GetNewVlessEnc()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.getNewmlkem768Error"), err)
|
jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.getNewmlkem768Error"), err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -18,6 +18,26 @@
|
||||||
</table>
|
</table>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
|
<template>
|
||||||
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
|
<a-form-item label="Authentication">
|
||||||
|
<a-select v-model="inbound.settings.selectedAuth" @change="getNewVlessEnc"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option value="X25519, not Post-Quantum">X25519 (not Post-Quantum)</a-select-option>
|
||||||
|
<a-select-option value="ML-KEM-768, Post-Quantum">ML-KEM-768 (Post-Quantum)</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="decryption">
|
||||||
|
<a-input v-model.trim="inbound.settings.decryption"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="encryption">
|
||||||
|
<a-input v-model="inbound.settings.encryption" disabled></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label=" ">
|
||||||
|
<a-button type="primary" icon="import" @click="getNewVlessEnc">Get New keys</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
<template v-if="inbound.isTcp">
|
<template v-if="inbound.isTcp">
|
||||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-form-item label="Fallbacks">
|
<a-form-item label="Fallbacks">
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
{{define "modals/inboundModal"}}
|
{{define "modals/inboundModal"}}
|
||||||
<a-modal id="inbound-modal" v-model="inModal.visible" :title="inModal.title"
|
<a-modal id="inbound-modal" v-model="inModal.visible" :title="inModal.title" :dialog-style="{ top: '20px' }"
|
||||||
:dialog-style="{ top: '20px' }" @ok="inModal.ok"
|
@ok="inModal.ok" :confirm-loading="inModal.confirmLoading" :closable="true" :mask-closable="false"
|
||||||
:confirm-loading="inModal.confirmLoading" :closable="true" :mask-closable="false"
|
:class="themeSwitcher.currentTheme" :ok-text="inModal.okText" cancel-text='{{ i18n "close" }}'>
|
||||||
:class="themeSwitcher.currentTheme"
|
|
||||||
:ok-text="inModal.okText" cancel-text='{{ i18n "close" }}'>
|
|
||||||
{{template "form/inbound"}}
|
{{template "form/inbound"}}
|
||||||
</a-modal>
|
</a-modal>
|
||||||
<script>
|
<script>
|
||||||
|
@ -20,7 +18,7 @@
|
||||||
ok() {
|
ok() {
|
||||||
ObjectUtil.execute(inModal.confirm, inModal.inbound, inModal.dbInbound);
|
ObjectUtil.execute(inModal.confirm, inModal.inbound, inModal.dbInbound);
|
||||||
},
|
},
|
||||||
show({ title = '', okText = '{{ i18n "sure" }}', inbound = null, dbInbound = null, confirm = (inbound, dbInbound) => {}, isEdit = false }) {
|
show({ title = '', okText = '{{ i18n "sure" }}', inbound = null, dbInbound = null, confirm = (inbound, dbInbound) => { }, isEdit = false }) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.okText = okText;
|
this.okText = okText;
|
||||||
if (inbound) {
|
if (inbound) {
|
||||||
|
@ -41,7 +39,7 @@
|
||||||
inModal.visible = false;
|
inModal.visible = false;
|
||||||
inModal.loading(false);
|
inModal.loading(false);
|
||||||
},
|
},
|
||||||
loading(loading=true) {
|
loading(loading = true) {
|
||||||
inModal.confirmLoading = loading;
|
inModal.confirmLoading = loading;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -107,7 +105,7 @@
|
||||||
this.inModal.inbound.settings.password = RandomUtil.randomShadowsocksPassword(this.inModal.inbound.settings.method)
|
this.inModal.inbound.settings.password = RandomUtil.randomShadowsocksPassword(this.inModal.inbound.settings.method)
|
||||||
|
|
||||||
if (this.inModal.inbound.isSSMultiUser) {
|
if (this.inModal.inbound.isSSMultiUser) {
|
||||||
if (this.inModal.inbound.settings.shadowsockses.length ==0){
|
if (this.inModal.inbound.settings.shadowsockses.length == 0) {
|
||||||
this.inModal.inbound.settings.shadowsockses = [new Inbound.ShadowsocksSettings.Shadowsocks()];
|
this.inModal.inbound.settings.shadowsockses = [new Inbound.ShadowsocksSettings.Shadowsocks()];
|
||||||
}
|
}
|
||||||
if (!this.inModal.inbound.isSS2022) {
|
if (!this.inModal.inbound.isSS2022) {
|
||||||
|
@ -123,7 +121,7 @@
|
||||||
client.password = RandomUtil.randomShadowsocksPassword(this.inModal.inbound.settings.method)
|
client.password = RandomUtil.randomShadowsocksPassword(this.inModal.inbound.settings.method)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
if (this.inModal.inbound.settings.shadowsockses.length > 0){
|
if (this.inModal.inbound.settings.shadowsockses.length > 0) {
|
||||||
this.inModal.inbound.settings.shadowsockses = [];
|
this.inModal.inbound.settings.shadowsockses = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,7 +152,7 @@
|
||||||
},
|
},
|
||||||
async getNewEchCert() {
|
async getNewEchCert() {
|
||||||
inModal.loading(true);
|
inModal.loading(true);
|
||||||
const msg = await HttpUtil.post('/server/getNewEchCert', {sni: inModal.inbound.stream.tls.sni});
|
const msg = await HttpUtil.post('/server/getNewEchCert', { sni: inModal.inbound.stream.tls.sni });
|
||||||
inModal.loading(false);
|
inModal.loading(false);
|
||||||
if (!msg.success) {
|
if (!msg.success) {
|
||||||
return;
|
return;
|
||||||
|
@ -162,16 +160,32 @@
|
||||||
inModal.inbound.stream.tls.echServerKeys = msg.obj.echServerKeys;
|
inModal.inbound.stream.tls.echServerKeys = msg.obj.echServerKeys;
|
||||||
inModal.inbound.stream.tls.settings.echConfigList = msg.obj.echConfigList;
|
inModal.inbound.stream.tls.settings.echConfigList = msg.obj.echConfigList;
|
||||||
},
|
},
|
||||||
async getNewmlkem768() {
|
async getNewVlessEnc() {
|
||||||
inModal.loading(true);
|
inModal.loading(true);
|
||||||
const msg = await HttpUtil.post('/server/getNewmlkem768');
|
const msg = await HttpUtil.post('/server/getNewVlessEnc');
|
||||||
inModal.loading(false);
|
inModal.loading(false);
|
||||||
|
|
||||||
if (!msg.success) {
|
if (!msg.success) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
inModal.inbound.stream.reality.mlkem768Seed = msg.obj.seed;
|
|
||||||
inModal.inbound.stream.reality.settings.mlkem768Client = msg.obj.Client;
|
const auths = msg.obj.auths || [];
|
||||||
},
|
const selected = inModal.inbound.settings.selectedAuth;
|
||||||
|
|
||||||
|
const block = auths.find(a => a.label === selected);
|
||||||
|
|
||||||
|
if (!block) {
|
||||||
|
console.error("No auth block for", selected);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ server config field
|
||||||
|
inModal.inbound.settings.decryption = block.decryption;
|
||||||
|
|
||||||
|
// ✅ UI-only field (not saved in config)
|
||||||
|
inModal.inbound.settings.encryption = block.encryption;
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -872,28 +872,51 @@ func (s *ServerService) GetNewEchCert(sni string) (interface{}, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerService) GetNewmlkem768() (any, error) {
|
type AuthBlock struct {
|
||||||
// Run the command
|
Label string `json:"label"`
|
||||||
cmd := exec.Command(xray.GetBinaryPath(), "mlkem768")
|
Decryption string `json:"decryption"`
|
||||||
|
Encryption string `json:"encryption"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServerService) GetNewVlessEnc() (any, error) {
|
||||||
|
cmd := exec.Command(xray.GetBinaryPath(), "vlessenc")
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
cmd.Stdout = &out
|
cmd.Stdout = &out
|
||||||
err := cmd.Run()
|
if err := cmd.Run(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
lines := strings.Split(out.String(), "\n")
|
lines := strings.Split(out.String(), "\n")
|
||||||
|
|
||||||
SeedLine := strings.Split(lines[0], ":")
|
var blocks []AuthBlock
|
||||||
ClientLine := strings.Split(lines[1], ":")
|
var current *AuthBlock
|
||||||
|
|
||||||
seed := strings.TrimSpace(SeedLine[1])
|
for _, line := range lines {
|
||||||
client := strings.TrimSpace(ClientLine[1])
|
line = strings.TrimSpace(line)
|
||||||
|
if strings.HasPrefix(line, "Authentication:") {
|
||||||
keyPair := map[string]any{
|
if current != nil {
|
||||||
"seed": seed,
|
blocks = append(blocks, *current)
|
||||||
"client": client,
|
}
|
||||||
|
current = &AuthBlock{Label: strings.TrimSpace(strings.TrimPrefix(line, "Authentication:"))}
|
||||||
|
} else if strings.HasPrefix(line, `"decryption"`) || strings.HasPrefix(line, `"encryption"`) {
|
||||||
|
parts := strings.SplitN(line, ":", 2)
|
||||||
|
if len(parts) == 2 && current != nil {
|
||||||
|
key := strings.Trim(parts[0], `" `)
|
||||||
|
val := strings.Trim(parts[1], `" `)
|
||||||
|
if key == "decryption" {
|
||||||
|
current.Decryption = val
|
||||||
|
} else if key == "encryption" {
|
||||||
|
current.Encryption = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return keyPair, nil
|
if current != nil {
|
||||||
|
blocks = append(blocks, *current)
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"auths": blocks,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue