@@ -24,15 +24,17 @@ import (
24
24
"iter"
25
25
"log/slog"
26
26
"slices"
27
+ "time"
27
28
28
29
"filippo.io/age"
29
30
"github.com/gravitational/trace"
30
31
32
+ "github.com/gravitational/teleport"
31
33
recordingencryptionv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/recordingencryption/v1"
32
34
"github.com/gravitational/teleport/api/types"
35
+ "github.com/gravitational/teleport/api/utils/retryutils"
33
36
"github.com/gravitational/teleport/lib/backend"
34
37
"github.com/gravitational/teleport/lib/cryptosuites"
35
- "github.com/gravitational/teleport/lib/events"
36
38
"github.com/gravitational/teleport/lib/services"
37
39
)
38
40
@@ -51,10 +53,6 @@ type ManagerConfig struct {
51
53
52
54
// NewManager returns a new Manager using the given ManagerConfig.
53
55
func NewManager (cfg ManagerConfig ) (* Manager , error ) {
54
- if cfg .Logger == nil {
55
- cfg .Logger = slog .Default ()
56
- }
57
-
58
56
if cfg .Backend == nil {
59
57
return nil , trace .BadParameter ("backend is required" )
60
58
}
@@ -63,6 +61,10 @@ func NewManager(cfg ManagerConfig) (*Manager, error) {
63
61
return nil , trace .BadParameter ("key store is required" )
64
62
}
65
63
64
+ if cfg .Logger == nil {
65
+ cfg .Logger = slog .With (teleport .ComponentKey , "encryption-manager" )
66
+ }
67
+
66
68
return & Manager {
67
69
RecordingEncryption : cfg .Backend ,
68
70
keyStore : cfg .KeyStore ,
@@ -78,7 +80,6 @@ type Manager struct {
78
80
79
81
logger * slog.Logger
80
82
keyStore EncryptionKeyStore
81
- uploader events.MultipartUploader
82
83
}
83
84
84
85
// ensureActiveRecordingEncryption returns the configured RecordingEncryption resource if it exists with active keys. If it does not,
@@ -301,94 +302,130 @@ type RecordingEncryptionResolver interface {
301
302
ResolveRecordingEncryption (ctx context.Context ) (* recordingencryptionv1.RecordingEncryption , error )
302
303
}
303
304
304
- // WatchConfig captures required dependencies for building a RecordingEncyprtion watcher that
305
+ // WatchConfig captures required dependencies for building a RecordingEncryption watcher that
305
306
// automatically resolves state.
306
307
type WatchConfig struct {
307
308
Events types.Events
308
309
Resolver RecordingEncryptionResolver
309
310
ClusterConfig services.ClusterConfiguration
310
311
Logger * slog.Logger
311
- LockConfig backend.RunWhileLockedConfig
312
+ LockConfig * backend.RunWhileLockedConfig
313
+ }
314
+
315
+ // A Watcher watches for changes to the RecordingEncryption resource and resolves the state for the calling
316
+ // auth server.
317
+ type Watcher struct {
318
+ events types.Events
319
+ resolver RecordingEncryptionResolver
320
+ clusterConfig services.ClusterConfiguration
321
+ logger * slog.Logger
322
+ lockConfig * backend.RunWhileLockedConfig
312
323
}
313
324
314
- // Watch creates a watcher responsible for responding to changes in the RecordingEncryption
315
- // resource. This is how auth servers cooperate and ensure there are accessible wrapped keys for each unique
316
- // keystore configuration in a cluster.
317
- func Watch (ctx context.Context , cfg WatchConfig ) error {
325
+ // NewWatcher returns a new Watcher.
326
+ func NewWatcher (cfg WatchConfig ) (* Watcher , error ) {
318
327
switch {
319
328
case cfg .Events == nil :
320
- return trace .BadParameter ("events is required" )
329
+ return nil , trace .BadParameter ("events is required" )
321
330
case cfg .Resolver == nil :
322
- return trace .BadParameter ("recording encryption resolver is required" )
331
+ return nil , trace .BadParameter ("recording encryption resolver is required" )
323
332
case cfg .ClusterConfig == nil :
324
- return trace .BadParameter ("cluster config backend is required" )
333
+ return nil , trace .BadParameter ("cluster config backend is required" )
334
+ case cfg .LockConfig == nil :
335
+ return nil , trace .BadParameter ("lock config is required" )
325
336
}
326
337
if cfg .Logger == nil {
327
- cfg .Logger = slog .Default ( )
338
+ cfg .Logger = slog .With ( teleport . ComponentKey , "encryption-watcher" )
328
339
}
329
340
330
- cfg .Logger .DebugContext (ctx , "creating recording_encryption watcher" )
331
- w , err := cfg .Events .NewWatcher (ctx , types.Watch {
332
- Name : "recording_encryption_watcher" ,
333
- Kinds : []types.WatchKind {
334
- {
335
- Kind : types .KindRecordingEncryption ,
336
- },
337
- },
338
- })
339
- if err != nil {
340
- return trace .Wrap (err )
341
+ return & Watcher {
342
+ events : cfg .Events ,
343
+ resolver : cfg .Resolver ,
344
+ clusterConfig : cfg .ClusterConfig ,
345
+ logger : cfg .Logger ,
346
+ lockConfig : cfg .LockConfig ,
347
+ }, nil
348
+ }
349
+
350
+ // Watch creates a watcher responsible for responding to changes in the RecordingEncryption resource.
351
+ // This is how auth servers cooperate and ensure there are accessible wrapped keys for each unique keystore
352
+ // configuration in a cluster.
353
+ func (w * Watcher ) Run (ctx context.Context ) (err error ) {
354
+ jitter := func () {
355
+ <- time .After (retryutils .SeventhJitter (time .Second * 5 ))
341
356
}
342
357
343
- go func () {
358
+ defer func () {
359
+ w .logger .InfoContext (ctx , "stopping encryption watcher" , "error" , err )
360
+ }()
361
+
362
+ for {
363
+ watch , err := w .events .NewWatcher (ctx , types.Watch {
364
+ Name : "recording_encryption_watcher" ,
365
+ Kinds : []types.WatchKind {
366
+ {
367
+ Kind : types .KindRecordingEncryption ,
368
+ },
369
+ },
370
+ })
371
+ if err != nil {
372
+ w .logger .ErrorContext (ctx , "failed to create watcher, retrying" , "error" , err )
373
+ jitter ()
374
+ }
375
+
376
+ HandleEvents:
344
377
for {
378
+ err := w .handleRecordingEncryptionChange (ctx )
379
+ if err != nil {
380
+ w .logger .ErrorContext (ctx , "failed to handle session recording config change" , "error" , err )
381
+ jitter ()
382
+ continue
383
+
384
+ }
385
+
345
386
select {
346
- case ev := <- w .Events ():
387
+ case ev := <- watch .Events ():
347
388
if ev .Type != types .OpPut {
348
389
continue
349
390
}
350
- const retries = 3
351
- for tries := range retries {
352
- err := handleRecordingEncryptionChange (ctx , cfg )
353
- if err == nil {
354
- break
355
- }
356
-
357
- cfg .Logger .ErrorContext (ctx , "failed to handle session recording config change" , "error" , err , "remaining_tries" , retries - tries - 1 )
391
+ case <- watch .Done ():
392
+ if err := watch .Error (); err == nil {
393
+ return nil
358
394
}
359
395
360
- case <- w .Done ():
361
- cfg .Logger .DebugContext (ctx , "no longer watching recording_encryption" )
362
- return
396
+ w .logger .ErrorContext (ctx , "watcher failed, retrying" , "error" , err )
397
+ jitter ()
398
+ break HandleEvents
399
+ case <- ctx .Done ():
400
+ watch .Close ()
401
+ return ctx .Err ()
363
402
}
364
403
}
365
- }()
366
-
367
- return nil
404
+ }
368
405
}
369
406
370
407
// this helper handles reacting to individual Put events on the RecordingEncryption resource and updates the
371
408
// SessionRecordingConfig with the results, if necessary
372
- func handleRecordingEncryptionChange (ctx context.Context , cfg WatchConfig ) error {
373
- return trace .Wrap (backend .RunWhileLocked (ctx , cfg . LockConfig , func (ctx context.Context ) error {
374
- recConfig , err := cfg . ClusterConfig .GetSessionRecordingConfig (ctx )
409
+ func ( w * Watcher ) handleRecordingEncryptionChange (ctx context.Context ) error {
410
+ return trace .Wrap (backend .RunWhileLocked (ctx , * w . lockConfig , func (ctx context.Context ) error {
411
+ recConfig , err := w . clusterConfig .GetSessionRecordingConfig (ctx )
375
412
if err != nil {
376
413
return trace .Wrap (err , "fetching recording config" )
377
414
}
378
415
379
416
if ! recConfig .GetEncrypted () {
380
- cfg . Logger .DebugContext (ctx , "session recording encryption disabled, skip resolving keys" )
417
+ w . logger .DebugContext (ctx , "session recording encryption disabled, skip resolving keys" )
381
418
return nil
382
419
}
383
420
384
- encryption , err := cfg . Resolver .ResolveRecordingEncryption (ctx )
421
+ encryption , err := w . resolver .ResolveRecordingEncryption (ctx )
385
422
if err != nil {
386
- cfg . Logger .ErrorContext (ctx , "failed to resolve recording encryption state" , "error" , err )
423
+ w . logger .ErrorContext (ctx , "failed to resolve recording encryption state" , "error" , err )
387
424
return trace .Wrap (err , "resolving recording encryption" )
388
425
}
389
426
390
427
if recConfig .SetEncryptionKeys (GetAgeEncryptionKeys (encryption .GetSpec ().ActiveKeys )) {
391
- _ , err = cfg . ClusterConfig .UpdateSessionRecordingConfig (ctx , recConfig )
428
+ _ , err = w . clusterConfig .UpdateSessionRecordingConfig (ctx , recConfig )
392
429
return trace .Wrap (err , "updating encryption keys" )
393
430
}
394
431
0 commit comments