forked from louketo/louketo-proxy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rotation.go
123 lines (108 loc) · 3.56 KB
/
rotation.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
/*
Copyright 2015 All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"crypto/tls"
"fmt"
"path"
"sync"
"github.com/fsnotify/fsnotify"
"go.uber.org/zap"
)
type certificationRotation struct {
sync.RWMutex
// certificate holds the current issuing certificate
certificate tls.Certificate
// certificateFile is the path the certificate
certificateFile string
// the privateKeyFile is the path of the private key
privateKeyFile string
// the logger for this service
log *zap.Logger
}
// newCertificateRotator creates a new certificate
func newCertificateRotator(cert, key string, log *zap.Logger) (*certificationRotation, error) {
// step: attempt to load the certificate
certificate, err := tls.LoadX509KeyPair(cert, key)
if err != nil {
return nil, err
}
// @step: are we watching the files for changes?
return &certificationRotation{
certificate: certificate,
certificateFile: cert,
log: log,
privateKeyFile: key,
}, nil
}
// watch is responsible for adding a file notification and watch on the files for changes
func (c *certificationRotation) watch() error {
c.log.Info("adding a file watch on the certificates, certificate",
zap.String("certificate", c.certificateFile),
zap.String("private_key", c.privateKeyFile))
watcher, err := fsnotify.NewWatcher()
if err != nil {
return err
}
// add the files to the watch list
for _, x := range []string{c.certificateFile, c.privateKeyFile} {
if err := watcher.Add(path.Dir(x)); err != nil {
return fmt.Errorf("unable to add watch on directory: %s, error: %s", path.Dir(x), err)
}
}
// step: watching for events
filewatchPaths := []string{c.certificateFile, c.privateKeyFile}
go func() {
c.log.Info("starting to watch changes to the tls certificate files")
for {
select {
case event := <-watcher.Events:
if event.Op&fsnotify.Write == fsnotify.Write {
// step: does the change effect our files?
if !containedIn(event.Name, filewatchPaths) {
continue
}
// step: reload the certificate
certificate, err := tls.LoadX509KeyPair(c.certificateFile, c.privateKeyFile)
if err != nil {
c.log.Error("unable to load the updated certificate",
zap.String("filename", event.Name),
zap.Error(err))
}
// @metric inform of the rotation
certificateRotationMetric.Inc()
// step: load the new certificate
c.storeCertificate(certificate)
// step: print a debug message for us
c.log.Info("replacing the server certifacte with updated version")
}
case err := <-watcher.Errors:
c.log.Error("received an error from the file watcher", zap.Error(err))
}
}
}()
return nil
}
// storeCertificate provides entrypoint to update the certificate
func (c *certificationRotation) storeCertificate(certifacte tls.Certificate) error {
c.Lock()
defer c.Unlock()
c.certificate = certifacte
return nil
}
// GetCertificate is responsible for retrieving
func (c *certificationRotation) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
c.RLock()
defer c.RUnlock()
return &c.certificate, nil
}