Parcourir la source

wip: support SAN certifications

0xJacky il y a 2 ans
Parent
commit
7d4999f83a

+ 7 - 24
frontend/src/views/domain/cert/IssueCert.vue

@@ -31,12 +31,6 @@ function job() {
         return
     }
 
-    if (server_name_more_than_one.value) {
-        message.error($gettext('server_name parameters more than one'))
-        issuing_cert.value = false
-        return
-    }
-
     const server_name = props.directivesMap['server_name'][0]
 
     if (!props.directivesMap['ssl_certificate']) {
@@ -102,10 +96,12 @@ const issue_cert = async (server_name: string, callback: Function) => {
 
     log($gettext('Getting the certificate, please wait...'))
 
-    const ws = websocket('/api/cert/issue/' + server_name, false)
+    const ws = websocket('/api/cert/issue', false)
 
     ws.onopen = () => {
-        ws.send('go')
+        ws.send(JSON.stringify({
+            server_name: server_name.trim().split(' ')
+        }))
     }
 
     ws.onmessage = m => {
@@ -132,11 +128,6 @@ const issue_cert = async (server_name: string, callback: Function) => {
     }
 }
 
-const server_name_more_than_one = computed(() => {
-    return props.directivesMap['server_name'] && (props.directivesMap['server_name'].length > 1 ||
-        props.directivesMap['server_name'][0].params.trim().indexOf(' ') > 0)
-})
-
 const no_server_name = computed(() => {
     return props.directivesMap['server_name'].length === 0
 })
@@ -154,11 +145,6 @@ const enabled = computed({
     }
 })
 
-watch(server_name_more_than_one, () => {
-    emit('update:enabled', false)
-    onchange(false)
-})
-
 watch(no_server_name, () => {
     emit('update:enabled', false)
     onchange(false)
@@ -166,7 +152,7 @@ watch(no_server_name, () => {
 
 const progressStrokeColor = {
     from: '#108ee9',
-    to: '#87d068',
+    to: '#87d068'
 }
 
 const progressPercent = ref(0)
@@ -197,10 +183,10 @@ const modalClosable = ref(false)
                 :loading="issuing_cert"
                 v-model:checked="enabled"
                 @change="onchange"
-                :disabled="no_server_name||server_name_more_than_one"
+                :disabled="no_server_name"
             />
             <a-alert
-                v-if="no_server_name||server_name_more_than_one"
+                v-if="no_server_name"
                 :message="$gettext('Warning')"
                 type="warning"
                 show-icon
@@ -209,9 +195,6 @@ const modalClosable = ref(false)
                     <span v-if="no_server_name" v-translate>
                         server_name parameter is required
                     </span>
-                    <span v-if="server_name_more_than_one" v-translate>
-                        server_name parameters more than one
-                    </span>
                 </template>
             </a-alert>
         </a-form-item>

+ 120 - 119
server/api/cert.go

@@ -1,140 +1,141 @@
 package api
 
 import (
-	"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"
+    "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"
+    "strings"
 )
 
 const (
-	Success = "success"
-	Info    = "info"
-	Error   = "error"
+    Success = "success"
+    Info    = "info"
+    Error   = "error"
 )
 
 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"`
 }
 
 func handleIssueCertLogChan(conn *websocket.Conn, logChan chan string) {
-	defer func() {
-		if err := recover(); err != nil {
-			log.Println("api.handleIssueCertLogChan recover", err)
-		}
-	}()
+    defer func() {
+        if err := recover(); err != nil {
+            log.Println("api.handleIssueCertLogChan recover", err)
+        }
+    }()
 
-	for logString := range logChan {
+    for logString := range logChan {
 
-		err := conn.WriteJSON(IssueCertResponse{
-			Status:  Info,
-			Message: logString,
-		})
+        err := conn.WriteJSON(IssueCertResponse{
+            Status:  Info,
+            Message: logString,
+        })
 
-		if err != nil {
-			log.Println("Error handleIssueCertLogChan", err)
-			return
-		}
+        if err != nil {
+            log.Println("Error handleIssueCertLogChan", err)
+            return
+        }
 
-	}
+    }
 }
 
 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" {
-		return
-	}
-
-	logChan := make(chan string, 1)
-	errChan := make(chan error, 1)
-
-	go cert.IssueCert(domain, logChan, errChan)
-
-	go handleIssueCertLogChan(ws, logChan)
-
-	// block, unless errChan closed
-	for err = range errChan {
-		log.Println("Error cert.IssueCert", err)
-
-		err = ws.WriteJSON(IssueCertResponse{
-			Status:  Error,
-			Message: err.Error(),
-		})
-
-		if err != nil {
-			log.Println(err)
-			return
-		}
-
-		return
-	}
-
-	close(logChan)
-
-	sslCertificatePath := nginx.GetNginxConfPath("ssl/" + domain + "/fullchain.cer")
-	sslCertificateKeyPath := nginx.GetNginxConfPath("ssl/" + domain + "/" + domain + ".key")
-
-	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(IssueCertResponse{
-		Status:            Success,
-		Message:           "Issued certificate successfully",
-		SSLCertificate:    sslCertificatePath,
-		SSLCertificateKey: sslCertificateKeyPath,
-	})
-
-	if err != nil {
-		log.Println(err)
-		return
-	}
+    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
+    var buffer struct {
+        ServerName []string `json:"server_name"`
+    }
+
+    err = ws.ReadJSON(&buffer)
+
+    if err != nil {
+        log.Println(err)
+        return
+    }
+
+    logChan := make(chan string, 1)
+    errChan := make(chan error, 1)
+
+    go cert.IssueCert(buffer.ServerName, logChan, errChan)
+
+    domain := strings.Join(buffer.ServerName, "_")
+
+    go handleIssueCertLogChan(ws, logChan)
+
+    // block, unless errChan closed
+    for err = range errChan {
+        log.Println("Error cert.IssueCert", err)
+
+        err = ws.WriteJSON(IssueCertResponse{
+            Status:  Error,
+            Message: err.Error(),
+        })
+
+        if err != nil {
+            log.Println(err)
+            return
+        }
+
+        return
+    }
+
+    close(logChan)
+
+    sslCertificatePath := nginx.GetNginxConfPath("ssl/" + domain + "/fullchain.cer")
+    sslCertificateKeyPath := nginx.GetNginxConfPath("ssl/" + domain + "/" + domain + ".key")
+
+    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(IssueCertResponse{
+        Status:            Success,
+        Message:           "Issued certificate successfully",
+        SSLCertificate:    sslCertificatePath,
+        SSLCertificateKey: sslCertificateKeyPath,
+    })
+
+    if err != nil {
+        log.Println(err)
+        return
+    }
 
 }

+ 1 - 1
server/pkg/cert/auto_cert.go

@@ -56,7 +56,7 @@ func AutoCert() {
 		logChan := make(chan string, 1)
 		errChan := make(chan error, 1)
 
-		go IssueCert(domain, logChan, errChan)
+		go IssueCert([]string{domain}, logChan, errChan)
 
 		go handleIssueCertLogChan(logChan)
 

+ 6 - 4
server/pkg/cert/cert.go

@@ -16,6 +16,7 @@ import (
 	"log"
 	"os"
 	"path/filepath"
+	"strings"
 )
 
 // MyUser You'll need a user or account type that implements acme.User
@@ -35,7 +36,7 @@ func (u *MyUser) GetPrivateKey() crypto.PrivateKey {
 	return u.key
 }
 
-func IssueCert(domain string, logChan chan string, errChan chan error) {
+func IssueCert(domain []string, logChan chan string, errChan chan error) {
 	defer func() {
 		if err := recover(); err != nil {
 			log.Println("Issue Cert recover", err)
@@ -94,7 +95,7 @@ func IssueCert(domain string, logChan chan string, errChan chan error) {
 	myUser.Registration = reg
 
 	request := certificate.ObtainRequest{
-		Domains: []string{domain},
+		Domains: domain,
 		Bundle:  true,
 	}
 
@@ -104,7 +105,8 @@ func IssueCert(domain string, logChan chan string, errChan chan error) {
 		errChan <- errors.Wrap(err, "issue cert fail to obtain")
 		return
 	}
-	saveDir := nginx.GetNginxConfPath("ssl/" + domain)
+	name := strings.Join(domain, "_")
+	saveDir := nginx.GetNginxConfPath("ssl/" + name)
 	if _, err = os.Stat(saveDir); os.IsNotExist(err) {
 		err = os.MkdirAll(saveDir, 0755)
 		if err != nil {
@@ -125,7 +127,7 @@ func IssueCert(domain string, logChan chan string, errChan chan error) {
 	}
 
 	logChan <- "Writing certificate private key to disk"
-	err = os.WriteFile(filepath.Join(saveDir, domain+".key"),
+	err = os.WriteFile(filepath.Join(saveDir, name+".key"),
 		certificates.PrivateKey, 0644)
 
 	if err != nil {

+ 1 - 1
server/router/routers.go

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