Skip to content

Commit

Permalink
feat: SSL management support different types of certificates of a sam…
Browse files Browse the repository at this point in the history
…e doamin name #309
  • Loading branch information
0xJacky committed Apr 30, 2024
1 parent 464e84a commit 3e90b83
Show file tree
Hide file tree
Showing 11 changed files with 54 additions and 24 deletions.
16 changes: 9 additions & 7 deletions api/certificate/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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")

Expand Down Expand Up @@ -144,6 +145,7 @@ func IssueCert(c *gin.Context) {
Message: "Issued certificate successfully",
SSLCertificate: sslCertificatePath,
SSLCertificateKey: sslCertificateKeyPath,
KeyType: payload.GetKeyType(),
})

if err != nil {
Expand Down
11 changes: 7 additions & 4 deletions api/sites/auto_cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,28 @@ 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"
)

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)
Expand Down
2 changes: 2 additions & 0 deletions app/src/api/cert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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')
Expand Down
2 changes: 2 additions & 0 deletions app/src/api/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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> {
Expand Down
2 changes: 2 additions & 0 deletions app/src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 5 additions & 3 deletions app/src/views/domain/cert/components/ObtainCert.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'])
Expand Down Expand Up @@ -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 => {
Expand Down
6 changes: 5 additions & 1 deletion app/src/views/domain/cert/components/ObtainCertLive.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
2 changes: 1 addition & 1 deletion internal/cert/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 5 additions & 0 deletions internal/cert/payload.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cert

import (
"github.com/0xJacky/Nginx-UI/internal/helper"
"github.com/go-acme/lego/v4/certcrypto"
)

Expand All @@ -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 changes: 12 additions & 0 deletions internal/helper/key_type.go
Original file line number Diff line number Diff line change
@@ -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
}
12 changes: 4 additions & 8 deletions model/cert.go
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
}

0 comments on commit 3e90b83

Please sign in to comment.