add custom acme feature in backend

This commit is contained in:
dalun 2023-08-20 20:56:17 +00:00
parent a3d55a3274
commit 352995e852
2 changed files with 76 additions and 12 deletions

View File

@ -8,7 +8,6 @@ import (
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"log"
@ -28,6 +27,11 @@ import (
"imuslab.com/zoraxy/mod/utils"
)
type CertificateInfoJSON struct {
AcmeName string `json:"acme_name"`
AcmeUrl string `json:"acme_url"`
}
// ACMEUser represents a user in the ACME system.
type ACMEUser struct {
Email string
@ -65,7 +69,7 @@ func NewACME(acmeServer string, port string) *ACMEHandler {
}
// ObtainCert obtains a certificate for the specified domains.
func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, ca string) (bool, error) {
func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, caName string, caUrl string) (bool, error) {
log.Println("[ACME] Obtaining certificate...")
// generate private key
@ -84,17 +88,23 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
// create config
config := lego.NewConfig(&adminUser)
// setup who is the issuer and the key type
config.CADirURL = a.DefaultAcmeServer
// setup the custom ACME url endpoint.
if caUrl != "" {
config.CADirURL = caUrl
}
//Overwrite the CADir URL if set
if ca != "" {
caLinkOverwrite, err := loadCAApiServerFromName(ca)
// if not custom ACME url, load it from ca.json
if caName == "custom" {
log.Println("[INFO] Using Custom ACME" + caUrl + " for CA Directory URL")
} else {
caLinkOverwrite, err := loadCAApiServerFromName(caName)
if err == nil {
config.CADirURL = caLinkOverwrite
log.Println("[INFO] Using " + caLinkOverwrite + " for CA Directory URL")
} else {
return false, errors.New("CA " + ca + " is not supported. Please contribute to the source code and add this CA's directory link.")
// (caName == "" || caUrl == "") will use default acme
config.CADirURL = a.DefaultAcmeServer
log.Println("[INFO] Using Default ACME" + a.DefaultAcmeServer + " for CA Directory URL")
}
}
@ -145,6 +155,24 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
return false, err
}
// Save certificate's ACME info for renew usage
certInfo := &CertificateInfoJSON{
AcmeName: caName,
AcmeUrl: caUrl,
}
certInfoBytes, err := json.Marshal(certInfo)
if err != nil {
log.Println(err)
return false, err
}
err = os.WriteFile("./conf/certs/"+certificateName+".json", certInfoBytes, 0777)
if err != nil {
log.Println(err)
return false, err
}
return true, nil
}
@ -250,14 +278,24 @@ func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Requ
return
}
var caUrl string
ca, err := utils.PostPara(r, "ca")
if err != nil {
log.Println("CA not set. Using default (Let's Encrypt)")
ca = "Let's Encrypt"
log.Println("CA not set. Using default")
ca, caUrl = "", ""
}
if ca == "custom" {
caUrl, err = utils.PostPara(r, "ca_url")
if err != nil {
log.Println("Custom CA set but no URL provide, Using default")
ca, caUrl = "", ""
}
}
domains := strings.Split(domainPara, ",")
result, err := a.ObtainCert(domains, filename, email, ca)
result, err := a.ObtainCert(domains, filename, email, ca, caUrl)
if err != nil {
utils.SendErrorResponse(w, jsonEscape(err.Error()))
return
@ -285,4 +323,20 @@ func IsPortInUse(port int) bool {
}
defer listener.Close()
return false // Port is not in use
}
func loadCertInfoJSON(filename string) (*CertificateInfoJSON, error) {
certInfoBytes, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
certInfo := &CertificateInfoJSON{}
if err = json.Unmarshal(certInfoBytes, certInfo); err != nil {
return nil, err
}
return certInfo, nil
}

View File

@ -3,6 +3,7 @@ package acme
import (
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"net/mail"
@ -355,7 +356,16 @@ func (a *AutoRenewer) renewExpiredDomains(certs []*ExpiredCerts) ([]string, erro
log.Println("Renewing " + expiredCert.Filepath + " (Might take a few minutes)")
fileName := filepath.Base(expiredCert.Filepath)
certName := fileName[:len(fileName)-len(filepath.Ext(fileName))]
_, err := a.AcmeHandler.ObtainCert(expiredCert.Domains, certName, a.RenewerConfig.Email, expiredCert.CA)
// Load certificate info for ACME detail
certInfoFilename := fmt.Sprintf("%s/%s.json", filepath.Dir(expiredCert.Filepath), certName)
certInfo, err := loadCertInfoJSON(certInfoFilename)
if err != nil {
log.Printf("Renew %s certificate error, can't get the ACME detail for cert: %v, using default ACME", certName, err)
certInfo = &CertificateInfoJSON{}
}
_, err = a.AcmeHandler.ObtainCert(expiredCert.Domains, certName, a.RenewerConfig.Email, certInfo.AcmeName, certInfo.AcmeUrl)
if err != nil {
log.Println("Renew " + fileName + "(" + strings.Join(expiredCert.Domains, ",") + ") failed: " + err.Error())
} else {