This commit is contained in:
mhsanaei 2025-09-06 21:00:24 +02:00
parent 9b9746ca6b
commit a15c8f8bfe
No known key found for this signature in database
GPG key ID: D875CD086CF668A0
5 changed files with 123 additions and 43 deletions

View file

@ -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 {

View file

@ -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

View file

@ -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">

View file

@ -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;
}
}, },
}); });

View file

@ -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
} }