@@ -2,7 +2,6 @@ package rest
22
33import (
44 "context"
5- "crypto/tls"
65 "fmt"
76 "net"
87 "net/http"
@@ -12,7 +11,7 @@ import (
1211 "sync"
1312 "syscall"
1413
15- "github.com/fsnotify/fsnotify "
14+ "github.com/cloudhut/common/tls "
1615 "github.com/go-chi/chi/v5"
1716 "go.uber.org/zap"
1817)
@@ -42,7 +41,7 @@ func NewServer(cfg *Config, logger *zap.Logger, router *chi.Mux) (*Server, error
4241 }
4342
4443 if cfg .TLS .Enabled {
45- tlsCfg , err := buildServerTLSConfig (logger , cfg .TLS )
44+ tlsCfg , err := tls . BuildWatchedTLSConfig (logger , cfg .TLS . CertFilepath , cfg . TLS . KeyFilepath , nil )
4645 if err != nil {
4746 return nil , fmt .Errorf ("failed to create TLS config: %w" , err )
4847 }
@@ -109,93 +108,3 @@ func (s *Server) Start() error {
109108
110109 return nil
111110}
112-
113- func buildServerTLSConfig (logger * zap.Logger , cfg TLSConfig ) (* tls.Config , error ) {
114- cert , err := tls .LoadX509KeyPair (cfg .CertFilepath , cfg .KeyFilepath )
115- if err != nil {
116- return nil , fmt .Errorf ("failed loading TLS cert: %w" , err )
117- }
118-
119- signalCh := make (chan os.Signal , 1 )
120- signal .Notify (signalCh , syscall .SIGINT , syscall .SIGTERM )
121-
122- watcher , err := fsnotify .NewWatcher ()
123- if err != nil {
124- return nil , fmt .Errorf ("failed to setup file watcher for hot reloading tls certificates: %w" , err )
125- }
126-
127- var lock sync.RWMutex
128- go func () {
129- defer watcher .Close ()
130- for {
131- select {
132- case event , ok := <- watcher .Events :
133- if ! ok {
134- return
135- }
136-
137- // Ignore all events that are neither remove nor write
138- isRelevantEvent := event .Has (fsnotify .Remove ) || event .Has (fsnotify .Write )
139- if ! isRelevantEvent {
140- continue
141- }
142-
143- if event .Has (fsnotify .Remove ) {
144- // Kubernetes uses symbolic links to create the illusion of atomic writes.
145- // Thus, we have to watch for the remove event and reconfigure our watcher.
146- // See: https://ahmet.im/blog/kubernetes-inotify/
147- _ = watcher .Remove (event .Name )
148- err = watcher .Add (cfg .CertFilepath )
149- if err != nil {
150- logger .Error ("failed to re-add file watcher" ,
151- zap .String ("file_path" , cfg .CertFilepath ),
152- zap .Error (err ))
153- }
154-
155- err = watcher .Add (cfg .KeyFilepath )
156- if err != nil {
157- logger .Warn ("failed to re-add file watcher" ,
158- zap .String ("file_path" , cfg .KeyFilepath ),
159- zap .Error (err ))
160- }
161- }
162-
163- logger .Info ("hot reloading the TLS certificate" )
164-
165- newCert , err := tls .LoadX509KeyPair (cfg .CertFilepath , cfg .KeyFilepath )
166- if err != nil {
167- logger .Error ("failed to load certificates" , zap .Error (err ))
168- continue
169- }
170- lock .Lock ()
171- cert = newCert
172- lock .Unlock ()
173-
174- logger .Info ("successfully hot reloaded the TLS certificate" )
175- case err , ok := <- watcher .Errors :
176- if ! ok {
177- return
178- }
179- logger .Error ("tls certificate watcher error" , zap .Error (err ))
180- case <- signalCh :
181- return
182- }
183- }
184- }()
185- if err := watcher .Add (cfg .CertFilepath ); err != nil {
186- return nil , fmt .Errorf ("failed to setup watcher for cert file: %w" , err )
187- }
188- if err = watcher .Add (cfg .KeyFilepath ); err != nil {
189- return nil , fmt .Errorf ("failed to setup watcher for key file: %w" , err )
190- }
191-
192- return & tls.Config {
193- MinVersion : tls .VersionTLS12 ,
194- GetCertificate : func (info * tls.ClientHelloInfo ) (* tls.Certificate , error ) {
195- lock .RLock ()
196- defer lock .RUnlock ()
197-
198- return & cert , nil
199- },
200- }, nil
201- }
0 commit comments