-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathreloader.go
135 lines (116 loc) · 3.5 KB
/
reloader.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
//
// simplecert
//
// Created by Philipp Mieden
// Contact: dreadl0ck@protonmail.ch
// Copyright © 2018 bestbytes. All rights reserved.
//
package simplecert
import (
"crypto/tls"
"log"
"os"
"os/signal"
"path/filepath"
"sync"
"syscall"
)
/*
* CertReloader
*/
// CertReloader manages a hot reload of a new cert
type CertReloader struct {
sync.RWMutex
cert *tls.Certificate
certPath string
keyPath string
}
// NewCertReloader returns a new CertReloader instance
// the optional cleanup func will be called when a syscall.SIGINT, syscall.SIGABRT is received
func NewCertReloader(certPath, keyPath string, logFile *os.File, cleanup func()) (*CertReloader, error) {
// init reloader
reloader := &CertReloader{
certPath: certPath,
keyPath: keyPath,
}
// Load keypair
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return nil, err
}
reloader.cert = &cert
// kickoff routine for handling singals
go func() {
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGHUP, syscall.SIGINT, syscall.SIGABRT)
for sig := range sigChan {
if sig == syscall.SIGHUP {
log.Printf("Received SIGHUP, reloading TLS certificate and key from %q and %q", certPath, keyPath)
reloader.reload()
} else {
// cleanup
err := logFile.Close()
if err != nil {
log.Println("[FATAL] simplecert: failed to close logfile handle: ", err)
}
log.Println("[INFO] simplecert: closed logfile handle")
// run custom cleanup func if available
if cleanup != nil {
// execute the cleanup function supplied by the user
// if you want to keep the program running at this point, supply a cleanup function that does nothing.
cleanup()
} else {
// if no cleanup function has been specified: exit cleanly
os.Exit(0)
}
}
}
}()
return reloader, nil
}
func (reloader *CertReloader) maybeReload() error {
newCert, err := tls.LoadX509KeyPair(reloader.certPath, reloader.keyPath)
if err != nil {
return err
}
reloader.Lock()
defer reloader.Unlock()
reloader.cert = &newCert
return nil
}
// GetCertificateFunc is needed for hot reload
func (reloader *CertReloader) GetCertificateFunc() func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
return func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
reloader.RLock()
defer reloader.RUnlock()
return reloader.cert, nil
}
}
// ReloadNow will force reloading the cert from disk
func (reloader *CertReloader) ReloadNow() {
reloader.reload()
}
func (reloader *CertReloader) reload() {
if err := reloader.maybeReload(); err != nil {
// there was an error reloading the certificate
// rollback files from backup dir
log.Printf("[INFO] simplecert: Keeping old TLS certificate because the new one could not be loaded: %v", err)
// restore private key
backupPrivKey := filepath.Join(c.CacheDir, "backup-"+backupDate, keyFileName)
err = os.Rename(backupPrivKey, filepath.Join(c.CacheDir, keyFileName))
if err != nil {
log.Fatal("[FATAL] simplecert: failed to move key into backup dir: ", err)
}
// restore certificate
backupCert := filepath.Join(c.CacheDir, "backup-"+backupDate, certFileName)
err = os.Rename(backupCert, filepath.Join(c.CacheDir, certFileName))
if err != nil {
log.Fatal("[FATAL] simplecert: failed to move cert into backup dir: ", err)
}
// remove backup directory
err = os.Remove(filepath.Join(c.CacheDir, "backup-"+backupDate))
if err != nil {
log.Fatal("[FATAL] simplecert: failed to remove backup dir: ", err)
}
}
}