-
Notifications
You must be signed in to change notification settings - Fork 34
/
renew.go
136 lines (111 loc) · 3.89 KB
/
renew.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//
// 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/v3/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...")
u, err := getUser()
if err != nil {
return fmt.Errorf("simplecert: failed to get acme user: %s", err)
}
// get ACME Client
client, err := createClient(u)
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}
backupDate = time.Now().Format("2006-January-02")
err = os.Mkdir(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! triggering reload via SIGHUP")
// trigger reload by sending our process a SIGHUP
err = syscall.Kill(os.Getpid(), syscall.SIGHUP)
if err != nil {
return fmt.Errorf("simplecert: failed to trigger reload of renewed certificate: %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)
// allow graceful shutdown of running services if required
if c.WillRenewCertificate != nil {
c.WillRenewCertificate()
}
// 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())
}
} else { // all good.
// allow service restart if required
if c.DidRenewCertificate != nil {
c.DidRenewCertificate()
}
}
}
}