...
1 package tls
2
3 import (
4 "context"
5 "crypto/tls"
6 "fmt"
7 "path/filepath"
8 "sync/atomic"
9
10 "github.com/fsnotify/fsnotify"
11 log "github.com/sirupsen/logrus"
12 )
13
14 const dataDirectoryLnName = "..data"
15
16
17 type FsCredsWatcher struct {
18 certRootPath string
19 certFilePath string
20 keyFilePath string
21 EventChan chan<- struct{}
22 ErrorChan chan<- error
23 }
24
25
26 func NewFsCredsWatcher(certRootPath string, updateEvent chan<- struct{}, errEvent chan<- error) *FsCredsWatcher {
27 return &FsCredsWatcher{certRootPath, "", "", updateEvent, errEvent}
28 }
29
30
31 func (fscw *FsCredsWatcher) WithFilePaths(certFilePath, keyFilePath string) *FsCredsWatcher {
32 fscw.certFilePath = certFilePath
33 fscw.keyFilePath = keyFilePath
34 return fscw
35 }
36
37
38 func (fscw *FsCredsWatcher) StartWatching(ctx context.Context) error {
39 watcher, err := fsnotify.NewWatcher()
40 if err != nil {
41 return err
42 }
43 defer watcher.Close()
44
45
46 if err := watcher.Add(fscw.certRootPath); err != nil {
47 return err
48 }
49
50 LOOP:
51 for {
52 select {
53 case event := <-watcher.Events:
54 log.Debugf("Received event: %v", event)
55
56
57 if event.Op&fsnotify.Create == fsnotify.Create &&
58 event.Name == filepath.Join(fscw.certRootPath, dataDirectoryLnName) {
59 fscw.EventChan <- struct{}{}
60 }
61 case err := <-watcher.Errors:
62 fscw.ErrorChan <- err
63 log.Warnf("Error while watching %s: %s", fscw.certRootPath, err)
64 break LOOP
65 case <-ctx.Done():
66 if err := ctx.Err(); err != nil {
67 fscw.ErrorChan <- err
68 }
69 break LOOP
70 }
71 }
72
73 return nil
74 }
75
76
77 func (fscw *FsCredsWatcher) UpdateCert(certVal *atomic.Value) error {
78 creds, err := ReadPEMCreds(fscw.keyFilePath, fscw.certFilePath)
79 if err != nil {
80 return fmt.Errorf("failed to read cert from disk: %w", err)
81 }
82
83 certPEM := creds.EncodePEM()
84 keyPEM := creds.EncodePrivateKeyPEM()
85 cert, err := tls.X509KeyPair([]byte(certPEM), []byte(keyPEM))
86 if err != nil {
87 return err
88 }
89 certVal.Store(&cert)
90 return nil
91 }
92
93
94 func (fscw *FsCredsWatcher) ProcessEvents(
95 log *log.Entry,
96 certVal *atomic.Value,
97 updateEvent <-chan struct{},
98 errEvent <-chan error,
99 ) {
100 for {
101 select {
102 case <-updateEvent:
103 if err := fscw.UpdateCert(certVal); err != nil {
104 log.Warnf("Skipping update as cert could not be read from disk: %s", err)
105 } else {
106 log.Infof("Updated certificate")
107 }
108 case err := <-errEvent:
109 log.Warnf("Received error from fs watcher: %s", err)
110 }
111 }
112 }
113
View as plain text