mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 02:15:48 +02:00
feat: using renewal api to renew certificate #319
This commit is contained in:
parent
e3876cffaf
commit
e16b077d20
11 changed files with 173 additions and 71 deletions
|
@ -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
|
||||
|
|
|
@ -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
63
internal/cert/obtain.go
Normal 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
|
||||
}
|
||||
}
|
|
@ -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
36
internal/cert/renew.go
Normal 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")
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue