-
Notifications
You must be signed in to change notification settings - Fork 34
/
reloader.go
133 lines (114 loc) · 3.35 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
//
// 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.Fatal("[FATAL] simplecert: failed to close logfile handle: ", err)
}
log.Println("[INFO] simplecert: closed logfile handle")
// run custom cleanup func if available
if cleanup != nil {
cleanup()
}
// keep running.
// it is up to the caller of simplecert.NewCertReloader()
// to decide what to do now
}
}
}()
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)
}
}
}