feat: using renewal api to renew certificate #319

This commit is contained in:
Jacky 2024-04-30 19:48:48 +08:00
parent e3876cffaf
commit e16b077d20
No known key found for this signature in database
GPG key ID: 215C21B10DF38B4D
11 changed files with 173 additions and 71 deletions

View file

@ -11,7 +11,7 @@ import (
"time"
)
func AutoObtain() {
func AutoCert() {
defer func() {
if err := recover(); err != nil {
buf := make([]byte, 1024)
@ -22,12 +22,12 @@ func AutoObtain() {
logger.Info("AutoCert Worker Started")
autoCertList := model.GetAutoCertList()
for _, certModel := range autoCertList {
renew(certModel)
autoCert(certModel)
}
logger.Info("AutoCert Worker End")
}
func renew(certModel *model.Cert) {
func autoCert(certModel *model.Cert) {
confName := certModel.Filename
log := &Logger{}
@ -75,6 +75,14 @@ func renew(certModel *model.Cert) {
ChallengeMethod: certModel.ChallengeMethod,
DNSCredentialID: certModel.DnsCredentialID,
KeyType: certModel.GetKeyType(),
Resource: &model.CertificateResource{
Resource: certModel.Resource.Resource,
PrivateKey: certModel.Resource.PrivateKey,
Certificate: certModel.Resource.Certificate,
IssuerCertificate: certModel.Resource.IssuerCertificate,
CSR: certModel.Resource.CSR,
},
NotBefore: cert.NotBefore,
}
// errChan will be closed inside IssueCert

View file

@ -7,7 +7,6 @@ import (
"github.com/0xJacky/Nginx-UI/internal/nginx"
"github.com/0xJacky/Nginx-UI/query"
"github.com/0xJacky/Nginx-UI/settings"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/challenge/http01"
"github.com/go-acme/lego/v4/lego"
legolog "github.com/go-acme/lego/v4/log"
@ -16,8 +15,6 @@ import (
"log"
"net/http"
"os"
"path/filepath"
"strings"
"time"
)
@ -44,15 +41,13 @@ func IssueCert(payload *ConfigPayload, logChan chan string, errChan chan error)
// Hijack the (logger) of lego
legolog.Logger = l
domain := payload.ServerName
l.Println("[INFO] [Nginx UI] Preparing lego configurations")
user, err := payload.GetACMEUser()
if err != nil {
errChan <- errors.Wrap(err, "issue cert get acme user error")
return
}
l.Printf("[INFO] [Nginx UI] ACME User: %s, CA Dir: %s\n", user.Email, user.CADir)
l.Printf("[INFO] [Nginx UI] ACME User: %s, Email: %s, CA Dir: %s\n", user.Name, user.Email, user.CADir)
// Start a goroutine to fetch and process logs from channel
go func() {
@ -134,45 +129,11 @@ func IssueCert(payload *ConfigPayload, logChan chan string, errChan chan error)
return
}
request := certificate.ObtainRequest{
Domains: domain,
Bundle: true,
}
l.Println("[INFO] [Nginx UI] Obtaining certificate")
certificates, err := client.Certificate.Obtain(request)
if err != nil {
errChan <- errors.Wrap(err, "obtain certificate error")
return
}
name := strings.Join(domain, "_")
saveDir := nginx.GetConfPath("ssl/" + name + "_" + string(payload.KeyType))
if _, err = os.Stat(saveDir); os.IsNotExist(err) {
err = os.MkdirAll(saveDir, 0755)
if err != nil {
errChan <- errors.Wrap(err, "mkdir error")
return
}
}
// Each certificate comes back with the cert bytes, the bytes of the client's
// private key, and a certificate URL. SAVE THESE TO DISK.
l.Println("[INFO] [Nginx UI] Writing certificate to disk")
err = os.WriteFile(filepath.Join(saveDir, "fullchain.cer"),
certificates.Certificate, 0644)
if err != nil {
errChan <- errors.Wrap(err, "write fullchain.cer error")
return
}
l.Println("[INFO] [Nginx UI] Writing certificate private key to disk")
err = os.WriteFile(filepath.Join(saveDir, "private.key"),
certificates.PrivateKey, 0644)
if err != nil {
errChan <- errors.Wrap(err, "write private.key error")
return
if time.Now().Sub(payload.NotBefore).Hours()/24 <= 21 &&
payload.Resource != nil && payload.Resource.Certificate != nil {
renew(payload, client, l, errChan)
} else {
obtain(payload, client, l, errChan)
}
l.Println("[INFO] [Nginx UI] Reloading nginx")

63
internal/cert/obtain.go Normal file
View file

@ -0,0 +1,63 @@
package cert
import (
"github.com/0xJacky/Nginx-UI/internal/nginx"
"github.com/0xJacky/Nginx-UI/model"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/lego"
"github.com/pkg/errors"
"log"
"os"
"path/filepath"
"strings"
)
func obtain(payload *ConfigPayload, client *lego.Client, l *log.Logger, errChan chan error) {
request := certificate.ObtainRequest{
Domains: payload.ServerName,
Bundle: true,
}
l.Println("[INFO] [Nginx UI] Obtaining certificate")
certificates, err := client.Certificate.Obtain(request)
if err != nil {
errChan <- errors.Wrap(err, "obtain certificate error")
return
}
payload.Resource = &model.CertificateResource{
Resource: certificates,
PrivateKey: certificates.PrivateKey,
Certificate: certificates.Certificate,
IssuerCertificate: certificates.IssuerCertificate,
CSR: certificates.CSR,
}
name := strings.Join(payload.ServerName, "_")
saveDir := nginx.GetConfPath("ssl/" + name + "_" + string(payload.KeyType))
if _, err = os.Stat(saveDir); os.IsNotExist(err) {
err = os.MkdirAll(saveDir, 0755)
if err != nil {
errChan <- errors.Wrap(err, "mkdir error")
return
}
}
// Each certificate comes back with the cert bytes, the bytes of the client's
// private key, and a certificate URL. SAVE THESE TO DISK.
l.Println("[INFO] [Nginx UI] Writing certificate to disk")
err = os.WriteFile(filepath.Join(saveDir, "fullchain.cer"),
certificates.Certificate, 0644)
if err != nil {
errChan <- errors.Wrap(err, "write fullchain.cer error")
return
}
l.Println("[INFO] [Nginx UI] Writing certificate private key to disk")
err = os.WriteFile(filepath.Join(saveDir, "private.key"),
certificates.PrivateKey, 0644)
if err != nil {
errChan <- errors.Wrap(err, "write private.key error")
return
}
}

View file

@ -6,14 +6,17 @@ import (
"github.com/0xJacky/Nginx-UI/model"
"github.com/0xJacky/Nginx-UI/query"
"github.com/go-acme/lego/v4/certcrypto"
"time"
)
type ConfigPayload struct {
ServerName []string `json:"server_name"`
ChallengeMethod string `json:"challenge_method"`
DNSCredentialID int `json:"dns_credential_id"`
ACMEUserID int `json:"acme_user_id"`
KeyType certcrypto.KeyType `json:"key_type"`
ServerName []string `json:"server_name"`
ChallengeMethod string `json:"challenge_method"`
DNSCredentialID int `json:"dns_credential_id"`
ACMEUserID int `json:"acme_user_id"`
KeyType certcrypto.KeyType `json:"key_type"`
Resource *model.CertificateResource `json:"resource,omitempty"`
NotBefore time.Time
}
func (c *ConfigPayload) GetACMEUser() (user *model.AcmeUser, err error) {

36
internal/cert/renew.go Normal file
View file

@ -0,0 +1,36 @@
package cert
import (
"github.com/0xJacky/Nginx-UI/model"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/lego"
"github.com/pkg/errors"
"log"
)
func renew(payload *ConfigPayload, client *lego.Client, l *log.Logger, errChan chan error) {
if payload.Resource == nil {
errChan <- errors.New("resource is nil")
return
}
options := &certificate.RenewOptions{
Bundle: true,
}
cert, err := client.Certificate.RenewWithOptions(payload.Resource.GetResource(), options)
if err != nil {
errChan <- errors.Wrap(err, "renew cert error")
return
}
payload.Resource = &model.CertificateResource{
Resource: cert,
PrivateKey: cert.PrivateKey,
Certificate: cert.Certificate,
IssuerCertificate: cert.IssuerCertificate,
CSR: cert.CSR,
}
l.Println("[INFO] [Nginx UI] Certificate renewed successfully")
}

View file

@ -18,7 +18,7 @@ func init() {
var logrotateJob *gocron.Job
func InitCronJobs() {
job, err := s.Every(30).Minute().SingletonMode().Do(cert.AutoObtain)
job, err := s.Every(30).Minute().SingletonMode().Do(cert.AutoCert)
if err != nil {
logger.Fatalf("AutoCert Job: %v, Err: %v\n", job, err)

View file

@ -90,7 +90,7 @@ func InitJsExtensionType() {
func InitCronJobs() {
s := gocron.NewScheduler(time.UTC)
job, err := s.Every(30).Minute().SingletonMode().Do(cert.AutoObtain)
job, err := s.Every(30).Minute().SingletonMode().Do(cert.AutoCert)
if err != nil {
logger.Fatalf("AutoCert Job: %v, Err: %v\n", job, err)