@@ -26,13 +26,15 @@ import (
26
26
"iter"
27
27
"log/slog"
28
28
"slices"
29
+ "time"
29
30
30
31
"filippo.io/age"
31
32
"github.com/gravitational/trace"
32
33
33
34
"github.com/gravitational/teleport"
34
35
recordingencryptionv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/recordingencryption/v1"
35
36
"github.com/gravitational/teleport/api/types"
37
+ "github.com/gravitational/teleport/api/utils/retryutils"
36
38
"github.com/gravitational/teleport/lib/backend"
37
39
"github.com/gravitational/teleport/lib/cryptosuites"
38
40
"github.com/gravitational/teleport/lib/services"
@@ -335,6 +337,111 @@ func (m *Manager) FindDecryptionKey(ctx context.Context, publicKeys ...[]byte) (
335
337
return nil , trace .NotFound ("no accessible decryption key found" )
336
338
}
337
339
340
+ func (m * Manager ) Watch (ctx context.Context , events types.Events ) (err error ) {
341
+ // shouldRetryAfterJitterFn waits at most 5 seconds and returns a bool specifying whether or not
342
+ // execution should continue
343
+ shouldRetryAfterJitterFn := func () bool {
344
+ select {
345
+ case <- time .After (retryutils .SeventhJitter (time .Second * 5 )):
346
+ return true
347
+ case <- ctx .Done ():
348
+ return false
349
+ }
350
+ }
351
+
352
+ defer func () {
353
+ m .logger .InfoContext (ctx , "stopping encryption watcher" , "error" , err )
354
+ }()
355
+
356
+ for {
357
+ watch , err := events .NewWatcher (ctx , types.Watch {
358
+ Name : "recording_encryption_watcher" ,
359
+ Kinds : []types.WatchKind {
360
+ {
361
+ Kind : types .KindRecordingEncryption ,
362
+ },
363
+ {
364
+ Kind : types .KindSessionRecordingConfig ,
365
+ },
366
+ },
367
+ })
368
+ if err != nil {
369
+ m .logger .ErrorContext (ctx , "failed to create watcher, retrying" , "error" , err )
370
+ if ! shouldRetryAfterJitterFn () {
371
+ return nil
372
+ }
373
+ continue
374
+ }
375
+ defer watch .Close ()
376
+
377
+ HandleEvents:
378
+ for {
379
+ select {
380
+ case ev := <- watch .Events ():
381
+ if err := m .handleEvent (ctx , ev , shouldRetryAfterJitterFn ); err != nil {
382
+ m .logger .ErrorContext (ctx , "failure handling recording encryption event" , "kind" , ev .Resource .GetKind (), "error" , err )
383
+ }
384
+ case <- watch .Done ():
385
+ if err := watch .Error (); err == nil {
386
+ return nil
387
+ }
388
+
389
+ m .logger .ErrorContext (ctx , "watcher failed, retrying" , "error" , err )
390
+ if ! shouldRetryAfterJitterFn () {
391
+ return nil
392
+ }
393
+ break HandleEvents
394
+ case <- ctx .Done ():
395
+ return nil
396
+ }
397
+
398
+ }
399
+ }
400
+ }
401
+
402
+ func (m * Manager ) handleEvent (ctx context.Context , ev types.Event , shouldRetryFn func () bool ) error {
403
+ if ev .Type != types .OpPut {
404
+ return nil
405
+ }
406
+
407
+ kind := ev .Resource .GetKind ()
408
+ for {
409
+ switch kind {
410
+ case types .KindRecordingEncryption :
411
+ if _ , err := m .ResolveRecordingEncryption (ctx ); err != nil {
412
+ m .logger .ErrorContext (ctx , "failed to resolve recording encryption keys, retrying" , "error" , err )
413
+ if shouldRetryFn () {
414
+ continue
415
+ }
416
+
417
+ return trace .Wrap (err )
418
+ }
419
+ case types .KindSessionRecordingConfig :
420
+ previousConfig := m .sessionRecordingConfig
421
+ var err error
422
+ m .sessionRecordingConfig , err = m .GetSessionRecordingConfig (ctx )
423
+ if err != nil {
424
+ m .logger .ErrorContext (ctx , "failed to fetch updated session_recording_config" , "error" , err )
425
+ if shouldRetryFn () {
426
+ continue
427
+ }
428
+
429
+ return trace .Wrap (err )
430
+ }
431
+
432
+ if m .sessionRecordingConfig .GetEncrypted () && (previousConfig == nil || ! previousConfig .GetEncrypted ()) {
433
+
434
+ // restart the loop and resolve recording encryption if
435
+ // encryption was just enabled
436
+ kind = types .KindRecordingEncryption
437
+ continue
438
+ }
439
+ }
440
+
441
+ return nil
442
+ }
443
+ }
444
+
338
445
// getAgeEncryptionKeys returns an iterator of AgeEncryptionKeys from a list of WrappedKeys. This is for use in
339
446
// populating the EncryptionKeys field of SessionRecordingConfigStatus.
340
447
func getAgeEncryptionKeys (keys []* recordingencryptionv1.WrappedKey ) iter.Seq [* types.AgeEncryptionKey ] {
0 commit comments