Browse Source

Get certificate info from local file

0xJacky 2 years ago
parent
commit
e28e46db32

+ 2 - 2
frontend/src/api/domain.ts

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

+ 1 - 1
frontend/src/version.json

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

+ 6 - 1
frontend/src/views/domain/cert/Cert.vue

@@ -23,6 +23,11 @@ const name = computed(() => {
     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({
     get() {
         return props.enabled
@@ -36,7 +41,7 @@ const enabled = computed({
 
 <template>
     <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
             :current_server_directives="props.current_server_directives"
             :directives-map="props.directivesMap"

+ 2 - 2
frontend/src/views/domain/cert/CertInfo.vue

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

+ 1 - 1
frontend/version.json

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

+ 145 - 125
server/api/cert.go

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

+ 1 - 0
server/api/domain.go

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

+ 7 - 2
server/model/cert.go

@@ -8,7 +8,8 @@ import (
 
 type Cert struct {
 	Model
-	Domain string `json:"domain"`
+	Domain             string `json:"domain"`
+	SSLCertificatePath string `json:"ssl_certificate_path"`
 }
 
 func FirstCert(domain string) (c Cert, err error) {
@@ -27,8 +28,8 @@ func FirstOrCreateCert(domain string) (c Cert, err error) {
 func GetAutoCertList() (c []Cert) {
 	var t []Cert
 	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")))
 
 	if err != nil {
@@ -49,6 +50,10 @@ func GetAutoCertList() (c []Cert) {
 	return
 }
 
+func (c *Cert) Updates(n *Cert) error {
+	return db.Model(c).Updates(n).Error
+}
+
 func (c *Cert) Remove() error {
 	return db.Where("domain", c.Domain).Delete(c).Error
 }

+ 16 - 2
server/pkg/cert/auto_cert.go

@@ -16,14 +16,28 @@ func AutoCert() {
 	autoCertList := model.GetAutoCertList()
 	for i := range autoCertList {
 		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 {
 			log.Println("GetCertInfo Err", err)
 			// Get certificate info error, ignore this domain
 			continue
 		}
 		// before 1 mo
-		if time.Now().Before(key.NotBefore.AddDate(0, 1, 0)) {
+		if time.Now().Before(cert.NotBefore.AddDate(0, 1, 0)) {
 			continue
 		}
 		// after 1 mo, reissue certificate

+ 15 - 29
server/pkg/cert/cert_info.go

@@ -1,47 +1,33 @@
 package cert
 
 import (
-	"crypto/tls"
 	"crypto/x509"
+	"encoding/pem"
 	"github.com/pkg/errors"
-	"io"
-	"log"
-	"net"
-	"net/http"
-	"time"
+	"os"
 )
 
-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,
+	if err != nil {
+		err = errors.Wrap(err, "error read certificate")
+		return
 	}
 
-	response, err = client.Get("https://" + domain)
+	block, _ := pem.Decode(certData)
 
-	if err != nil {
-		err = errors.Wrap(err, "get cert info error")
+	if block == nil || block.Type != "CERTIFICATE" {
+		err = errors.New("certificate decoding error")
 		return
 	}
 
-	defer func(Body io.ReadCloser) {
-		err = Body.Close()
-		if err != nil {
-			log.Println(err)
-			return
-		}
-	}(response.Body)
+	cert, err = x509.ParseCertificate(block.Bytes)
 
-	key = response.TLS.PeerCertificates[0]
+	if err != nil {
+		err = errors.Wrap(err, "certificate parsing error")
+		return
+	}
 
 	return
 }

+ 1 - 1
server/router/routers.go

@@ -84,7 +84,7 @@ func InitRouter() *gin.Engine {
 			g.GET("template", api.GetTemplate)
 
 			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
 			g.POST("cert/:domain", api.AddDomainToAutoCert)