Selaa lähdekoodia

feat: SSL management support different types of certificates of a same doamin name #309

Jacky 1 vuosi sitten
vanhempi
commit
3e90b838fd

+ 9 - 7
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 {

+ 7 - 4
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)

+ 2 - 0
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<Cert> = new Curd('/cert')

+ 2 - 0
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<Site> {

+ 2 - 0
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

+ 5 - 3
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 => {

+ 5 - 1
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'

+ 1 - 1
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 {

+ 5 - 0
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)
+}

+ 12 - 0
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
+}

+ 4 - 8
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)
 }