Get certificate info from local file

This commit is contained in:
0xJacky 2022-08-12 00:23:42 +08:00
parent c747390f94
commit e28e46db32
No known key found for this signature in database
GPG key ID: B6E4A6E4A561BAF0
11 changed files with 175 additions and 144 deletions

View file

@ -14,8 +14,8 @@ class Domain extends Curd {
return http.get('template') return http.get('template')
} }
cert_info(domain: string) { cert_info(path: string) {
return http.get('cert/' + domain + '/info') return http.get('cert_info?ssl_certificate_path=' + path)
} }
add_auto_cert(domain: string) { add_auto_cert(domain: string) {

View file

@ -1 +1 @@
{"version":"1.5.0","build_id":40,"total_build":110} {"version":"1.5.0","build_id":41,"total_build":111}

View file

@ -23,6 +23,11 @@ const name = computed(() => {
return props.directivesMap['server_name'][0].params.trim() return props.directivesMap['server_name'][0].params.trim()
}) })
const ssl_certificate_path = computed(() => {
return props.directivesMap['ssl_certificate']?.[0].params.trim() ?? null
})
const enabled = computed({ const enabled = computed({
get() { get() {
return props.enabled return props.enabled
@ -36,7 +41,7 @@ const enabled = computed({
<template> <template>
<div> <div>
<cert-info ref="info" :domain="name" v-if="name"/> <cert-info ref="info" :ssl_certificate_path="ssl_certificate_path" v-if="ssl_certificate_path"/>
<issue-cert <issue-cert
:current_server_directives="props.current_server_directives" :current_server_directives="props.current_server_directives"
:directives-map="props.directivesMap" :directives-map="props.directivesMap"

View file

@ -4,7 +4,7 @@ import dayjs from 'dayjs'
import {reactive, ref} from 'vue' import {reactive, ref} from 'vue'
import domain from '@/api/domain' import domain from '@/api/domain'
const props = defineProps(['domain']) const props = defineProps(['ssl_certificate_path'])
const ok = ref(false) const ok = ref(false)
const cert = reactive({issuer_name: '', subject_name: '', not_after: '', not_before: ''}) const cert = reactive({issuer_name: '', subject_name: '', not_after: '', not_before: ''})
@ -12,7 +12,7 @@ const cert = reactive({issuer_name: '', subject_name: '', not_after: '', not_bef
get() get()
function get() { function get() {
domain.cert_info(props.domain).then((r: any) => { domain.cert_info(props.ssl_certificate_path).then((r: any) => {
Object.assign(cert, r) Object.assign(cert, r)
ok.value = true ok.value = true
}).catch(() => { }).catch(() => {

View file

@ -1 +1 @@
{"version":"1.5.0","build_id":40,"total_build":110} {"version":"1.5.0","build_id":41,"total_build":111}

View file

@ -1,135 +1,155 @@
package api package api
import ( import (
"github.com/0xJacky/Nginx-UI/server/pkg/cert" "github.com/0xJacky/Nginx-UI/server/model"
"github.com/0xJacky/Nginx-UI/server/pkg/nginx" "github.com/0xJacky/Nginx-UI/server/pkg/cert"
"github.com/gin-gonic/gin" "github.com/0xJacky/Nginx-UI/server/pkg/nginx"
"github.com/gorilla/websocket" "github.com/gin-gonic/gin"
"log" "github.com/gorilla/websocket"
"net/http" "log"
"os" "net/http"
"os"
) )
func CertInfo(c *gin.Context) { func CertInfo(c *gin.Context) {
domain := c.Param("domain") path := c.Query("ssl_certificate_path")
key, err := cert.GetCertInfo(domain) log.Println(path)
if err != nil { key, err := cert.GetCertInfo(path)
c.JSON(http.StatusOK, gin.H{
"message": "Failed to get cert information",
"error": err,
})
return
}
c.JSON(http.StatusOK, gin.H{ if err != nil {
"subject_name": key.Subject.CommonName, c.JSON(http.StatusInternalServerError, gin.H{
"issuer_name": key.Issuer.CommonName, "message": "Failed to get certificate information",
"not_after": key.NotAfter, "error": err.Error(),
"not_before": key.NotBefore, })
}) return
}
c.JSON(http.StatusOK, gin.H{
"subject_name": key.Subject.CommonName,
"issuer_name": key.Issuer.CommonName,
"not_after": key.NotAfter,
"not_before": key.NotBefore,
})
} }
func IssueCert(c *gin.Context) { func IssueCert(c *gin.Context) {
domain := c.Param("domain") domain := c.Param("domain")
var upGrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// upgrade http to websocket var upGrader = websocket.Upgrader{
ws, err := upGrader.Upgrade(c.Writer, c.Request, nil) CheckOrigin: func(r *http.Request) bool {
if err != nil { return true
log.Println(err) },
return }
}
defer func(ws *websocket.Conn) { // upgrade http to websocket
err := ws.Close() ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if err != nil { if err != nil {
log.Println("defer websocket close err", err) log.Println(err)
} return
}(ws) }
// read defer func(ws *websocket.Conn) {
mt, message, err := ws.ReadMessage() err := ws.Close()
if err != nil { if err != nil {
log.Println(err) log.Println("defer websocket close err", err)
return }
} }(ws)
if mt == websocket.TextMessage && string(message) == "go" { // read
mt, message, err := ws.ReadMessage()
if err != nil {
log.Println(err)
return
}
err = cert.IssueCert(domain) if mt == websocket.TextMessage && string(message) == "go" {
if err != nil { err = cert.IssueCert(domain)
log.Println(err) if err != nil {
err = ws.WriteJSON(gin.H{ log.Println(err)
"status": "error",
"message": err.Error(),
})
if err != nil { err = ws.WriteJSON(gin.H{
log.Println(err) "status": "error",
return "message": err.Error(),
} })
return if err != nil {
} log.Println(err)
return
}
sslCertificatePath := nginx.GetNginxConfPath("ssl/" + domain + "/fullchain.cer") return
_, err = os.Stat(sslCertificatePath) }
if err != nil { sslCertificatePath := nginx.GetNginxConfPath("ssl/" + domain + "/fullchain.cer")
log.Println(err) _, err = os.Stat(sslCertificatePath)
return
}
log.Println("[found]", "fullchain.cer") if err != nil {
log.Println(err)
return
}
err = ws.WriteJSON(gin.H{ log.Println("[found]", "fullchain.cer")
"status": "success",
"message": "[found] fullchain.cer",
})
if err != nil { err = ws.WriteJSON(gin.H{
log.Println(err) "status": "success",
return "message": "[found] fullchain.cer",
} })
sslCertificateKeyPath := nginx.GetNginxConfPath("ssl/" + domain + "/" + domain + ".key") if err != nil {
_, err = os.Stat(sslCertificateKeyPath) log.Println(err)
return
}
if err != nil { sslCertificateKeyPath := nginx.GetNginxConfPath("ssl/" + domain + "/" + domain + ".key")
log.Println(err) _, err = os.Stat(sslCertificateKeyPath)
return
}
log.Println("[found]", "cert key") if err != nil {
err = ws.WriteJSON(gin.H{ log.Println(err)
"status": "success", return
"message": "[found] Certificate Key", }
})
if err != nil { log.Println("[found]", "cert key")
log.Println(err) err = ws.WriteJSON(gin.H{
return "status": "success",
} "message": "[found] Certificate Key",
})
err = ws.WriteJSON(gin.H{ if err != nil {
"status": "success", log.Println(err)
"message": "Issued certificate successfully", return
"ssl_certificate": sslCertificatePath, }
"ssl_certificate_key": sslCertificateKeyPath,
})
if err != nil { certModel, err := model.FirstCert(domain)
log.Println(err)
return if err != nil {
} log.Println(err)
} return
}
err = certModel.Updates(&model.Cert{
SSLCertificatePath: sslCertificatePath,
})
if err != nil {
log.Println(err)
return
}
err = ws.WriteJSON(gin.H{
"status": "success",
"message": "Issued certificate successfully",
"ssl_certificate": sslCertificatePath,
"ssl_certificate_key": sslCertificateKeyPath,
})
if err != nil {
log.Println(err)
return
}
}
} }

View file

@ -248,6 +248,7 @@ func DeleteDomain(c *gin.Context) {
func AddDomainToAutoCert(c *gin.Context) { func AddDomainToAutoCert(c *gin.Context) {
domain := c.Param("domain") domain := c.Param("domain")
cert, err := model.FirstOrCreateCert(domain) cert, err := model.FirstOrCreateCert(domain)
if err != nil { if err != nil {
ErrHandler(c, err) ErrHandler(c, err)

View file

@ -8,7 +8,8 @@ import (
type Cert struct { type Cert struct {
Model Model
Domain string `json:"domain"` Domain string `json:"domain"`
SSLCertificatePath string `json:"ssl_certificate_path"`
} }
func FirstCert(domain string) (c Cert, err error) { func FirstCert(domain string) (c Cert, err error) {
@ -27,8 +28,8 @@ func FirstOrCreateCert(domain string) (c Cert, err error) {
func GetAutoCertList() (c []Cert) { func GetAutoCertList() (c []Cert) {
var t []Cert var t []Cert
db.Find(&t) db.Find(&t)
// check if this domain is enabled
// check if this domain is enabled
enabledConfig, err := os.ReadDir(filepath.Join(nginx.GetNginxConfPath("sites-enabled"))) enabledConfig, err := os.ReadDir(filepath.Join(nginx.GetNginxConfPath("sites-enabled")))
if err != nil { if err != nil {
@ -49,6 +50,10 @@ func GetAutoCertList() (c []Cert) {
return return
} }
func (c *Cert) Updates(n *Cert) error {
return db.Model(c).Updates(n).Error
}
func (c *Cert) Remove() error { func (c *Cert) Remove() error {
return db.Where("domain", c.Domain).Delete(c).Error return db.Where("domain", c.Domain).Delete(c).Error
} }

View file

@ -16,14 +16,28 @@ func AutoCert() {
autoCertList := model.GetAutoCertList() autoCertList := model.GetAutoCertList()
for i := range autoCertList { for i := range autoCertList {
domain := autoCertList[i].Domain domain := autoCertList[i].Domain
key, err := GetCertInfo(domain)
certModel, err := model.FirstCert(domain)
if err != nil {
log.Println("[AutoCert] Error get certificate from database", err)
continue
}
if certModel.SSLCertificatePath != "" {
log.Println("[AutoCert] Error ssl_certificate_path is empty, " +
"try to reopen auto-cert for this domain:" + domain)
continue
}
cert, err := GetCertInfo(certModel.SSLCertificatePath)
if err != nil { if err != nil {
log.Println("GetCertInfo Err", err) log.Println("GetCertInfo Err", err)
// Get certificate info error, ignore this domain // Get certificate info error, ignore this domain
continue continue
} }
// before 1 mo // before 1 mo
if time.Now().Before(key.NotBefore.AddDate(0, 1, 0)) { if time.Now().Before(cert.NotBefore.AddDate(0, 1, 0)) {
continue continue
} }
// after 1 mo, reissue certificate // after 1 mo, reissue certificate

View file

@ -1,47 +1,33 @@
package cert package cert
import ( import (
"crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/pem"
"github.com/pkg/errors" "github.com/pkg/errors"
"io" "os"
"log"
"net"
"net/http"
"time"
) )
func GetCertInfo(domain string) (key *x509.Certificate, err error) { func GetCertInfo(sslCertificatePath string) (cert *x509.Certificate, err error) {
certData, err := os.ReadFile(sslCertificatePath)
var response *http.Response
client := &http.Client{
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: 5 * time.Second,
}).DialContext,
DisableKeepAlives: true,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
Timeout: 5 * time.Second,
}
response, err = client.Get("https://" + domain)
if err != nil { if err != nil {
err = errors.Wrap(err, "get cert info error") err = errors.Wrap(err, "error read certificate")
return return
} }
defer func(Body io.ReadCloser) { block, _ := pem.Decode(certData)
err = Body.Close()
if err != nil {
log.Println(err)
return
}
}(response.Body)
key = response.TLS.PeerCertificates[0] if block == nil || block.Type != "CERTIFICATE" {
err = errors.New("certificate decoding error")
return
}
cert, err = x509.ParseCertificate(block.Bytes)
if err != nil {
err = errors.Wrap(err, "certificate parsing error")
return
}
return return
} }

View file

@ -84,7 +84,7 @@ func InitRouter() *gin.Engine {
g.GET("template", api.GetTemplate) g.GET("template", api.GetTemplate)
g.GET("cert/issue/:domain", api.IssueCert) g.GET("cert/issue/:domain", api.IssueCert)
g.GET("cert/:domain/info", api.CertInfo) g.GET("cert_info", api.CertInfo)
// Add domain to auto-renew cert list // Add domain to auto-renew cert list
g.POST("cert/:domain", api.AddDomainToAutoCert) g.POST("cert/:domain", api.AddDomainToAutoCert)