mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-10 18:05:48 +02:00
feat: SSL management support different types of certificates of a same doamin name #309
This commit is contained in:
parent
464e84a64f
commit
3e90b838fd
11 changed files with 54 additions and 24 deletions
|
@ -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 {
|
||||
|
|
|
@ -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,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')
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
internal/helper/key_type.go
Normal file
12
internal/helper/key_type.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue