mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 02:15:48 +02:00
Get certificate info from local file
This commit is contained in:
parent
c747390f94
commit
e28e46db32
11 changed files with 175 additions and 144 deletions
|
@ -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) {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"version":"1.5.0","build_id":40,"total_build":110}
|
{"version":"1.5.0","build_id":41,"total_build":111}
|
|
@ -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"
|
||||||
|
|
|
@ -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(() => {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"version":"1.5.0","build_id":40,"total_build":110}
|
{"version":"1.5.0","build_id":41,"total_build":111}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue