443 lines
16 KiB
Vue
443 lines
16 KiB
Vue
<template>
|
|
<v-dialog transition="dialog-bottom-transition" width="800">
|
|
<v-card class="rounded-lg">
|
|
<v-card-title>
|
|
{{ $t('actions.' + title) + " " + $t('objects.tls') }}
|
|
</v-card-title>
|
|
<v-divider></v-divider>
|
|
<v-card-text>
|
|
<v-card class="rounded-lg">
|
|
<v-row>
|
|
<v-col cols="12" sm="6" md="4">
|
|
<v-text-field
|
|
:label="$t('client.name')"
|
|
hide-details
|
|
v-model="tls.name">
|
|
</v-text-field>
|
|
</v-col>
|
|
<v-col align="end">
|
|
<v-btn-toggle v-model="tlsType"
|
|
class="rounded-xl"
|
|
density="compact"
|
|
variant="outlined"
|
|
@update:model-value="changeTlsType"
|
|
shaped
|
|
mandatory>
|
|
<v-btn>TLS</v-btn>
|
|
<v-btn>Reality</v-btn>
|
|
</v-btn-toggle>
|
|
</v-col>
|
|
</v-row>
|
|
<v-row>
|
|
<v-col cols="12" sm="6" md="4" v-if="inTls.server_name != undefined">
|
|
<v-text-field
|
|
label="SNI"
|
|
hide-details
|
|
v-model="inTls.server_name">
|
|
</v-text-field>
|
|
</v-col>
|
|
<template v-if="tlsType == 0">
|
|
<v-col cols="12" sm="6" md="4" v-if="inTls.min_version">
|
|
<v-select
|
|
hide-details
|
|
:label="$t('tls.minVer')"
|
|
:items="tlsVersions"
|
|
v-model="inTls.min_version">
|
|
</v-select>
|
|
</v-col>
|
|
<v-col cols="12" sm="6" md="4" v-if="inTls.max_version">
|
|
<v-select
|
|
hide-details
|
|
:label="$t('tls.maxVer')"
|
|
:items="tlsVersions"
|
|
v-model="inTls.max_version">
|
|
</v-select>
|
|
</v-col>
|
|
<v-col cols="12" sm="6" md="4" v-if="inTls.alpn">
|
|
<v-select
|
|
hide-details
|
|
label="ALPN"
|
|
multiple
|
|
:items="alpn"
|
|
v-model="inTls.alpn">
|
|
</v-select>
|
|
</v-col>
|
|
<v-col cols="12" md="8" v-if="inTls.cipher_suites != undefined">
|
|
<v-select
|
|
hide-details
|
|
:label="$t('tls.cs')"
|
|
multiple
|
|
:items="cipher_suites"
|
|
v-model="inTls.cipher_suites">
|
|
</v-select>
|
|
</v-col>
|
|
</template>
|
|
</v-row>
|
|
<template v-if="tlsType == 0">
|
|
<v-row>
|
|
<v-col>
|
|
<v-btn-toggle v-model="usePath"
|
|
class="rounded-xl"
|
|
density="compact"
|
|
variant="outlined"
|
|
shaped
|
|
mandatory>
|
|
<v-btn
|
|
@click="inTls.key=undefined; inTls.certificate=undefined"
|
|
>{{ $t('tls.usePath') }}</v-btn>
|
|
<v-btn
|
|
@click="inTls.key_path=undefined; inTls.certificate_path=undefined"
|
|
>{{ $t('tls.useText') }}</v-btn>
|
|
</v-btn-toggle>
|
|
</v-col>
|
|
</v-row>
|
|
<v-row v-if="usePath == 0">
|
|
<v-col cols="12" sm="6">
|
|
<v-text-field
|
|
:label="$t('tls.certPath')"
|
|
hide-details
|
|
v-model="inTls.certificate_path">
|
|
</v-text-field>
|
|
</v-col>
|
|
<v-col cols="12" sm="6">
|
|
<v-text-field
|
|
:label="$t('tls.keyPath')"
|
|
hide-details
|
|
v-model="inTls.key_path">
|
|
</v-text-field>
|
|
</v-col>
|
|
</v-row>
|
|
<v-row v-else>
|
|
<v-col cols="12" sm="6">
|
|
<v-textarea
|
|
:label="$t('tls.cert')"
|
|
hide-details
|
|
v-model="certText">
|
|
</v-textarea>
|
|
</v-col>
|
|
<v-col cols="12" sm="6">
|
|
<v-textarea
|
|
:label="$t('tls.key')"
|
|
hide-details
|
|
v-model="keyText">
|
|
</v-textarea>
|
|
</v-col>
|
|
</v-row>
|
|
<v-row>
|
|
<v-col cols="12" sm="6" md="4" v-if="outTls.utls != undefined">
|
|
<v-select
|
|
hide-details
|
|
label="Fingerprint"
|
|
:items="fingerprints"
|
|
v-model="outTls.utls.fingerprint">
|
|
</v-select>
|
|
</v-col>
|
|
<v-col cols="12" sm="6" md="4">
|
|
<v-switch color="primary" :label="$t('tls.disableSni')" v-model="disableSni" hide-details></v-switch>
|
|
</v-col>
|
|
<v-col cols="12" sm="6" md="4">
|
|
<v-switch color="primary" :label="$t('tls.insecure')" v-model="insecure" hide-details></v-switch>
|
|
</v-col>
|
|
</v-row>
|
|
</template>
|
|
<template v-if="outTls.reality && inTls.reality">
|
|
<v-row>
|
|
<v-col cols="12" sm="6" md="4">
|
|
<v-text-field
|
|
:label="$t('types.shdwTls.hs')"
|
|
hide-details
|
|
v-model="inTls.reality.handshake.server">
|
|
</v-text-field>
|
|
</v-col>
|
|
<v-col cols="12" sm="6" md="4">
|
|
<v-text-field
|
|
:label="$t('out.port')"
|
|
type="number"
|
|
min="0"
|
|
hide-details
|
|
v-model="server_port">
|
|
</v-text-field>
|
|
</v-col>
|
|
</v-row>
|
|
<v-row>
|
|
<v-col cols="12" md="6">
|
|
<v-text-field
|
|
:label="$t('tls.privKey')"
|
|
hide-details
|
|
v-model="inTls.reality.private_key">
|
|
</v-text-field>
|
|
</v-col>
|
|
<v-col cols="12" md="6">
|
|
<v-text-field
|
|
:label="$t('tls.pubKey')"
|
|
hide-details
|
|
v-model="outTls.reality.public_key">
|
|
</v-text-field>
|
|
</v-col>
|
|
<v-col cols="12" md="4">
|
|
<v-text-field
|
|
label="Short IDs"
|
|
hide-details
|
|
v-model="short_id">
|
|
</v-text-field>
|
|
</v-col>
|
|
<v-col cols="12" sm="6" md="4" v-if="optionTime">
|
|
<v-text-field
|
|
label="Max Time Diference"
|
|
type="number"
|
|
min="1"
|
|
:suffix="$t('date.m')"
|
|
hide-details
|
|
v-model="max_time">
|
|
</v-text-field>
|
|
</v-col>
|
|
</v-row>
|
|
</template>
|
|
<v-card-actions>
|
|
<v-spacer></v-spacer>
|
|
<v-menu v-model="menu" :close-on-content-click="false" location="start">
|
|
<template v-slot:activator="{ props }">
|
|
<v-btn v-bind="props" hide-details>{{ $t('tls.options') }}</v-btn>
|
|
</template>
|
|
<v-card>
|
|
<v-list>
|
|
<template v-if="tlsType == 0">
|
|
<v-list-item>
|
|
<v-switch v-model="optionSNI" color="primary" label="SNI" hide-details></v-switch>
|
|
</v-list-item>
|
|
<v-list-item>
|
|
<v-switch v-model="optionALPN" color="primary" label="ALPN" hide-details></v-switch>
|
|
</v-list-item>
|
|
<v-list-item>
|
|
<v-switch v-model="optionMinV" color="primary" :label="$t('tls.minVer')" hide-details></v-switch>
|
|
</v-list-item>
|
|
<v-list-item>
|
|
<v-switch v-model="optionMaxV" color="primary" :label="$t('tls.maxVer')" hide-details></v-switch>
|
|
</v-list-item>
|
|
<v-list-item>
|
|
<v-switch v-model="optionCS" color="primary" :label="$t('tls.cs')" hide-details></v-switch>
|
|
</v-list-item>
|
|
<v-list-item>
|
|
<v-switch v-model="optionFP" color="primary" label="UTLS" hide-details></v-switch>
|
|
</v-list-item>
|
|
</template>
|
|
<template v-else>
|
|
<v-list-item>
|
|
<v-switch v-model="optionTime" color="primary" label="Max Time Difference" hide-details></v-switch>
|
|
</v-list-item>
|
|
</template>
|
|
</v-list>
|
|
</v-card>
|
|
</v-menu>
|
|
</v-card-actions>
|
|
</v-card>
|
|
<AcmeVue :tls="inTls" />
|
|
<EchVue :iTls="inTls" :oTls="outTls" />
|
|
</v-card-text>
|
|
<v-card-actions>
|
|
<v-spacer></v-spacer>
|
|
<v-btn
|
|
color="blue-darken-1"
|
|
variant="text"
|
|
@click="closeModal"
|
|
>
|
|
{{ $t('actions.close') }}
|
|
</v-btn>
|
|
<v-btn
|
|
color="blue-darken-1"
|
|
variant="text"
|
|
:loading="loading"
|
|
@click="saveChanges"
|
|
>
|
|
{{ $t('actions.save') }}
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-dialog>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { iTls, defaultInTls } from '@/types/inTls'
|
|
import { oTls, defaultOutTls } from '@/types/outTls'
|
|
import AcmeVue from '@/components/Acme.vue'
|
|
import EchVue from '@/components/Ech.vue'
|
|
export default {
|
|
props: ['visible', 'data', 'index'],
|
|
emits: ['close', 'save'],
|
|
data() {
|
|
return {
|
|
tls: { id: -1, name: '', inbounds: [], server: <iTls>{ enabled: true }, client: <oTls>{} },
|
|
title: "add",
|
|
loading: false,
|
|
menu: false,
|
|
tlsType: 0,
|
|
usePath: 0,
|
|
alpn: [
|
|
{ title: "H3", value: 'h3' },
|
|
{ title: "H2", value: 'h2' },
|
|
{ title: "Http/1.1", value: 'http/1.1' },
|
|
],
|
|
tlsVersions: [ '1.0', '1.1', '1.2', '1.3' ],
|
|
cipher_suites: [
|
|
{ title: "RSA-AES128-CBC-SHA", value: "TLS_RSA_WITH_AES_128_CBC_SHA" },
|
|
{ title: "RSA-AES256-CBC-SHA", value: "TLS_RSA_WITH_AES_256_CBC_SHA" },
|
|
{ title: "RSA-AES128-GCM-SHA256", value: "TLS_RSA_WITH_AES_128_GCM_SHA256" },
|
|
{ title: "RSA-AES256-GCM-SHA384", value: "TLS_RSA_WITH_AES_256_GCM_SHA384" },
|
|
{ title: "AES128-GCM-SHA256", value: "TLS_AES_128_GCM_SHA256" },
|
|
{ title: "AES256-GCM-SHA384", value: "TLS_AES_256_GCM_SHA384" },
|
|
{ title: "CHACHA20-POLY1305-SHA256", value: "TLS_CHACHA20_POLY1305_SHA256" },
|
|
{ title: "ECDHE-ECDSA-AES128-CBC-SHA", value: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA" },
|
|
{ title: "ECDHE-ECDSA-AES256-CBC-SHA", value: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA" },
|
|
{ title: "ECDHE-RSA-AES128-CBC-SHA", value: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" },
|
|
{ title: "ECDHE-RSA-AES256-CBC-SHA", value: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" },
|
|
{ title: "ECDHE-ECDSA-AES128-GCM-SHA256", value: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" },
|
|
{ title: "ECDHE-ECDSA-AES256-GCM-SHA384", value: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" },
|
|
{ title: "ECDHE-RSA-AES128-GCM-SHA256", value: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" },
|
|
{ title: "ECDHE-RSA-AES256-GCM-SHA384", value: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" },
|
|
{ title: "ECDHE-ECDSA-CHACHA20-POLY1305-SHA256", value: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" },
|
|
{ title: "ECDHE-RSA-CHACHA20-POLY1305-SHA256", value: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" }
|
|
],
|
|
fingerprints: [
|
|
{ title: "Chrome", value: "chrome" },
|
|
{ title: "Chrome PSK", value: "chrome_psk" },
|
|
{ title: "Chrome PSK Shuffle", value: "chrome_psk_shuffle" },
|
|
{ title: "Chrome Padding PSK Shuffle", value: "chrome_padding_psk_shuffle" },
|
|
{ title: "Chrome Post-Quantum", value: "chrome_pq" },
|
|
{ title: "Chrome Post-Quantum PSK", value: "chrome_pq_psk" },
|
|
{ title: "Firefox", value: "firefox" },
|
|
{ title: "Microsoft Edge", value: "edge" },
|
|
{ title: "Apple Safari", value: "safari" },
|
|
{ title: "360", value: "360" },
|
|
{ title: "QQ", value: "qq" },
|
|
{ title: "Apple IOS", value: "ios" },
|
|
{ title: "Android", value: "android" },
|
|
{ title: "Random", value: "random" },
|
|
{ title: "Randomized", value: "randomized" },
|
|
]
|
|
}
|
|
},
|
|
methods: {
|
|
updateData() {
|
|
if (this.$props.index != -1) {
|
|
const newData = JSON.parse(this.$props.data)
|
|
this.tls = newData
|
|
this.tlsType = newData.server?.reality == undefined ? 0 : 1
|
|
this.usePath = newData.server?.key == undefined ? 0 : 1
|
|
this.title = "edit"
|
|
}
|
|
else {
|
|
this.tls = { id: 0, name: '', inbounds: [], server: {enabled: true}, client: {} }
|
|
this.tlsType = 0
|
|
this.usePath = 0
|
|
this.title = "add"
|
|
}
|
|
},
|
|
changeTlsType(){
|
|
if (this.tlsType) {
|
|
this.tls.server = <iTls>{ enabled: true, reality: { enabled: true, handshake: { server_port: 443 } }, server_name: "" }
|
|
this.tls.client = <oTls>{ reality: { public_key: "" } }
|
|
} else {
|
|
this.tls.server = <iTls>{ enabled: true }
|
|
this.tls.client = <oTls>{}
|
|
}
|
|
},
|
|
closeModal() {
|
|
this.updateData() // reset
|
|
this.$emit('close')
|
|
},
|
|
saveChanges() {
|
|
this.loading = true
|
|
this.$emit('save', this.tls)
|
|
this.loading = false
|
|
},
|
|
},
|
|
computed: {
|
|
inTls(): iTls {
|
|
return <iTls> this.tls.server
|
|
},
|
|
outTls(): oTls {
|
|
return <oTls> this.tls.client
|
|
},
|
|
certText: {
|
|
get(): string { return this.inTls.certificate ? this.inTls.certificate.join('\n') : '' },
|
|
set(v:string) { this.inTls.certificate = v.split('\n') }
|
|
},
|
|
keyText: {
|
|
get(): string { return this.inTls.key ? this.inTls.key.join('\n') : '' },
|
|
set(v:string) { this.inTls.key = v.split('\n') }
|
|
},
|
|
disableSni: {
|
|
get() { return this.outTls.disable_sni ?? false },
|
|
set(v: boolean) { this.outTls.disable_sni = v ? true : undefined }
|
|
},
|
|
insecure: {
|
|
get() { return this.outTls.insecure ?? false },
|
|
set(v: boolean) { this.outTls.insecure = v ? true : undefined }
|
|
},
|
|
server_port: {
|
|
get() { return this.inTls.reality?.handshake?.server_port ? this.inTls.reality.handshake.server_port : 443 },
|
|
set(v: any) {
|
|
if (this.inTls.reality){
|
|
this.inTls.reality.handshake.server_port = v.length == 0 || v == 0 ? 443 : parseInt(v)
|
|
}
|
|
}
|
|
},
|
|
short_id: {
|
|
get() { return this.inTls.reality?.short_id ? this.inTls.reality.short_id.join(',') : undefined },
|
|
set(v: string) {
|
|
if (this.inTls.reality){
|
|
this.inTls.reality.short_id = v.length > 0 ? v.split(',') : []
|
|
}
|
|
}
|
|
},
|
|
max_time: {
|
|
get() { return this.inTls?.reality?.max_time_difference ? this.inTls.reality.max_time_difference.replace('m','') : 1 },
|
|
set(v: number) {
|
|
if (this.inTls.reality){
|
|
this.inTls.reality.max_time_difference = v > 0 ? v + 'm' : '1m'
|
|
}
|
|
}
|
|
},
|
|
optionSNI: {
|
|
get(): boolean { return this.inTls.server_name != undefined },
|
|
set(v:boolean) { this.inTls.server_name = v ? '' : undefined }
|
|
},
|
|
optionALPN: {
|
|
get(): boolean { return this.inTls.alpn != undefined },
|
|
set(v:boolean) { this.inTls.alpn = v ? defaultInTls.alpn : undefined }
|
|
},
|
|
optionMinV: {
|
|
get(): boolean { return this.inTls.min_version != undefined },
|
|
set(v:boolean) { this.inTls.min_version = v ? defaultInTls.min_version : undefined }
|
|
},
|
|
optionMaxV: {
|
|
get(): boolean { return this.inTls.max_version != undefined },
|
|
set(v:boolean) { this.inTls.max_version = v ? defaultInTls.max_version : undefined }
|
|
},
|
|
optionCS: {
|
|
get(): boolean { return this.inTls.cipher_suites != undefined },
|
|
set(v:boolean) { this.inTls.cipher_suites = v ? defaultInTls.cipher_suites : undefined }
|
|
},
|
|
optionFP: {
|
|
get(): boolean { return this.outTls.utls != undefined },
|
|
set(v:boolean) { this.outTls.utls = v ? defaultOutTls.utls : undefined }
|
|
},
|
|
optionEch: {
|
|
get(): boolean { return this.outTls.ech != undefined },
|
|
set(v:boolean) { this.outTls.ech = v ? defaultOutTls.ech : undefined }
|
|
},
|
|
optionTime: {
|
|
get(): boolean { return this.inTls?.reality?.max_time_difference != undefined },
|
|
set(v:boolean) { if (this.inTls.reality) this.inTls.reality.max_time_difference = v ? "1m" : undefined }
|
|
}
|
|
},
|
|
watch: {
|
|
visible(v) {
|
|
if (v) {
|
|
this.updateData()
|
|
}
|
|
},
|
|
},
|
|
components: { AcmeVue, EchVue }
|
|
}
|
|
</script> |