Files
simplecert/renew.go
2020-11-25 23:56:29 +01:00

146 lines
4.2 KiB
Go

//
// simplecert
//
// Created by Philipp Mieden
// Contact: dreadl0ck@protonmail.ch
// Copyright © 2018 bestbytes. All rights reserved.
//
package simplecert
import (
"fmt"
"log"
"os"
"path/filepath"
"syscall"
"time"
"github.com/go-acme/lego/v4/certificate"
)
func renew(cert *certificate.Resource) error {
// Input certificate is PEM encoded. Decode it here as we may need the decoded
// cert later on in the renewal process. The input may be a bundle or a single certificate.
certificates, err := parsePEMBundle(cert.Certificate)
if err != nil {
return fmt.Errorf("simplecert: failed to parsePEMBundle: %s", err)
}
// check if first cert is CA
x509Cert := certificates[0]
if x509Cert.IsCA {
return fmt.Errorf("[%s] Certificate bundle starts with a CA certificate", cert.Domain)
}
// Calculate TimeLeft
timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC())
log.Printf("[INFO][%s] acme: %d hours remaining, renewBefore: %d\n", cert.Domain, int(timeLeft.Hours()), int(c.RenewBefore))
// Check against renewBefore
if int(timeLeft.Hours()) <= int(c.RenewBefore) {
log.Println("[INFO] simplecert: renewing cert...")
// allow graceful shutdown of running services if required
if c.WillRenewCertificate != nil {
c.WillRenewCertificate()
}
u, err := getUser()
if err != nil {
return fmt.Errorf("simplecert: failed to get acme user: %s", err)
}
// get ACME Client
client, err := createClient(u, c.DNSServers)
if err != nil {
return fmt.Errorf("simplecert: failed to create lego.Client: %s", err)
}
// start renewal
// bundle CA with certificate to avoid "transport: x509: certificate signed by unknown authority" error
cert, err := client.Certificate.Renew(*cert, true, false, "")
if err != nil {
return fmt.Errorf("simplecert: failed to renew cert: %s", err)
}
// if we made it here we got a new cert
// backup old cert and key
// create a new directory for those in cacheDir, named backup-{currentDate}-{currentTime}
backupDate = time.Now().Format("2006-January-02-1504")
err = os.MkdirAll(filepath.Join(c.CacheDir, "backup-"+backupDate), c.CacheDirPerm)
if err != nil {
return fmt.Errorf("simplecert: failed to create backup dir: %s", err)
}
// backup private key
err = os.Rename(filepath.Join(c.CacheDir, keyFileName), filepath.Join(c.CacheDir, "backup-"+backupDate, keyFileName))
if err != nil {
return fmt.Errorf("simplecert: failed to move key into backup dir: %s", err)
}
// backup certificate
err = os.Rename(filepath.Join(c.CacheDir, certFileName), filepath.Join(c.CacheDir, "backup-"+backupDate, keyFileName))
if err != nil {
return fmt.Errorf("simplecert: failed to move cert into backup dir: %s", err)
}
// Save new cert to disk
err = saveCertToDisk(cert, c.CacheDir)
if err != nil {
return fmt.Errorf("simplecert: failed to write new cert to disk: %s", err)
}
log.Println("[INFO] simplecert: wrote new cert to disk!")
// allow service restart if required
if c.DidRenewCertificate != nil {
c.DidRenewCertificate()
} else {
// if the user has not specified a DidRenewCertificate handler to restart the service
// we will force the server to reload the cert by sending a HUP signal
log.Println("[INFO] triggering reload via SIGHUP")
// trigger reload by sending our process a SIGHUP
p, err := os.FindProcess(os.Getpid())
if err != nil {
return fmt.Errorf("simplecert: failed to get process by PID: %s", err)
}
// send signal
err = p.Signal(syscall.SIGHUP)
if err != nil {
return fmt.Errorf("simplecert: failed to send SIGHUP to our process: %s", err)
}
}
}
return nil
}
// take care of checking the cert in the configured interval
// and renew if timeLeft is less than or equal to renewBefore
// when initially started, the certificate is checked against the thresholds and renewed if neccessary
func renewalRoutine(cr *certificate.Resource) {
for {
// sleep for duration of checkInterval
time.Sleep(c.CheckInterval)
// renew the certificate
err := renew(cr)
if err != nil { // something went wrong.
// call handler if set
if c.FailedToRenewCertificate != nil {
c.FailedToRenewCertificate(err)
} else {
// otherwise fatal
log.Fatal("[FATAL] failed to renew cert: ", err.Error())
}
}
}
}