diff --git a/api/certificate/issue.go b/api/certificate/issue.go index 2251191a..2139ab2c 100644 --- a/api/certificate/issue.go +++ b/api/certificate/issue.go @@ -6,6 +6,7 @@ import ( "github.com/0xJacky/Nginx-UI/internal/nginx" "github.com/0xJacky/Nginx-UI/model" "github.com/gin-gonic/gin" + "github.com/go-acme/lego/v4/certcrypto" "github.com/gorilla/websocket" "net/http" "strings" @@ -18,10 +19,11 @@ const ( ) type IssueCertResponse struct { - Status string `json:"status"` - Message string `json:"message"` - SSLCertificate string `json:"ssl_certificate,omitempty"` - SSLCertificateKey string `json:"ssl_certificate_key,omitempty"` + Status string `json:"status"` + Message string `json:"message"` + SSLCertificate string `json:"ssl_certificate,omitempty"` + SSLCertificateKey string `json:"ssl_certificate_key,omitempty"` + KeyType certcrypto.KeyType `json:"key_type"` } func handleIssueCertLogChan(conn *websocket.Conn, log *cert.Logger, logChan chan string) { @@ -75,8 +77,7 @@ func IssueCert(c *gin.Context) { return } - certModel, err := model.FirstOrCreateCert(c.Param("name")) - + certModel, err := model.FirstOrCreateCert(c.Param("name"), payload.GetKeyType()) if err != nil { logger.Error(err) return @@ -113,7 +114,7 @@ func IssueCert(c *gin.Context) { return } - certDirName := strings.Join(payload.ServerName, "_") + certDirName := strings.Join(payload.ServerName, "_") + "_" + string(payload.GetKeyType()) sslCertificatePath := nginx.GetConfPath("ssl", certDirName, "fullchain.cer") sslCertificateKeyPath := nginx.GetConfPath("ssl", certDirName, "private.key") @@ -144,6 +145,7 @@ func IssueCert(c *gin.Context) { Message: "Issued certificate successfully", SSLCertificate: sslCertificatePath, SSLCertificateKey: sslCertificateKeyPath, + KeyType: payload.GetKeyType(), }) if err != nil { diff --git a/api/sites/auto_cert.go b/api/sites/auto_cert.go index 2319ff82..9348f205 100644 --- a/api/sites/auto_cert.go +++ b/api/sites/auto_cert.go @@ -2,8 +2,10 @@ package sites import ( "github.com/0xJacky/Nginx-UI/api" + "github.com/0xJacky/Nginx-UI/internal/helper" "github.com/0xJacky/Nginx-UI/model" "github.com/gin-gonic/gin" + "github.com/go-acme/lego/v4/certcrypto" "net/http" ) @@ -11,16 +13,17 @@ func AddDomainToAutoCert(c *gin.Context) { name := c.Param("name") var json struct { - DnsCredentialID int `json:"dns_credential_id"` - ChallengeMethod string `json:"challenge_method"` - Domains []string `json:"domains"` + DnsCredentialID int `json:"dns_credential_id"` + ChallengeMethod string `json:"challenge_method"` + Domains []string `json:"domains"` + KeyType certcrypto.KeyType `json:"key_type"` } if !api.BindAndValid(c, &json) { return } - certModel, err := model.FirstOrCreateCert(name) + certModel, err := model.FirstOrCreateCert(name, helper.GetKeyType(json.KeyType)) if err != nil { api.ErrHandler(c, err) diff --git a/app/src/api/cert.ts b/app/src/api/cert.ts index 2be1fb02..448d41dd 100644 --- a/app/src/api/cert.ts +++ b/app/src/api/cert.ts @@ -2,6 +2,7 @@ import type { ModelBase } from '@/api/curd' import Curd from '@/api/curd' import type { DnsCredential } from '@/api/dns_credential' import type { AcmeUser } from '@/api/acme_user' +import type { PrivateKeyType } from '@/constants' export interface Cert extends ModelBase { name: string @@ -32,6 +33,7 @@ export interface CertificateInfo { export interface CertificateResult { ssl_certificate: string ssl_certificate_key: string + key_type: PrivateKeyType } const cert: Curd = new Curd('/cert') diff --git a/app/src/api/domain.ts b/app/src/api/domain.ts index 1475f66a..cc655bd5 100644 --- a/app/src/api/domain.ts +++ b/app/src/api/domain.ts @@ -3,6 +3,7 @@ import http from '@/lib/http' import type { ChatComplicationMessage } from '@/api/openai' import type { CertificateInfo } from '@/api/cert' import type { NgxConfig } from '@/api/ngx' +import type { PrivateKeyType } from '@/constants' export interface Site { modified_at: string @@ -22,6 +23,7 @@ export interface AutoCertRequest { dns_credential_id: number | null challenge_method: string domains: string[] + key_type: PrivateKeyType } class Domain extends Curd { diff --git a/app/src/constants/index.ts b/app/src/constants/index.ts index b8606cc8..23580867 100644 --- a/app/src/constants/index.ts +++ b/app/src/constants/index.ts @@ -36,3 +36,5 @@ export const PrivateKeyTypeMask = { } as const export const PrivateKeyTypeList = Object.entries(PrivateKeyTypeMask).map(([key, name]) => ({ key, name })) + +export type PrivateKeyType = keyof typeof PrivateKeyTypeMask diff --git a/app/src/views/domain/cert/components/ObtainCert.vue b/app/src/views/domain/cert/components/ObtainCert.vue index b0cb19f6..88ab21aa 100644 --- a/app/src/views/domain/cert/components/ObtainCert.vue +++ b/app/src/views/domain/cert/components/ObtainCert.vue @@ -8,6 +8,7 @@ import type { Props } from '@/views/domain/cert/IssueCert.vue' import type { DnsChallenge } 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 emit = defineEmits(['update:auto_cert']) @@ -48,20 +49,21 @@ const issue_cert = (config_name: string, server_name: string) => { refObtainCertLive.value.issue_cert(config_name, server_name.trim().split(' ')).then(resolveCert) } -async function resolveCert({ ssl_certificate, ssl_certificate_key }: CertificateResult) { +async function resolveCert({ ssl_certificate, ssl_certificate_key, key_type }: CertificateResult) { directivesMap.value.ssl_certificate[0].params = ssl_certificate directivesMap.value.ssl_certificate_key[0].params = ssl_certificate_key await save_config() - change_auto_cert(true) + change_auto_cert(true, key_type) emit('update:auto_cert', true) } -function change_auto_cert(status: boolean) { +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, + key_type: key_type!, }).then(() => { message.success($gettext('Auto-renewal enabled for %{name}', { name: name.value })) }).catch(e => { diff --git a/app/src/views/domain/cert/components/ObtainCertLive.vue b/app/src/views/domain/cert/components/ObtainCertLive.vue index 0196cc2d..e9314f59 100644 --- a/app/src/views/domain/cert/components/ObtainCertLive.vue +++ b/app/src/views/domain/cert/components/ObtainCertLive.vue @@ -106,7 +106,11 @@ const issue_cert = async (config_name: string, server_name: string[], key_type: if (r.status === 'success' && r.ssl_certificate !== undefined && r.ssl_certificate_key !== undefined) { progressStatus.value = 'success' progressPercent.value = 100 - resolve({ ssl_certificate: r.ssl_certificate, ssl_certificate_key: r.ssl_certificate_key }) + resolve({ + ssl_certificate: r.ssl_certificate, + ssl_certificate_key: r.ssl_certificate_key, + key_type: r.key_type, + }) } else { progressStatus.value = 'exception' diff --git a/internal/cert/cert.go b/internal/cert/cert.go index c1e9fb8f..3664b125 100644 --- a/internal/cert/cert.go +++ b/internal/cert/cert.go @@ -167,7 +167,7 @@ func IssueCert(payload *ConfigPayload, logChan chan string, errChan chan error) return } name := strings.Join(domain, "_") - saveDir := nginx.GetConfPath("ssl/" + name) + saveDir := nginx.GetConfPath("ssl/" + name + "_" + string(payload.KeyType)) if _, err = os.Stat(saveDir); os.IsNotExist(err) { err = os.MkdirAll(saveDir, 0755) if err != nil { diff --git a/internal/cert/payload.go b/internal/cert/payload.go index 97fecc5f..ade08fc7 100644 --- a/internal/cert/payload.go +++ b/internal/cert/payload.go @@ -1,6 +1,7 @@ package cert import ( + "github.com/0xJacky/Nginx-UI/internal/helper" "github.com/go-acme/lego/v4/certcrypto" ) @@ -10,3 +11,7 @@ type ConfigPayload struct { DNSCredentialID int `json:"dns_credential_id"` KeyType certcrypto.KeyType `json:"key_type"` } + +func (c *ConfigPayload) GetKeyType() certcrypto.KeyType { + return helper.GetKeyType(c.KeyType) +} diff --git a/internal/helper/key_type.go b/internal/helper/key_type.go new file mode 100644 index 00000000..4fbb6abf --- /dev/null +++ b/internal/helper/key_type.go @@ -0,0 +1,12 @@ +package helper + +import "github.com/go-acme/lego/v4/certcrypto" + +func GetKeyType(keyType certcrypto.KeyType) certcrypto.KeyType { + switch keyType { + case certcrypto.RSA2048, certcrypto.RSA3072, certcrypto.RSA4096, + certcrypto.EC256, certcrypto.EC384: + return keyType + } + return certcrypto.RSA2048 +} diff --git a/model/cert.go b/model/cert.go index f2705a56..442fff84 100644 --- a/model/cert.go +++ b/model/cert.go @@ -1,6 +1,7 @@ package model import ( + "github.com/0xJacky/Nginx-UI/internal/helper" "github.com/0xJacky/Nginx-UI/internal/nginx" "github.com/go-acme/lego/v4/certcrypto" "github.com/lib/pq" @@ -41,9 +42,9 @@ func FirstCert(confName string) (c Cert, err error) { return } -func FirstOrCreateCert(confName string) (c Cert, err error) { +func FirstOrCreateCert(confName string, keyType certcrypto.KeyType) (c Cert, err error) { // Filename is used to check whether this site is enabled - err = db.FirstOrCreate(&c, &Cert{Name: confName, Filename: confName}).Error + err = db.FirstOrCreate(&c, &Cert{Name: confName, Filename: confName, KeyType: keyType}).Error return } @@ -96,10 +97,5 @@ func (c *Cert) Remove() 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 + return helper.GetKeyType(c.KeyType) }