mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-10 18:05:48 +02:00
refactor: auto certificate options
1. Add OCSP Must Staple options #292 2. Add LEGO_DISABLE_CNAME_SUPPORT options #407
This commit is contained in:
parent
532d6e83c5
commit
4660a46a7e
18 changed files with 234 additions and 212 deletions
|
@ -117,14 +117,16 @@ func IssueCert(c *gin.Context) {
|
|||
}
|
||||
|
||||
err = certModel.Updates(&model.Cert{
|
||||
Domains: payload.ServerName,
|
||||
SSLCertificatePath: payload.GetCertificatePath(),
|
||||
SSLCertificateKeyPath: payload.GetCertificateKeyPath(),
|
||||
AutoCert: model.AutoCertEnabled,
|
||||
KeyType: payload.KeyType,
|
||||
ChallengeMethod: payload.ChallengeMethod,
|
||||
DnsCredentialID: payload.DNSCredentialID,
|
||||
Resource: payload.Resource,
|
||||
Domains: payload.ServerName,
|
||||
SSLCertificatePath: payload.GetCertificatePath(),
|
||||
SSLCertificateKeyPath: payload.GetCertificateKeyPath(),
|
||||
AutoCert: model.AutoCertEnabled,
|
||||
KeyType: payload.KeyType,
|
||||
ChallengeMethod: payload.ChallengeMethod,
|
||||
DnsCredentialID: payload.DNSCredentialID,
|
||||
Resource: payload.Resource,
|
||||
MustStaple: payload.MustStaple,
|
||||
LegoDisableCNAMESupport: payload.LegoDisableCNAMESupport,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
|
|
@ -5,21 +5,27 @@ export interface DNSProvider {
|
|||
code?: string
|
||||
provider?: string
|
||||
configuration: {
|
||||
credentials: {
|
||||
[key: string]: string
|
||||
}
|
||||
additional: {
|
||||
[key: string]: string
|
||||
}
|
||||
credentials: Record<string, string>
|
||||
additional: Record<string, string>
|
||||
}
|
||||
links?: {
|
||||
api: string
|
||||
go_client: string
|
||||
}
|
||||
}
|
||||
export interface DnsChallenge extends DNSProvider {
|
||||
dns_credential_id: number | null
|
||||
challenge_method: string
|
||||
|
||||
export interface AutoCertOptions {
|
||||
name?: string
|
||||
domains: string[]
|
||||
code?: string
|
||||
dns_credential_id?: number | null
|
||||
challenge_method?: string
|
||||
configuration?: DNSProvider['configuration']
|
||||
key_type: string
|
||||
acme_user_id?: number
|
||||
provider?: string
|
||||
must_staple?: boolean
|
||||
lego_disable_cname_support?: boolean
|
||||
}
|
||||
|
||||
const auto_cert = {
|
||||
|
|
|
@ -3,18 +3,19 @@ import type { SelectProps } from 'ant-design-vue'
|
|||
import type { Ref } from 'vue'
|
||||
import type { AcmeUser } from '@/api/acme_user'
|
||||
import acme_user from '@/api/acme_user'
|
||||
import type { Cert } from '@/api/cert'
|
||||
import type { AutoCertOptions } from '@/api/auto_cert'
|
||||
|
||||
const users = ref([]) as Ref<AcmeUser[]>
|
||||
|
||||
// This data is provided by the Top StdCurd component,
|
||||
// is the object that you are trying to modify it
|
||||
// we externalize the dns_credential_id to the parent component,
|
||||
// this is used to tell the backend which dns_credential to use
|
||||
const data = inject('data') as Ref<Cert>
|
||||
const data = defineModel<AutoCertOptions>('options', {
|
||||
default: () => {
|
||||
return {}
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
|
||||
const id = computed(() => {
|
||||
return data.value.acme_user_id
|
||||
return data.value?.acme_user_id
|
||||
})
|
||||
|
||||
const user_idx = ref()
|
||||
|
@ -35,7 +36,7 @@ watch(id, init)
|
|||
|
||||
watch(current, () => {
|
||||
if (mounted.value)
|
||||
data.value.acme_user_id = current.value.id
|
||||
data.value!.acme_user_id = current.value.id
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
|
@ -84,8 +85,9 @@ const filterOption = (input: string, option: { label: string }) => {
|
|||
<AFormItem :label="$gettext('ACME User')">
|
||||
<ASelect
|
||||
v-model:value="user_idx"
|
||||
:placeholder="$gettext('System Initial User')"
|
||||
show-search
|
||||
:options="options"
|
||||
:options
|
||||
:filter-option="filterOption"
|
||||
/>
|
||||
</AFormItem>
|
||||
|
|
|
@ -52,12 +52,6 @@ function save() {
|
|||
})
|
||||
}
|
||||
|
||||
provide('data', data)
|
||||
|
||||
provide('no_server_name', computed(() => {
|
||||
return false
|
||||
}))
|
||||
|
||||
const log = computed(() => {
|
||||
const logs = data.value.log?.split('\n')
|
||||
|
||||
|
@ -134,9 +128,17 @@ const isManaged = computed(() => {
|
|||
</AForm>
|
||||
|
||||
<template v-if="isManaged">
|
||||
<RenewCert @renewed="init" />
|
||||
<RenewCert
|
||||
:options="{
|
||||
name: data.name,
|
||||
domains: data.domains,
|
||||
key_type: data.key_type,
|
||||
}"
|
||||
@renewed="init"
|
||||
/>
|
||||
|
||||
<AutoCertStepOne
|
||||
v-model:options="data"
|
||||
style="max-width: 600px"
|
||||
hide-note
|
||||
/>
|
||||
|
|
|
@ -5,13 +5,6 @@ import StdTable from '@/components/StdDesign/StdDataDisplay/StdTable.vue'
|
|||
import cert from '@/api/cert'
|
||||
import WildcardCertificate from '@/views/certificate/WildcardCertificate.vue'
|
||||
|
||||
// DO NOT REMOVE THESE LINES
|
||||
const no_server_name = computed(() => {
|
||||
return false
|
||||
})
|
||||
|
||||
provide('no_server_name', no_server_name)
|
||||
|
||||
const refWildcard = ref()
|
||||
const refTable = ref()
|
||||
</script>
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
import type { Ref } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import ObtainCertLive from '@/views/domain/cert/components/ObtainCertLive.vue'
|
||||
import type { Cert } from '@/api/cert'
|
||||
import type { AutoCertOptions } from '@/api/auto_cert'
|
||||
|
||||
const props = defineProps<{
|
||||
options: AutoCertOptions
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
renewed: [void]
|
||||
|
@ -13,12 +16,12 @@ const modalClosable = ref(true)
|
|||
|
||||
const refObtainCertLive = ref()
|
||||
|
||||
const data = inject('data') as Ref<Cert>
|
||||
|
||||
const issueCert = () => {
|
||||
modalVisible.value = true
|
||||
|
||||
refObtainCertLive.value.issue_cert(data.value.name, data.value.domains, data.value.key_type).then(() => {
|
||||
const { name, domains, key_type } = props.options
|
||||
|
||||
refObtainCertLive.value.issue_cert(name, domains, key_type).then(() => {
|
||||
message.success($gettext('Renew successfully'))
|
||||
emit('renewed')
|
||||
})
|
||||
|
@ -52,6 +55,7 @@ provide('issuing_cert', issuing_cert)
|
|||
ref="refObtainCertLive"
|
||||
v-model:modal-closable="modalClosable"
|
||||
v-model:modal-visible="modalVisible"
|
||||
:options
|
||||
/>
|
||||
</AModal>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
<script setup lang="ts">
|
||||
import type { Ref } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import type { Cert } from '@/api/cert'
|
||||
import ObtainCertLive from '@/views/domain/cert/components/ObtainCertLive.vue'
|
||||
import DNSChallenge from '@/views/domain/cert/components/DNSChallenge.vue'
|
||||
import { PrivateKeyTypeList } from '@/constants'
|
||||
import type { AutoCertOptions } from '@/api/auto_cert'
|
||||
import AutoCertStepOne from '@/views/domain/cert/components/AutoCertStepOne.vue'
|
||||
|
||||
const emit = defineEmits<{
|
||||
issued: [void]
|
||||
|
@ -12,10 +11,9 @@ const emit = defineEmits<{
|
|||
|
||||
const step = ref(0)
|
||||
const visible = ref(false)
|
||||
const data = ref({}) as Ref<Cert>
|
||||
const data = ref({}) as Ref<AutoCertOptions>
|
||||
const issuing_cert = ref(false)
|
||||
|
||||
provide('data', data)
|
||||
provide('issuing_cert', issuing_cert)
|
||||
function open() {
|
||||
visible.value = true
|
||||
|
@ -23,7 +21,7 @@ function open() {
|
|||
data.value = {
|
||||
challenge_method: 'dns01',
|
||||
key_type: '2048',
|
||||
} as Cert
|
||||
} as AutoCertOptions
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
|
@ -66,8 +64,6 @@ const issueCert = () => {
|
|||
force-render
|
||||
>
|
||||
<template v-if="step === 0">
|
||||
<DNSChallenge />
|
||||
|
||||
<AForm layout="vertical">
|
||||
<AFormItem :label="$gettext('Domain')">
|
||||
<AInput
|
||||
|
@ -75,19 +71,15 @@ const issueCert = () => {
|
|||
addon-before="*."
|
||||
/>
|
||||
</AFormItem>
|
||||
|
||||
<AFormItem :label="$gettext('Key Type')">
|
||||
<ASelect v-model:value="data.key_type">
|
||||
<ASelectOption
|
||||
v-for="t in PrivateKeyTypeList"
|
||||
:key="t.key"
|
||||
:value="t.key"
|
||||
>
|
||||
{{ t.name }}
|
||||
</ASelectOption>
|
||||
</ASelect>
|
||||
</AFormItem>
|
||||
</AForm>
|
||||
|
||||
<AutoCertStepOne
|
||||
v-model:options="data"
|
||||
style="max-width: 600px"
|
||||
hide-note
|
||||
force-dns-challenge
|
||||
/>
|
||||
|
||||
<div
|
||||
v-if="step === 0"
|
||||
class="flex justify-end"
|
||||
|
@ -106,6 +98,7 @@ const issueCert = () => {
|
|||
ref="refObtainCertLive"
|
||||
v-model:modal-closable="modalClosable"
|
||||
v-model:modal-visible="modalVisible"
|
||||
:options="data"
|
||||
/>
|
||||
</AModal>
|
||||
</div>
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
import ObtainCert from '@/views/domain/cert/components/ObtainCert.vue'
|
||||
import type { NgxDirective } from '@/api/ngx'
|
||||
|
||||
export interface Props {
|
||||
defineProps<{
|
||||
configName: string
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
}>()
|
||||
|
||||
const issuing_cert = ref(false)
|
||||
const obtain_cert = ref()
|
||||
|
@ -16,18 +14,16 @@ const enabled = defineModel<boolean>('enabled', {
|
|||
default: () => false,
|
||||
})
|
||||
|
||||
const no_server_name = computed(() => {
|
||||
const noServerName = computed(() => {
|
||||
if (!directivesMap.value.server_name)
|
||||
return true
|
||||
|
||||
return directivesMap.value.server_name.length === 0
|
||||
})
|
||||
|
||||
provide('no_server_name', no_server_name)
|
||||
provide('props', props)
|
||||
provide('issuing_cert', issuing_cert)
|
||||
|
||||
watch(no_server_name, () => {
|
||||
watch(noServerName, () => {
|
||||
enabled.value = false
|
||||
})
|
||||
|
||||
|
@ -45,6 +41,8 @@ async function onchange() {
|
|||
<ObtainCert
|
||||
ref="obtain_cert"
|
||||
:key="update"
|
||||
:no-server-name="noServerName"
|
||||
:config-name="configName"
|
||||
@update:auto_cert="r => enabled = r"
|
||||
/>
|
||||
<div class="issue-cert">
|
||||
|
@ -52,7 +50,7 @@ async function onchange() {
|
|||
<ASwitch
|
||||
:loading="issuing_cert"
|
||||
:checked="enabled"
|
||||
:disabled="no_server_name"
|
||||
:disabled="noServerName"
|
||||
@change="onchange"
|
||||
/>
|
||||
</AFormItem>
|
||||
|
|
|
@ -1,43 +1,38 @@
|
|||
<script setup lang="ts">
|
||||
import type { Ref } from 'vue'
|
||||
import type { DnsChallenge } from '@/api/auto_cert'
|
||||
import { $gettext } from '../../../../gettext'
|
||||
import type { AutoCertOptions } from '@/api/auto_cert'
|
||||
import DNSChallenge from '@/views/domain/cert/components/DNSChallenge.vue'
|
||||
import type { Cert } from '@/api/cert'
|
||||
import ACMEUserSelector from '@/views/certificate/ACMEUserSelector.vue'
|
||||
import { PrivateKeyTypeList } from '@/constants'
|
||||
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
hideNote?: boolean
|
||||
forceDnsChallenge?: boolean
|
||||
}>()
|
||||
|
||||
const no_server_name = inject('no_server_name')
|
||||
|
||||
// Provide by ObtainCert.vue
|
||||
const data = inject('data') as Ref<DnsChallenge & Cert>
|
||||
const data = defineModel<AutoCertOptions>('options', {
|
||||
default: () => {
|
||||
return {}
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (!data.value.key_type)
|
||||
data.value.key_type = '2048'
|
||||
|
||||
if (props.forceDnsChallenge)
|
||||
data.value.challenge_method = 'dns01'
|
||||
})
|
||||
|
||||
watch(() => props.forceDnsChallenge, v => {
|
||||
if (v)
|
||||
data.value.challenge_method = 'dns01'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="no_server_name">
|
||||
<AAlert
|
||||
:message="$gettext('Warning')"
|
||||
type="warning"
|
||||
show-icon
|
||||
>
|
||||
<template #description>
|
||||
<span v-if="no_server_name">
|
||||
{{ $gettext('server_name parameter is required') }}
|
||||
</span>
|
||||
</template>
|
||||
</AAlert>
|
||||
<br>
|
||||
</template>
|
||||
|
||||
<AAlert
|
||||
v-if="!hideNote"
|
||||
type="info"
|
||||
|
@ -52,8 +47,8 @@ onMounted(() => {
|
|||
+ 'multiple domains.') }}
|
||||
</p>
|
||||
<p>
|
||||
{{ $gettext('The certificate for the domain will be checked 5 minutes, '
|
||||
+ 'and will be renewed if it has been more than 1 week since it was last issued.') }}
|
||||
{{ $gettext('The certificate for the domain will be checked 30 minutes, '
|
||||
+ 'and will be renewed if it has been more than 1 week or the period you set in settings since it was last issued.') }}
|
||||
</p>
|
||||
<p v-if="data.challenge_method === 'http01'">
|
||||
{{ $gettext('Make sure you have configured a reverse proxy for .well-known '
|
||||
|
@ -67,7 +62,10 @@ onMounted(() => {
|
|||
</template>
|
||||
</AAlert>
|
||||
<AForm layout="vertical">
|
||||
<AFormItem :label="$gettext('Challenge Method')">
|
||||
<AFormItem
|
||||
v-if="!forceDnsChallenge"
|
||||
:label="$gettext('Challenge Method')"
|
||||
>
|
||||
<ASelect v-model:value="data.challenge_method">
|
||||
<ASelectOption value="http01">
|
||||
{{ $gettext('HTTP01') }}
|
||||
|
@ -89,8 +87,32 @@ onMounted(() => {
|
|||
</ASelect>
|
||||
</AFormItem>
|
||||
</AForm>
|
||||
<ACMEUserSelector />
|
||||
<DNSChallenge v-if="data.challenge_method === 'dns01'" />
|
||||
<ACMEUserSelector v-model:options="data" />
|
||||
<DNSChallenge
|
||||
v-if="data.challenge_method === 'dns01'"
|
||||
v-model:options="data"
|
||||
/>
|
||||
<AForm layout="vertical">
|
||||
<AFormItem :label="$gettext('OCSP Must Staple')">
|
||||
<template #help>
|
||||
<p>
|
||||
{{ $gettext('Do not enable this option unless you are sure that you need it.') }}
|
||||
{{ $gettext('OCSP Must Staple may cause errors for some users on first access using Firefox.') }}
|
||||
<a href="https://github.com/0xJacky/nginx-ui/issues/322">#322</a>
|
||||
</p>
|
||||
</template>
|
||||
<ASwitch v-model:checked="data.must_staple" />
|
||||
</AFormItem>
|
||||
<AFormItem :label="$gettext('Lego disable CNAME Support')">
|
||||
<template #help>
|
||||
<p>
|
||||
{{ $gettext('If your domain has CNAME records and you cannot obtain certificates, '
|
||||
+ 'you need to enable this option.') }}
|
||||
</p>
|
||||
</template>
|
||||
<ASwitch v-model:checked="data.lego_disable_cname_support" />
|
||||
</AFormItem>
|
||||
</AForm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
<script setup lang="ts">
|
||||
import type { SelectProps } from 'ant-design-vue'
|
||||
import type { Ref } from 'vue'
|
||||
import type { SelectValue } from 'ant-design-vue/es/select'
|
||||
import type { DNSProvider } from '@/api/auto_cert'
|
||||
import type { AutoCertOptions, DNSProvider } from '@/api/auto_cert'
|
||||
import auto_cert from '@/api/auto_cert'
|
||||
import dns_credential from '@/api/dns_credential'
|
||||
|
||||
const providers = ref([]) as Ref<DNSProvider[]>
|
||||
const credentials = ref<SelectProps['options']>([])
|
||||
|
||||
// This data is provided by the Top StdCurd component,
|
||||
// is the object that you are trying to modify it
|
||||
// we externalize the dns_credential_id to the parent component,
|
||||
// this is used to tell the backend which dns_credential to use
|
||||
const data = inject('data') as Ref<DNSProvider & { dns_credential_id: SelectValue }>
|
||||
const data = defineModel<AutoCertOptions>('options', {
|
||||
default: () => {
|
||||
return {}
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
|
||||
const code = computed(() => {
|
||||
return data.value.code
|
||||
|
@ -95,7 +95,7 @@ const filterOption = (input: string, option: { label: string }) => {
|
|||
<ASelect
|
||||
v-model:value="provider_idx"
|
||||
show-search
|
||||
:options="options"
|
||||
:options
|
||||
:filter-option="filterOption"
|
||||
/>
|
||||
</AFormItem>
|
||||
|
@ -105,7 +105,7 @@ const filterOption = (input: string, option: { label: string }) => {
|
|||
:rules="[{ required: true }]"
|
||||
>
|
||||
<ASelect
|
||||
v-model:value="data.dns_credential_id"
|
||||
v-model:value="data.dns_credential_id as any"
|
||||
:options="credentials"
|
||||
/>
|
||||
</AFormItem>
|
||||
|
|
|
@ -4,12 +4,16 @@ import type { ComputedRef, Ref } from 'vue'
|
|||
import domain from '@/api/domain'
|
||||
import AutoCertStepOne from '@/views/domain/cert/components/AutoCertStepOne.vue'
|
||||
import type { NgxConfig, NgxDirective } from '@/api/ngx'
|
||||
import type { Props } from '@/views/domain/cert/IssueCert.vue'
|
||||
import type { DnsChallenge } from '@/api/auto_cert'
|
||||
import type { AutoCertOptions } from '@/api/auto_cert'
|
||||
import ObtainCertLive from '@/views/domain/cert/components/ObtainCertLive.vue'
|
||||
import type { CertificateResult } from '@/api/cert'
|
||||
import type { PrivateKeyType } from '@/constants'
|
||||
|
||||
const props = defineProps<{
|
||||
configName: string
|
||||
noServerName?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['update:auto_cert'])
|
||||
|
||||
const modalVisible = ref(false)
|
||||
|
@ -26,15 +30,11 @@ const data = ref({
|
|||
credentials: {},
|
||||
additional: {},
|
||||
},
|
||||
}) as Ref<DnsChallenge>
|
||||
}) as Ref<AutoCertOptions>
|
||||
|
||||
const modalClosable = ref(true)
|
||||
|
||||
provide('data', data)
|
||||
|
||||
const save_config = inject('save_config') as () => Promise<void>
|
||||
const no_server_name = inject('no_server_name') as Ref<boolean>
|
||||
const props = inject('props') as Props
|
||||
const issuing_cert = inject('issuing_cert') as Ref<boolean>
|
||||
const ngx_config = inject('ngx_config') as NgxConfig
|
||||
const current_server_directives = inject('current_server_directives') as ComputedRef<NgxDirective[]>
|
||||
|
@ -61,8 +61,8 @@ function change_auto_cert(status: boolean, key_type?: PrivateKeyType) {
|
|||
if (status) {
|
||||
domain.add_auto_cert(props.configName, {
|
||||
domains: name.value.trim().split(' '),
|
||||
challenge_method: data.value.challenge_method,
|
||||
dns_credential_id: data.value.dns_credential_id,
|
||||
challenge_method: data.value.challenge_method!,
|
||||
dns_credential_id: data.value.dns_credential_id!,
|
||||
key_type: key_type!,
|
||||
}).then(() => {
|
||||
message.success($gettext('Auto-renewal enabled for %{name}', { name: name.value }))
|
||||
|
@ -98,7 +98,7 @@ function job() {
|
|||
modalClosable.value = false
|
||||
issuing_cert.value = true
|
||||
|
||||
if (no_server_name.value) {
|
||||
if (props.noServerName) {
|
||||
message.error($gettext('server_name not found in directives'))
|
||||
issuing_cert.value = false
|
||||
|
||||
|
@ -183,13 +183,17 @@ function next() {
|
|||
force-render
|
||||
>
|
||||
<template v-if="step === 1">
|
||||
<AutoCertStepOne />
|
||||
<AutoCertStepOne
|
||||
v-model:options="data"
|
||||
:no-server-name="noServerName"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="step === 2">
|
||||
<ObtainCertLive
|
||||
ref="refObtainCertLive"
|
||||
v-model:modal-closable="modalClosable"
|
||||
v-model:modal-visible="modalVisible"
|
||||
:options="data"
|
||||
/>
|
||||
</template>
|
||||
<div
|
||||
|
|
|
@ -1,40 +1,17 @@
|
|||
<script setup lang="ts">
|
||||
import type { Ref } from 'vue'
|
||||
import websocket from '@/lib/websocket'
|
||||
import type { DnsChallenge } from '@/api/auto_cert'
|
||||
import Error from '@/views/other/Error.vue'
|
||||
import type { CertificateResult } from '@/api/cert'
|
||||
import type { AutoCertOptions } from '@/api/auto_cert'
|
||||
|
||||
const props = defineProps<{
|
||||
modalClosable: boolean
|
||||
modalVisible: boolean
|
||||
options: AutoCertOptions
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modalClosable': [value: boolean]
|
||||
'update:modalVisible': [value: boolean]
|
||||
}>()
|
||||
|
||||
const modalClosable = computed({
|
||||
get() {
|
||||
return props.modalClosable
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modalClosable', value)
|
||||
},
|
||||
})
|
||||
|
||||
const modalVisible = computed({
|
||||
get() {
|
||||
return props.modalVisible
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modalVisible', value)
|
||||
},
|
||||
})
|
||||
const modalVisible = defineModel<boolean>('modalVisible')
|
||||
const modalClosable = defineModel<boolean>('modalClosable')
|
||||
|
||||
const issuing_cert = inject('issuing_cert') as Ref<boolean>
|
||||
const data = inject('data') as Ref<DnsChallenge>
|
||||
|
||||
const progressStrokeColor = {
|
||||
from: '#108ee9',
|
||||
|
@ -71,8 +48,8 @@ const issue_cert = async (config_name: string, server_name: string[], key_type:
|
|||
ws.onopen = () => {
|
||||
ws.send(JSON.stringify({
|
||||
server_name,
|
||||
...props.options,
|
||||
key_type,
|
||||
...data.value,
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -114,7 +91,7 @@ const issue_cert = async (config_name: string, server_name: string[], key_type:
|
|||
}
|
||||
else {
|
||||
progressStatus.value = 'exception'
|
||||
reject(new Error($gettext('Fail to obtain certificate')))
|
||||
reject($gettext('Fail to obtain certificate'))
|
||||
}
|
||||
break
|
||||
}
|
||||
|
|
|
@ -71,12 +71,14 @@ func autoCert(certModel *model.Cert) {
|
|||
|
||||
// support SAN certification
|
||||
payload := &ConfigPayload{
|
||||
CertID: certModel.ID,
|
||||
ServerName: certModel.Domains,
|
||||
ChallengeMethod: certModel.ChallengeMethod,
|
||||
DNSCredentialID: certModel.DnsCredentialID,
|
||||
KeyType: certModel.GetKeyType(),
|
||||
NotBefore: certInfo.NotBefore,
|
||||
CertID: certModel.ID,
|
||||
ServerName: certModel.Domains,
|
||||
ChallengeMethod: certModel.ChallengeMethod,
|
||||
DNSCredentialID: certModel.DnsCredentialID,
|
||||
KeyType: certModel.GetKeyType(),
|
||||
NotBefore: certInfo.NotBefore,
|
||||
MustStaple: certModel.MustStaple,
|
||||
LegoDisableCNAMESupport: certModel.LegoDisableCNAMESupport,
|
||||
}
|
||||
|
||||
if certModel.Resource != nil {
|
||||
|
|
|
@ -130,7 +130,6 @@ func IssueCert(payload *ConfigPayload, logChan chan string, errChan chan error)
|
|||
errChan <- errors.Wrap(err, "environment configuration is empty")
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -138,6 +137,18 @@ func IssueCert(payload *ConfigPayload, logChan chan string, errChan chan error)
|
|||
return
|
||||
}
|
||||
|
||||
// fix #407
|
||||
if payload.LegoDisableCNAMESupport {
|
||||
err = os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", "true")
|
||||
if err != nil {
|
||||
errChan <- errors.Wrap(err, "set env flag to disable lego CNAME support error")
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = os.Unsetenv("LEGO_DISABLE_CNAME_SUPPORT")
|
||||
}()
|
||||
}
|
||||
|
||||
if time.Now().Sub(payload.NotBefore).Hours()/24 <= 21 &&
|
||||
payload.Resource != nil && payload.Resource.Certificate != nil {
|
||||
renew(payload, client, l, errChan)
|
||||
|
|
|
@ -10,8 +10,9 @@ import (
|
|||
|
||||
func obtain(payload *ConfigPayload, client *lego.Client, l *log.Logger, errChan chan error) {
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: payload.ServerName,
|
||||
Bundle: true,
|
||||
Domains: payload.ServerName,
|
||||
Bundle: true,
|
||||
MustStaple: payload.MustStaple,
|
||||
}
|
||||
|
||||
l.Println("[INFO] [Nginx UI] Obtaining certificate")
|
||||
|
|
|
@ -16,17 +16,19 @@ import (
|
|||
)
|
||||
|
||||
type ConfigPayload struct {
|
||||
CertID int `json:"cert_id"`
|
||||
ServerName []string `json:"server_name"`
|
||||
ChallengeMethod string `json:"challenge_method"`
|
||||
DNSCredentialID int `json:"dns_credential_id"`
|
||||
ACMEUserID int `json:"acme_user_id"`
|
||||
KeyType certcrypto.KeyType `json:"key_type"`
|
||||
Resource *model.CertificateResource `json:"resource,omitempty"`
|
||||
NotBefore time.Time `json:"-"`
|
||||
CertificateDir string `json:"-"`
|
||||
SSLCertificatePath string `json:"-"`
|
||||
SSLCertificateKeyPath string `json:"-"`
|
||||
CertID int `json:"cert_id"`
|
||||
ServerName []string `json:"server_name"`
|
||||
ChallengeMethod string `json:"challenge_method"`
|
||||
DNSCredentialID int `json:"dns_credential_id"`
|
||||
ACMEUserID int `json:"acme_user_id"`
|
||||
KeyType certcrypto.KeyType `json:"key_type"`
|
||||
Resource *model.CertificateResource `json:"resource,omitempty"`
|
||||
MustStaple bool `json:"must_staple"`
|
||||
LegoDisableCNAMESupport bool `json:"lego_disable_cname_support"`
|
||||
NotBefore time.Time `json:"-"`
|
||||
CertificateDir string `json:"-"`
|
||||
SSLCertificatePath string `json:"-"`
|
||||
SSLCertificateKeyPath string `json:"-"`
|
||||
}
|
||||
|
||||
func (c *ConfigPayload) GetACMEUser() (user *model.AcmeUser, err error) {
|
||||
|
|
|
@ -1,38 +1,39 @@
|
|||
package cert
|
||||
|
||||
import (
|
||||
"github.com/0xJacky/Nginx-UI/model"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/pkg/errors"
|
||||
"log"
|
||||
"github.com/0xJacky/Nginx-UI/model"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/pkg/errors"
|
||||
"log"
|
||||
)
|
||||
|
||||
func renew(payload *ConfigPayload, client *lego.Client, l *log.Logger, errChan chan error) {
|
||||
if payload.Resource == nil {
|
||||
errChan <- errors.New("resource is nil")
|
||||
return
|
||||
}
|
||||
if payload.Resource == nil {
|
||||
errChan <- errors.New("resource is nil")
|
||||
return
|
||||
}
|
||||
|
||||
options := &certificate.RenewOptions{
|
||||
Bundle: true,
|
||||
}
|
||||
options := &certificate.RenewOptions{
|
||||
Bundle: true,
|
||||
MustStaple: payload.MustStaple,
|
||||
}
|
||||
|
||||
cert, err := client.Certificate.RenewWithOptions(payload.Resource.GetResource(), options)
|
||||
if err != nil {
|
||||
errChan <- errors.Wrap(err, "renew cert error")
|
||||
return
|
||||
}
|
||||
cert, err := client.Certificate.RenewWithOptions(payload.Resource.GetResource(), options)
|
||||
if err != nil {
|
||||
errChan <- errors.Wrap(err, "renew cert error")
|
||||
return
|
||||
}
|
||||
|
||||
payload.Resource = &model.CertificateResource{
|
||||
Resource: cert,
|
||||
PrivateKey: cert.PrivateKey,
|
||||
Certificate: cert.Certificate,
|
||||
IssuerCertificate: cert.IssuerCertificate,
|
||||
CSR: cert.CSR,
|
||||
}
|
||||
payload.Resource = &model.CertificateResource{
|
||||
Resource: cert,
|
||||
PrivateKey: cert.PrivateKey,
|
||||
Certificate: cert.Certificate,
|
||||
IssuerCertificate: cert.IssuerCertificate,
|
||||
CSR: cert.CSR,
|
||||
}
|
||||
|
||||
payload.WriteFile(l, errChan)
|
||||
payload.WriteFile(l, errChan)
|
||||
|
||||
l.Println("[INFO] [Nginx UI] Certificate renewed successfully")
|
||||
l.Println("[INFO] [Nginx UI] Certificate renewed successfully")
|
||||
}
|
||||
|
|
|
@ -30,21 +30,23 @@ type CertificateResource struct {
|
|||
|
||||
type Cert struct {
|
||||
Model
|
||||
Name string `json:"name"`
|
||||
Domains pq.StringArray `json:"domains" gorm:"type:text[]"`
|
||||
Filename string `json:"filename"`
|
||||
SSLCertificatePath string `json:"ssl_certificate_path"`
|
||||
SSLCertificateKeyPath string `json:"ssl_certificate_key_path"`
|
||||
AutoCert int `json:"auto_cert"`
|
||||
ChallengeMethod string `json:"challenge_method"`
|
||||
DnsCredentialID int `json:"dns_credential_id"`
|
||||
DnsCredential *DnsCredential `json:"dns_credential,omitempty"`
|
||||
ACMEUserID int `json:"acme_user_id"`
|
||||
ACMEUser *AcmeUser `json:"acme_user,omitempty"`
|
||||
KeyType certcrypto.KeyType `json:"key_type"`
|
||||
Log string `json:"log"`
|
||||
Resource *CertificateResource `json:"-" gorm:"serializer:json"`
|
||||
SyncNodeIds []int `json:"sync_node_ids" gorm:"serializer:json"`
|
||||
Name string `json:"name"`
|
||||
Domains pq.StringArray `json:"domains" gorm:"type:text[]"`
|
||||
Filename string `json:"filename"`
|
||||
SSLCertificatePath string `json:"ssl_certificate_path"`
|
||||
SSLCertificateKeyPath string `json:"ssl_certificate_key_path"`
|
||||
AutoCert int `json:"auto_cert"`
|
||||
ChallengeMethod string `json:"challenge_method"`
|
||||
DnsCredentialID int `json:"dns_credential_id"`
|
||||
DnsCredential *DnsCredential `json:"dns_credential,omitempty"`
|
||||
ACMEUserID int `json:"acme_user_id"`
|
||||
ACMEUser *AcmeUser `json:"acme_user,omitempty"`
|
||||
KeyType certcrypto.KeyType `json:"key_type"`
|
||||
Log string `json:"log"`
|
||||
Resource *CertificateResource `json:"-" gorm:"serializer:json"`
|
||||
SyncNodeIds []int `json:"sync_node_ids" gorm:"serializer:json"`
|
||||
MustStaple bool `json:"must_staple"`
|
||||
LegoDisableCNAMESupport bool `json:"lego_disable_cname_support"`
|
||||
}
|
||||
|
||||
func FirstCert(confName string) (c Cert, err error) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue