mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 02:15:48 +02:00
feat: configurable cert key type #264
This commit is contained in:
parent
a095c88fd2
commit
22d62e420b
12 changed files with 115 additions and 28 deletions
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/0xJacky/Nginx-UI/model"
|
"github.com/0xJacky/Nginx-UI/model"
|
||||||
"github.com/0xJacky/Nginx-UI/query"
|
"github.com/0xJacky/Nginx-UI/query"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/go-acme/lego/v4/certcrypto"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
@ -76,13 +77,14 @@ func GetCert(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type certJson struct {
|
type certJson struct {
|
||||||
Name string `json:"name" binding:"required"`
|
Name string `json:"name" binding:"required"`
|
||||||
SSLCertificatePath string `json:"ssl_certificate_path" binding:"required,certificate_path"`
|
SSLCertificatePath string `json:"ssl_certificate_path" binding:"required,certificate_path"`
|
||||||
SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"required,privatekey_path"`
|
SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"required,privatekey_path"`
|
||||||
SSLCertificate string `json:"ssl_certificate" binding:"omitempty,certificate"`
|
SSLCertificate string `json:"ssl_certificate" binding:"omitempty,certificate"`
|
||||||
SSLCertificateKey string `json:"ssl_certificate_key" binding:"omitempty,privatekey"`
|
SSLCertificateKey string `json:"ssl_certificate_key" binding:"omitempty,privatekey"`
|
||||||
ChallengeMethod string `json:"challenge_method"`
|
KeyType certcrypto.KeyType `json:"key_type" binding:"omitempty,auto_cert_key_type"`
|
||||||
DnsCredentialID int `json:"dns_credential_id"`
|
ChallengeMethod string `json:"challenge_method"`
|
||||||
|
DnsCredentialID int `json:"dns_credential_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddCert(c *gin.Context) {
|
func AddCert(c *gin.Context) {
|
||||||
|
@ -96,6 +98,7 @@ func AddCert(c *gin.Context) {
|
||||||
Name: json.Name,
|
Name: json.Name,
|
||||||
SSLCertificatePath: json.SSLCertificatePath,
|
SSLCertificatePath: json.SSLCertificatePath,
|
||||||
SSLCertificateKeyPath: json.SSLCertificateKeyPath,
|
SSLCertificateKeyPath: json.SSLCertificateKeyPath,
|
||||||
|
KeyType: json.KeyType,
|
||||||
ChallengeMethod: json.ChallengeMethod,
|
ChallengeMethod: json.ChallengeMethod,
|
||||||
DnsCredentialID: json.DnsCredentialID,
|
DnsCredentialID: json.DnsCredentialID,
|
||||||
}
|
}
|
||||||
|
@ -146,6 +149,7 @@ func ModifyCert(c *gin.Context) {
|
||||||
SSLCertificatePath: json.SSLCertificatePath,
|
SSLCertificatePath: json.SSLCertificatePath,
|
||||||
SSLCertificateKeyPath: json.SSLCertificateKeyPath,
|
SSLCertificateKeyPath: json.SSLCertificateKeyPath,
|
||||||
ChallengeMethod: json.ChallengeMethod,
|
ChallengeMethod: json.ChallengeMethod,
|
||||||
|
KeyType: json.KeyType,
|
||||||
DnsCredentialID: json.DnsCredentialID,
|
DnsCredentialID: json.DnsCredentialID,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -122,6 +122,7 @@ func IssueCert(c *gin.Context) {
|
||||||
SSLCertificatePath: sslCertificatePath,
|
SSLCertificatePath: sslCertificatePath,
|
||||||
SSLCertificateKeyPath: sslCertificateKeyPath,
|
SSLCertificateKeyPath: sslCertificateKeyPath,
|
||||||
AutoCert: model.AutoCertEnabled,
|
AutoCert: model.AutoCertEnabled,
|
||||||
|
KeyType: payload.KeyType,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -14,6 +14,7 @@ export interface Cert extends ModelBase {
|
||||||
challenge_method: string
|
challenge_method: string
|
||||||
dns_credential_id: number
|
dns_credential_id: number
|
||||||
dns_credential?: DnsCredential
|
dns_credential?: DnsCredential
|
||||||
|
key_type: string
|
||||||
log: string
|
log: string
|
||||||
certificate_info: CertificateInfo
|
certificate_info: CertificateInfo
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ const data = inject('data') as Ref<Cert>
|
||||||
const issueCert = () => {
|
const issueCert = () => {
|
||||||
modalVisible.value = true
|
modalVisible.value = true
|
||||||
|
|
||||||
refObtainCertLive.value.issue_cert(data.value.name, data.value.domains).then(() => {
|
refObtainCertLive.value.issue_cert(data.value.name, data.value.domains, data.value.key_type).then(() => {
|
||||||
message.success($gettext('Renew successfully'))
|
message.success($gettext('Renew successfully'))
|
||||||
emit('renewed')
|
emit('renewed')
|
||||||
})
|
})
|
||||||
|
|
|
@ -44,10 +44,12 @@ const issueCert = () => {
|
||||||
step.value++
|
step.value++
|
||||||
modalVisible.value = true
|
modalVisible.value = true
|
||||||
|
|
||||||
refObtainCertLive.value.issue_cert(computedDomain.value, [computedDomain.value, domain.value]).then(() => {
|
refObtainCertLive.value.issue_cert(computedDomain.value,
|
||||||
message.success($gettext('Renew successfully'))
|
[computedDomain.value, domain.value])
|
||||||
emit('issued')
|
.then(() => {
|
||||||
})
|
message.success($gettext('Renew successfully'))
|
||||||
|
emit('issued')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { useGettext } from 'vue3-gettext'
|
||||||
import type { Ref } from 'vue'
|
import type { Ref } from 'vue'
|
||||||
import type { DnsChallenge } from '@/api/auto_cert'
|
import type { DnsChallenge } from '@/api/auto_cert'
|
||||||
import DNSChallenge from '@/views/domain/cert/components/DNSChallenge.vue'
|
import DNSChallenge from '@/views/domain/cert/components/DNSChallenge.vue'
|
||||||
|
import type { Cert } from '@/api/cert'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
hideNote?: boolean
|
hideNote?: boolean
|
||||||
|
@ -13,7 +14,39 @@ const { $gettext } = useGettext()
|
||||||
const no_server_name = inject('no_server_name')
|
const no_server_name = inject('no_server_name')
|
||||||
|
|
||||||
// Provide by ObtainCert.vue
|
// Provide by ObtainCert.vue
|
||||||
const data = inject('data') as Ref<DnsChallenge>
|
const data = inject('data') as Ref<DnsChallenge & Cert>
|
||||||
|
|
||||||
|
const keyType = shallowRef([
|
||||||
|
{
|
||||||
|
key: '2048',
|
||||||
|
name: 'RSA2048',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '3072',
|
||||||
|
name: 'RSA3072',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '4096',
|
||||||
|
name: 'RSA4096',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '8192',
|
||||||
|
name: 'RAS8192',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'P256',
|
||||||
|
name: 'EC256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'P384',
|
||||||
|
name: 'EC384',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (data.value.key_type === '')
|
||||||
|
data.value.key_type = 'RSA2048'
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -72,6 +105,17 @@ const data = inject('data') as Ref<DnsChallenge>
|
||||||
</ASelectOption>
|
</ASelectOption>
|
||||||
</ASelect>
|
</ASelect>
|
||||||
</AFormItem>
|
</AFormItem>
|
||||||
|
<AFormItem :label="$gettext('Key Type')">
|
||||||
|
<ASelect v-model:value="data.key_type">
|
||||||
|
<ASelectOption
|
||||||
|
v-for="t in keyType"
|
||||||
|
:key="t.key"
|
||||||
|
:value="t.key"
|
||||||
|
>
|
||||||
|
{{ t.name }}
|
||||||
|
</ASelectOption>
|
||||||
|
</ASelect>
|
||||||
|
</AFormItem>
|
||||||
</AForm>
|
</AForm>
|
||||||
<DNSChallenge v-if="data.challenge_method === 'dns01'" />
|
<DNSChallenge v-if="data.challenge_method === 'dns01'" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -59,7 +59,7 @@ function log(msg: string) {
|
||||||
logContainer.value?.scroll({ top: 100000, left: 0, behavior: 'smooth' })
|
logContainer.value?.scroll({ top: 100000, left: 0, behavior: 'smooth' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const issue_cert = async (config_name: string, server_name: string[]) => {
|
const issue_cert = async (config_name: string, server_name: string[], key_type: string) => {
|
||||||
return new Promise<CertificateResult>((resolve, reject) => {
|
return new Promise<CertificateResult>((resolve, reject) => {
|
||||||
progressStatus.value = 'active'
|
progressStatus.value = 'active'
|
||||||
modalClosable.value = false
|
modalClosable.value = false
|
||||||
|
@ -74,6 +74,7 @@ const issue_cert = async (config_name: string, server_name: string[]) => {
|
||||||
ws.onopen = () => {
|
ws.onopen = () => {
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
server_name,
|
server_name,
|
||||||
|
key_type,
|
||||||
...data.value,
|
...data.value,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,7 @@ func renew(certModel *model.Cert) {
|
||||||
ServerName: certModel.Domains,
|
ServerName: certModel.Domains,
|
||||||
ChallengeMethod: certModel.ChallengeMethod,
|
ChallengeMethod: certModel.ChallengeMethod,
|
||||||
DNSCredentialID: certModel.DnsCredentialID,
|
DNSCredentialID: certModel.DnsCredentialID,
|
||||||
|
KeyType: certModel.GetKeyType(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// errChan will be closed inside IssueCert
|
// errChan will be closed inside IssueCert
|
||||||
|
|
|
@ -32,9 +32,10 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigPayload struct {
|
type ConfigPayload struct {
|
||||||
ServerName []string `json:"server_name"`
|
ServerName []string `json:"server_name"`
|
||||||
ChallengeMethod string `json:"challenge_method"`
|
ChallengeMethod string `json:"challenge_method"`
|
||||||
DNSCredentialID int `json:"dns_credential_id"`
|
DNSCredentialID int `json:"dns_credential_id"`
|
||||||
|
KeyType certcrypto.KeyType `json:"key_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func IssueCert(payload *ConfigPayload, logChan chan string, errChan chan error) {
|
func IssueCert(payload *ConfigPayload, logChan chan string, errChan chan error) {
|
||||||
|
@ -93,7 +94,7 @@ func IssueCert(payload *ConfigPayload, logChan chan string, errChan chan error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Certificate.KeyType = certcrypto.RSA2048
|
config.Certificate.KeyType = payload.KeyType
|
||||||
|
|
||||||
l.Println("[INFO] [Nginx UI] Creating client facilitates communication with the CA server")
|
l.Println("[INFO] [Nginx UI] Creating client facilitates communication with the CA server")
|
||||||
// A client facilitates communication with the CA server.
|
// A client facilitates communication with the CA server.
|
||||||
|
|
15
internal/validation/key_type.go
Normal file
15
internal/validation/key_type.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package validation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-acme/lego/v4/certcrypto"
|
||||||
|
val "github.com/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
func autoCertKeyType(fl val.FieldLevel) bool {
|
||||||
|
switch certcrypto.KeyType(fl.Field().String()) {
|
||||||
|
case certcrypto.RSA2048, certcrypto.RSA3072, certcrypto.RSA4096,
|
||||||
|
certcrypto.EC256, certcrypto.EC384:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -42,5 +42,11 @@ func Init() {
|
||||||
logger.Fatal(err)
|
logger.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = v.RegisterValidation("auto_cert_key_type", autoCertKeyType)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||||
|
"github.com/go-acme/lego/v4/certcrypto"
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
@ -17,16 +18,17 @@ type CertDomains []string
|
||||||
|
|
||||||
type Cert struct {
|
type Cert struct {
|
||||||
Model
|
Model
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Domains pq.StringArray `json:"domains" gorm:"type:text[]"`
|
Domains pq.StringArray `json:"domains" gorm:"type:text[]"`
|
||||||
Filename string `json:"filename"`
|
Filename string `json:"filename"`
|
||||||
SSLCertificatePath string `json:"ssl_certificate_path"`
|
SSLCertificatePath string `json:"ssl_certificate_path"`
|
||||||
SSLCertificateKeyPath string `json:"ssl_certificate_key_path"`
|
SSLCertificateKeyPath string `json:"ssl_certificate_key_path"`
|
||||||
AutoCert int `json:"auto_cert"`
|
AutoCert int `json:"auto_cert"`
|
||||||
ChallengeMethod string `json:"challenge_method"`
|
ChallengeMethod string `json:"challenge_method"`
|
||||||
DnsCredentialID int `json:"dns_credential_id"`
|
DnsCredentialID int `json:"dns_credential_id"`
|
||||||
DnsCredential *DnsCredential `json:"dns_credential,omitempty"`
|
DnsCredential *DnsCredential `json:"dns_credential,omitempty"`
|
||||||
Log string `json:"log"`
|
KeyType certcrypto.KeyType `json:"key_type"`
|
||||||
|
Log string `json:"log"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func FirstCert(confName string) (c Cert, err error) {
|
func FirstCert(confName string) (c Cert, err error) {
|
||||||
|
@ -90,3 +92,12 @@ func (c *Cert) Remove() error {
|
||||||
|
|
||||||
return db.Where("filename", c.Filename).Delete(c).Error
|
return db.Where("filename", c.Filename).Delete(c).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cert) GetKeyType() certcrypto.KeyType {
|
||||||
|
switch c.KeyType {
|
||||||
|
case certcrypto.RSA2048, certcrypto.RSA3072, certcrypto.RSA4096,
|
||||||
|
certcrypto.EC256, certcrypto.EC384:
|
||||||
|
return c.KeyType
|
||||||
|
}
|
||||||
|
return certcrypto.RSA2048
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue