-
Notifications
You must be signed in to change notification settings - Fork 34
/
renew.go
145 lines (119 loc) · 4.23 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
137
138
139
140
141
142
143
144
145
//
// 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...")
// 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())
}
}
}
}