@@ -39,14 +39,18 @@ use tokio::{
39
39
} ;
40
40
use tower_http:: trace:: { self , TraceLayer } ;
41
41
42
+ // This is required by certificate hot reload when using inotify, which is available only on linux
43
+ #[ cfg( target_os = "linux" ) ]
44
+ use tokio_stream:: StreamExt ;
45
+
42
46
use crate :: api:: handlers:: {
43
47
audit_handler, pprof_get_cpu, pprof_get_heap, readiness_handler, validate_handler,
44
48
validate_raw_handler,
45
49
} ;
46
50
use crate :: api:: state:: ApiServerState ;
47
51
use crate :: evaluation:: precompiled_policy:: { PrecompiledPolicies , PrecompiledPolicy } ;
48
52
use crate :: policy_downloader:: { Downloader , FetchedPolicies } ;
49
- use config:: Config ;
53
+ use config:: { Config , TlsConfig } ;
50
54
51
55
use tikv_jemallocator:: Jemalloc ;
52
56
@@ -193,9 +197,7 @@ impl PolicyServer {
193
197
} ) ;
194
198
195
199
let tls_config = if let Some ( tls_config) = config. tls_config {
196
- let rustls_config =
197
- RustlsConfig :: from_pem_file ( tls_config. cert_file , tls_config. key_file ) . await ?;
198
- Some ( rustls_config)
200
+ Some ( create_tls_config_and_watch_certificate_changes ( tls_config) . await ?)
199
201
} else {
200
202
None
201
203
} ;
@@ -269,6 +271,88 @@ impl PolicyServer {
269
271
}
270
272
}
271
273
274
+ /// There's no watching of the certificate files on non-linux platforms
275
+ /// since we rely on inotify to watch for changes
276
+ #[ cfg( not( target_os = "linux" ) ) ]
277
+ async fn create_tls_config_and_watch_certificate_changes (
278
+ tls_config : TlsConfig ,
279
+ ) -> Result < RustlsConfig > {
280
+ let cfg = RustlsConfig :: from_pem_file ( tls_config. cert_file , tls_config. key_file ) . await ?;
281
+ Ok ( cfg)
282
+ }
283
+
284
+ /// Return the RustlsConfig and watch for changes in the certificate files
285
+ /// using inotify.
286
+ /// When a both the certificate and its key are changed, the RustlsConfig is reloaded,
287
+ /// causing the https server to use the new certificate.
288
+ ///
289
+ /// Relying on inotify is only available on linux
290
+ #[ cfg( target_os = "linux" ) ]
291
+ async fn create_tls_config_and_watch_certificate_changes (
292
+ tls_config : TlsConfig ,
293
+ ) -> Result < RustlsConfig > {
294
+ let cert_file = tls_config. cert_file . clone ( ) ;
295
+ let key_file = tls_config. key_file . clone ( ) ;
296
+
297
+ let rust_config =
298
+ RustlsConfig :: from_pem_file ( tls_config. cert_file , tls_config. key_file ) . await ?;
299
+ let reloadable_rust_config = rust_config. clone ( ) ;
300
+
301
+ let inotify =
302
+ inotify:: Inotify :: init ( ) . map_err ( |e| anyhow ! ( "Cannot initialize inotify: {e}" ) ) ?;
303
+ let cert_watch = inotify
304
+ . watches ( )
305
+ . add ( cert_file. clone ( ) , inotify:: WatchMask :: MODIFY )
306
+ . map_err ( |e| anyhow ! ( "Cannot watch certificate file: {e}" ) ) ?;
307
+ let key_watch = inotify
308
+ . watches ( )
309
+ . add ( key_file. clone ( ) , inotify:: WatchMask :: MODIFY )
310
+ . map_err ( |e| anyhow ! ( "Cannot watch key file: {e}" ) ) ?;
311
+
312
+ let buffer = [ 0 ; 1024 ] ;
313
+ let stream = inotify
314
+ . into_event_stream ( buffer)
315
+ . map_err ( |e| anyhow ! ( "Cannot create inotify event stream: {e}" ) ) ?;
316
+
317
+ tokio:: spawn ( async move {
318
+ tokio:: pin!( stream) ;
319
+ let mut cert_changed = false ;
320
+ let mut key_changed = false ;
321
+
322
+ while let Some ( event) = stream. next ( ) . await {
323
+ let event = match event {
324
+ Ok ( event) => event,
325
+ Err ( e) => {
326
+ warn ! ( "Cannot read inotify event: {e}" ) ;
327
+ continue ;
328
+ }
329
+ } ;
330
+
331
+ if event. wd == cert_watch {
332
+ info ! ( "TLS certificate file has been modified" ) ;
333
+ cert_changed = true ;
334
+ }
335
+ if event. wd == key_watch {
336
+ info ! ( "TLS key file has been modified" ) ;
337
+ key_changed = true ;
338
+ }
339
+
340
+ if key_changed && cert_changed {
341
+ info ! ( "reloading TLS certificate" ) ;
342
+
343
+ cert_changed = false ;
344
+ key_changed = false ;
345
+ reloadable_rust_config
346
+ . reload_from_pem_file ( cert_file. clone ( ) , key_file. clone ( ) )
347
+ . await
348
+ . expect ( "Cannot reload TLS certificate" ) ; // we want to panic here
349
+ }
350
+ }
351
+ } ) ;
352
+
353
+ Ok ( rust_config)
354
+ }
355
+
272
356
fn precompile_policies (
273
357
engine : & wasmtime:: Engine ,
274
358
fetched_policies : & FetchedPolicies ,
0 commit comments