-
Notifications
You must be signed in to change notification settings - Fork 34
/
reloader.go
124 lines (107 loc) · 3.24 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
//
// 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)
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)
}
}
} 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
}
}