From b609982cf22df91da17fa0b939b51d6d124216c9 Mon Sep 17 00:00:00 2001 From: Erik Tate Date: Tue, 5 Aug 2025 16:38:17 -0400 Subject: [PATCH] adding new protos for rotated keys and the local service for interacting with them --- .../v1/recording_encryption.pb.go | 313 ++++++++++- .../v1/recording_encryption_service.pb.go | 489 ++++++++++++++++-- .../recording_encryption_service_grpc.pb.go | 166 +++++- .../v1/recording_encryption.proto | 38 ++ .../v1/recording_encryption_service.proto | 51 +- api/types/constants.go | 3 + lib/auth/recordingencryption/manager.go | 4 +- lib/services/local/events.go | 2 + lib/services/local/recording_encryption.go | 95 +++- .../local/recording_encryption_test.go | 99 ++++ lib/services/recording_encryption.go | 8 + 11 files changed, 1204 insertions(+), 64 deletions(-) diff --git a/api/gen/proto/go/teleport/recordingencryption/v1/recording_encryption.pb.go b/api/gen/proto/go/teleport/recordingencryption/v1/recording_encryption.pb.go index 8dc36719a1806..41be3b7e2f965 100644 --- a/api/gen/proto/go/teleport/recordingencryption/v1/recording_encryption.pb.go +++ b/api/gen/proto/go/teleport/recordingencryption/v1/recording_encryption.pb.go @@ -37,11 +37,71 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// The possible states a KeyPair can be in. +type KeyPairState int32 + +const ( + // Unspecified value + KeyPairState_KEY_PAIR_STATE_UNSPECIFIED KeyPairState = 0 + // Represents an active key. + KeyPairState_KEY_PAIR_STATE_ACTIVE KeyPairState = 1 + // Represents a key in the process of being rotated. + KeyPairState_KEY_PAIR_STATE_ROTATING KeyPairState = 2 + // Represents a key being rotated in that is inaccessible to at least one + // auth server. + KeyPairState_KEY_PAIR_STATE_INACCESSIBLE KeyPairState = 3 +) + +// Enum value maps for KeyPairState. +var ( + KeyPairState_name = map[int32]string{ + 0: "KEY_PAIR_STATE_UNSPECIFIED", + 1: "KEY_PAIR_STATE_ACTIVE", + 2: "KEY_PAIR_STATE_ROTATING", + 3: "KEY_PAIR_STATE_INACCESSIBLE", + } + KeyPairState_value = map[string]int32{ + "KEY_PAIR_STATE_UNSPECIFIED": 0, + "KEY_PAIR_STATE_ACTIVE": 1, + "KEY_PAIR_STATE_ROTATING": 2, + "KEY_PAIR_STATE_INACCESSIBLE": 3, + } +) + +func (x KeyPairState) Enum() *KeyPairState { + p := new(KeyPairState) + *p = x + return p +} + +func (x KeyPairState) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (KeyPairState) Descriptor() protoreflect.EnumDescriptor { + return file_teleport_recordingencryption_v1_recording_encryption_proto_enumTypes[0].Descriptor() +} + +func (KeyPairState) Type() protoreflect.EnumType { + return &file_teleport_recordingencryption_v1_recording_encryption_proto_enumTypes[0] +} + +func (x KeyPairState) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use KeyPairState.Descriptor instead. +func (KeyPairState) EnumDescriptor() ([]byte, []int) { + return file_teleport_recordingencryption_v1_recording_encryption_proto_rawDescGZIP(), []int{0} +} + // A key pair used with age to wrap and unwrap file keys for session recording encryption. type KeyPair struct { state protoimpl.MessageState `protogen:"open.v1"` // A key pair used with age to wrap and unwrap file keys for session recording encryption. - KeyPair *types.EncryptionKeyPair `protobuf:"bytes,1,opt,name=key_pair,json=keyPair,proto3" json:"key_pair,omitempty"` + KeyPair *types.EncryptionKeyPair `protobuf:"bytes,1,opt,name=key_pair,json=keyPair,proto3" json:"key_pair,omitempty"` + // The current state of the key pair. + State KeyPairState `protobuf:"varint,2,opt,name=state,proto3,enum=teleport.recordingencryption.v1.KeyPairState" json:"state,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -83,6 +143,13 @@ func (x *KeyPair) GetKeyPair() *types.EncryptionKeyPair { return nil } +func (x *KeyPair) GetState() KeyPairState { + if x != nil { + return x.State + } + return KeyPairState_KEY_PAIR_STATE_UNSPECIFIED +} + // RecordingEncryptionSpec contains the active key set for encrypted session recording. type RecordingEncryptionSpec struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -254,13 +321,186 @@ func (x *RecordingEncryption) GetStatus() *RecordingEncryptionStatus { return nil } +// A rotated key pair previously used with age to wrap and unwrap file keys for session recording +// encryption. +type RotatedKeySpec struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The rotated key pair previously used with age to wrap and unwrap file keys for session recording + // encryption. + EncryptionKeyPair *types.EncryptionKeyPair `protobuf:"bytes,2,opt,name=encryption_key_pair,json=encryptionKeyPair,proto3" json:"encryption_key_pair,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RotatedKeySpec) Reset() { + *x = RotatedKeySpec{} + mi := &file_teleport_recordingencryption_v1_recording_encryption_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RotatedKeySpec) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RotatedKeySpec) ProtoMessage() {} + +func (x *RotatedKeySpec) ProtoReflect() protoreflect.Message { + mi := &file_teleport_recordingencryption_v1_recording_encryption_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RotatedKeySpec.ProtoReflect.Descriptor instead. +func (*RotatedKeySpec) Descriptor() ([]byte, []int) { + return file_teleport_recordingencryption_v1_recording_encryption_proto_rawDescGZIP(), []int{4} +} + +func (x *RotatedKeySpec) GetEncryptionKeyPair() *types.EncryptionKeyPair { + if x != nil { + return x.EncryptionKeyPair + } + return nil +} + +// The empty status of a RotatedKey. +type RotatedKeyStatus struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RotatedKeyStatus) Reset() { + *x = RotatedKeyStatus{} + mi := &file_teleport_recordingencryption_v1_recording_encryption_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RotatedKeyStatus) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RotatedKeyStatus) ProtoMessage() {} + +func (x *RotatedKeyStatus) ProtoReflect() protoreflect.Message { + mi := &file_teleport_recordingencryption_v1_recording_encryption_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RotatedKeyStatus.ProtoReflect.Descriptor instead. +func (*RotatedKeyStatus) Descriptor() ([]byte, []int) { + return file_teleport_recordingencryption_v1_recording_encryption_proto_rawDescGZIP(), []int{5} +} + +// A previously rotated encryption key for session recordings kept for future replay. The metadata.name +// is expected to be the fingerprint of the public key contained in the spec, which is a hex encoded +// SHA256 hash of its PKIX form. +type RotatedKey struct { + state protoimpl.MessageState `protogen:"open.v1"` + Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` + SubKind string `protobuf:"bytes,2,opt,name=sub_kind,json=subKind,proto3" json:"sub_kind,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + Metadata *v1.Metadata `protobuf:"bytes,4,opt,name=metadata,proto3" json:"metadata,omitempty"` + Spec *RotatedKeySpec `protobuf:"bytes,5,opt,name=spec,proto3" json:"spec,omitempty"` + Status *RotatedKeyStatus `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RotatedKey) Reset() { + *x = RotatedKey{} + mi := &file_teleport_recordingencryption_v1_recording_encryption_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RotatedKey) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RotatedKey) ProtoMessage() {} + +func (x *RotatedKey) ProtoReflect() protoreflect.Message { + mi := &file_teleport_recordingencryption_v1_recording_encryption_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RotatedKey.ProtoReflect.Descriptor instead. +func (*RotatedKey) Descriptor() ([]byte, []int) { + return file_teleport_recordingencryption_v1_recording_encryption_proto_rawDescGZIP(), []int{6} +} + +func (x *RotatedKey) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +func (x *RotatedKey) GetSubKind() string { + if x != nil { + return x.SubKind + } + return "" +} + +func (x *RotatedKey) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *RotatedKey) GetMetadata() *v1.Metadata { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *RotatedKey) GetSpec() *RotatedKeySpec { + if x != nil { + return x.Spec + } + return nil +} + +func (x *RotatedKey) GetStatus() *RotatedKeyStatus { + if x != nil { + return x.Status + } + return nil +} + var File_teleport_recordingencryption_v1_recording_encryption_proto protoreflect.FileDescriptor const file_teleport_recordingencryption_v1_recording_encryption_proto_rawDesc = "" + "\n" + - ":teleport/recordingencryption/v1/recording_encryption.proto\x12\x1fteleport.recordingencryption.v1\x1a!teleport/header/v1/metadata.proto\x1a!teleport/legacy/types/types.proto\">\n" + + ":teleport/recordingencryption/v1/recording_encryption.proto\x12\x1fteleport.recordingencryption.v1\x1a!teleport/header/v1/metadata.proto\x1a!teleport/legacy/types/types.proto\"\x83\x01\n" + "\aKeyPair\x123\n" + - "\bkey_pair\x18\x01 \x01(\v2\x18.types.EncryptionKeyPairR\akeyPair\"\x80\x01\n" + + "\bkey_pair\x18\x01 \x01(\v2\x18.types.EncryptionKeyPairR\akeyPair\x12C\n" + + "\x05state\x18\x02 \x01(\x0e2-.teleport.recordingencryption.v1.KeyPairStateR\x05state\"\x80\x01\n" + "\x17RecordingEncryptionSpec\x12R\n" + "\x10active_key_pairs\x18\x02 \x03(\v2(.teleport.recordingencryption.v1.KeyPairR\x0eactiveKeyPairsJ\x04\b\x01\x10\x02R\vactive_keys\"\x1b\n" + "\x19RecordingEncryptionStatus\"\xba\x02\n" + @@ -270,7 +510,23 @@ const file_teleport_recordingencryption_v1_recording_encryption_proto_rawDesc = "\aversion\x18\x03 \x01(\tR\aversion\x128\n" + "\bmetadata\x18\x04 \x01(\v2\x1c.teleport.header.v1.MetadataR\bmetadata\x12L\n" + "\x04spec\x18\x05 \x01(\v28.teleport.recordingencryption.v1.RecordingEncryptionSpecR\x04spec\x12R\n" + - "\x06status\x18\x06 \x01(\v2:.teleport.recordingencryption.v1.RecordingEncryptionStatusR\x06statusBjZhgithub.com/gravitational/teleport/api/gen/proto/go/teleport/recordingencryption/v1;recordingencryptionv1b\x06proto3" + "\x06status\x18\x06 \x01(\v2:.teleport.recordingencryption.v1.RecordingEncryptionStatusR\x06status\"Z\n" + + "\x0eRotatedKeySpec\x12H\n" + + "\x13encryption_key_pair\x18\x02 \x01(\v2\x18.types.EncryptionKeyPairR\x11encryptionKeyPair\"\x12\n" + + "\x10RotatedKeyStatus\"\x9f\x02\n" + + "\n" + + "RotatedKey\x12\x12\n" + + "\x04kind\x18\x01 \x01(\tR\x04kind\x12\x19\n" + + "\bsub_kind\x18\x02 \x01(\tR\asubKind\x12\x18\n" + + "\aversion\x18\x03 \x01(\tR\aversion\x128\n" + + "\bmetadata\x18\x04 \x01(\v2\x1c.teleport.header.v1.MetadataR\bmetadata\x12C\n" + + "\x04spec\x18\x05 \x01(\v2/.teleport.recordingencryption.v1.RotatedKeySpecR\x04spec\x12I\n" + + "\x06status\x18\x06 \x01(\v21.teleport.recordingencryption.v1.RotatedKeyStatusR\x06status*\x87\x01\n" + + "\fKeyPairState\x12\x1e\n" + + "\x1aKEY_PAIR_STATE_UNSPECIFIED\x10\x00\x12\x19\n" + + "\x15KEY_PAIR_STATE_ACTIVE\x10\x01\x12\x1b\n" + + "\x17KEY_PAIR_STATE_ROTATING\x10\x02\x12\x1f\n" + + "\x1bKEY_PAIR_STATE_INACCESSIBLE\x10\x03BjZhgithub.com/gravitational/teleport/api/gen/proto/go/teleport/recordingencryption/v1;recordingencryptionv1b\x06proto3" var ( file_teleport_recordingencryption_v1_recording_encryption_proto_rawDescOnce sync.Once @@ -284,26 +540,36 @@ func file_teleport_recordingencryption_v1_recording_encryption_proto_rawDescGZIP return file_teleport_recordingencryption_v1_recording_encryption_proto_rawDescData } -var file_teleport_recordingencryption_v1_recording_encryption_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_teleport_recordingencryption_v1_recording_encryption_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_teleport_recordingencryption_v1_recording_encryption_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_teleport_recordingencryption_v1_recording_encryption_proto_goTypes = []any{ - (*KeyPair)(nil), // 0: teleport.recordingencryption.v1.KeyPair - (*RecordingEncryptionSpec)(nil), // 1: teleport.recordingencryption.v1.RecordingEncryptionSpec - (*RecordingEncryptionStatus)(nil), // 2: teleport.recordingencryption.v1.RecordingEncryptionStatus - (*RecordingEncryption)(nil), // 3: teleport.recordingencryption.v1.RecordingEncryption - (*types.EncryptionKeyPair)(nil), // 4: types.EncryptionKeyPair - (*v1.Metadata)(nil), // 5: teleport.header.v1.Metadata + (KeyPairState)(0), // 0: teleport.recordingencryption.v1.KeyPairState + (*KeyPair)(nil), // 1: teleport.recordingencryption.v1.KeyPair + (*RecordingEncryptionSpec)(nil), // 2: teleport.recordingencryption.v1.RecordingEncryptionSpec + (*RecordingEncryptionStatus)(nil), // 3: teleport.recordingencryption.v1.RecordingEncryptionStatus + (*RecordingEncryption)(nil), // 4: teleport.recordingencryption.v1.RecordingEncryption + (*RotatedKeySpec)(nil), // 5: teleport.recordingencryption.v1.RotatedKeySpec + (*RotatedKeyStatus)(nil), // 6: teleport.recordingencryption.v1.RotatedKeyStatus + (*RotatedKey)(nil), // 7: teleport.recordingencryption.v1.RotatedKey + (*types.EncryptionKeyPair)(nil), // 8: types.EncryptionKeyPair + (*v1.Metadata)(nil), // 9: teleport.header.v1.Metadata } var file_teleport_recordingencryption_v1_recording_encryption_proto_depIdxs = []int32{ - 4, // 0: teleport.recordingencryption.v1.KeyPair.key_pair:type_name -> types.EncryptionKeyPair - 0, // 1: teleport.recordingencryption.v1.RecordingEncryptionSpec.active_key_pairs:type_name -> teleport.recordingencryption.v1.KeyPair - 5, // 2: teleport.recordingencryption.v1.RecordingEncryption.metadata:type_name -> teleport.header.v1.Metadata - 1, // 3: teleport.recordingencryption.v1.RecordingEncryption.spec:type_name -> teleport.recordingencryption.v1.RecordingEncryptionSpec - 2, // 4: teleport.recordingencryption.v1.RecordingEncryption.status:type_name -> teleport.recordingencryption.v1.RecordingEncryptionStatus - 5, // [5:5] is the sub-list for method output_type - 5, // [5:5] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 8, // 0: teleport.recordingencryption.v1.KeyPair.key_pair:type_name -> types.EncryptionKeyPair + 0, // 1: teleport.recordingencryption.v1.KeyPair.state:type_name -> teleport.recordingencryption.v1.KeyPairState + 1, // 2: teleport.recordingencryption.v1.RecordingEncryptionSpec.active_key_pairs:type_name -> teleport.recordingencryption.v1.KeyPair + 9, // 3: teleport.recordingencryption.v1.RecordingEncryption.metadata:type_name -> teleport.header.v1.Metadata + 2, // 4: teleport.recordingencryption.v1.RecordingEncryption.spec:type_name -> teleport.recordingencryption.v1.RecordingEncryptionSpec + 3, // 5: teleport.recordingencryption.v1.RecordingEncryption.status:type_name -> teleport.recordingencryption.v1.RecordingEncryptionStatus + 8, // 6: teleport.recordingencryption.v1.RotatedKeySpec.encryption_key_pair:type_name -> types.EncryptionKeyPair + 9, // 7: teleport.recordingencryption.v1.RotatedKey.metadata:type_name -> teleport.header.v1.Metadata + 5, // 8: teleport.recordingencryption.v1.RotatedKey.spec:type_name -> teleport.recordingencryption.v1.RotatedKeySpec + 6, // 9: teleport.recordingencryption.v1.RotatedKey.status:type_name -> teleport.recordingencryption.v1.RotatedKeyStatus + 10, // [10:10] is the sub-list for method output_type + 10, // [10:10] is the sub-list for method input_type + 10, // [10:10] is the sub-list for extension type_name + 10, // [10:10] is the sub-list for extension extendee + 0, // [0:10] is the sub-list for field type_name } func init() { file_teleport_recordingencryption_v1_recording_encryption_proto_init() } @@ -316,13 +582,14 @@ func file_teleport_recordingencryption_v1_recording_encryption_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_teleport_recordingencryption_v1_recording_encryption_proto_rawDesc), len(file_teleport_recordingencryption_v1_recording_encryption_proto_rawDesc)), - NumEnums: 0, - NumMessages: 4, + NumEnums: 1, + NumMessages: 7, NumExtensions: 0, NumServices: 0, }, GoTypes: file_teleport_recordingencryption_v1_recording_encryption_proto_goTypes, DependencyIndexes: file_teleport_recordingencryption_v1_recording_encryption_proto_depIdxs, + EnumInfos: file_teleport_recordingencryption_v1_recording_encryption_proto_enumTypes, MessageInfos: file_teleport_recordingencryption_v1_recording_encryption_proto_msgTypes, }.Build() File_teleport_recordingencryption_v1_recording_encryption_proto = out.File diff --git a/api/gen/proto/go/teleport/recordingencryption/v1/recording_encryption_service.pb.go b/api/gen/proto/go/teleport/recordingencryption/v1/recording_encryption_service.pb.go index 34a7e66d9e934..1b0ca645dd6e9 100644 --- a/api/gen/proto/go/teleport/recordingencryption/v1/recording_encryption_service.pb.go +++ b/api/gen/proto/go/teleport/recordingencryption/v1/recording_encryption_service.pb.go @@ -413,7 +413,7 @@ func (x *CompleteUploadRequest) GetParts() []*Part { return nil } -// CompleteUploadResponse is the empty return value of a CompleteUpload request. +// The body of a CompleteUpload request. type CompleteUploadResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -450,11 +450,395 @@ func (*CompleteUploadResponse) Descriptor() ([]byte, []int) { return file_teleport_recordingencryption_v1_recording_encryption_service_proto_rawDescGZIP(), []int{7} } +// The body of a RotateKey request. +type RotateKeyRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RotateKeyRequest) Reset() { + *x = RotateKeyRequest{} + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RotateKeyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RotateKeyRequest) ProtoMessage() {} + +func (x *RotateKeyRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RotateKeyRequest.ProtoReflect.Descriptor instead. +func (*RotateKeyRequest) Descriptor() ([]byte, []int) { + return file_teleport_recordingencryption_v1_recording_encryption_service_proto_rawDescGZIP(), []int{8} +} + +// The return value of a RotateKey request. +type RotateKeyResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RotateKeyResponse) Reset() { + *x = RotateKeyResponse{} + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RotateKeyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RotateKeyResponse) ProtoMessage() {} + +func (x *RotateKeyResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RotateKeyResponse.ProtoReflect.Descriptor instead. +func (*RotateKeyResponse) Descriptor() ([]byte, []int) { + return file_teleport_recordingencryption_v1_recording_encryption_service_proto_rawDescGZIP(), []int{9} +} + +// The body of a GetRotationState request. +type GetRotationStateRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetRotationStateRequest) Reset() { + *x = GetRotationStateRequest{} + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetRotationStateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRotationStateRequest) ProtoMessage() {} + +func (x *GetRotationStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRotationStateRequest.ProtoReflect.Descriptor instead. +func (*GetRotationStateRequest) Descriptor() ([]byte, []int) { + return file_teleport_recordingencryption_v1_recording_encryption_service_proto_rawDescGZIP(), []int{10} +} + +func (x *GetRotationStateRequest) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +func (x *GetRotationStateRequest) GetPageToken() string { + if x != nil { + return x.PageToken + } + return "" +} + +// A public key fingerprint coupled with its current state. +type FingerprintWithState struct { + state protoimpl.MessageState `protogen:"open.v1"` + // A fingerprint identifying the public key of a KeyPair. + Fingerprint string `protobuf:"bytes,1,opt,name=fingerprint,proto3" json:"fingerprint,omitempty"` + // The state associated with the identified KeyPair. + State KeyPairState `protobuf:"varint,2,opt,name=state,proto3,enum=teleport.recordingencryption.v1.KeyPairState" json:"state,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FingerprintWithState) Reset() { + *x = FingerprintWithState{} + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FingerprintWithState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FingerprintWithState) ProtoMessage() {} + +func (x *FingerprintWithState) ProtoReflect() protoreflect.Message { + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FingerprintWithState.ProtoReflect.Descriptor instead. +func (*FingerprintWithState) Descriptor() ([]byte, []int) { + return file_teleport_recordingencryption_v1_recording_encryption_service_proto_rawDescGZIP(), []int{11} +} + +func (x *FingerprintWithState) GetFingerprint() string { + if x != nil { + return x.Fingerprint + } + return "" +} + +func (x *FingerprintWithState) GetState() KeyPairState { + if x != nil { + return x.State + } + return KeyPairState_KEY_PAIR_STATE_UNSPECIFIED +} + +// The current state of all active encryption key pairs. +type GetRotationStateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + NextPageToken string `protobuf:"bytes,1,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + // The state of all active encryption key pairs. + KeyPairStates []*FingerprintWithState `protobuf:"bytes,2,rep,name=key_pair_states,json=keyPairStates,proto3" json:"key_pair_states,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetRotationStateResponse) Reset() { + *x = GetRotationStateResponse{} + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetRotationStateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRotationStateResponse) ProtoMessage() {} + +func (x *GetRotationStateResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRotationStateResponse.ProtoReflect.Descriptor instead. +func (*GetRotationStateResponse) Descriptor() ([]byte, []int) { + return file_teleport_recordingencryption_v1_recording_encryption_service_proto_rawDescGZIP(), []int{12} +} + +func (x *GetRotationStateResponse) GetNextPageToken() string { + if x != nil { + return x.NextPageToken + } + return "" +} + +func (x *GetRotationStateResponse) GetKeyPairStates() []*FingerprintWithState { + if x != nil { + return x.KeyPairStates + } + return nil +} + +// The body of a CompleteRotation request. +type CompleteRotationRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CompleteRotationRequest) Reset() { + *x = CompleteRotationRequest{} + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CompleteRotationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CompleteRotationRequest) ProtoMessage() {} + +func (x *CompleteRotationRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CompleteRotationRequest.ProtoReflect.Descriptor instead. +func (*CompleteRotationRequest) Descriptor() ([]byte, []int) { + return file_teleport_recordingencryption_v1_recording_encryption_service_proto_rawDescGZIP(), []int{13} +} + +// The return value of a CompleteRotation request. +type CompleteRotationResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CompleteRotationResponse) Reset() { + *x = CompleteRotationResponse{} + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CompleteRotationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CompleteRotationResponse) ProtoMessage() {} + +func (x *CompleteRotationResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CompleteRotationResponse.ProtoReflect.Descriptor instead. +func (*CompleteRotationResponse) Descriptor() ([]byte, []int) { + return file_teleport_recordingencryption_v1_recording_encryption_service_proto_rawDescGZIP(), []int{14} +} + +// The body of a RollbackRotation request. +type RollbackRotationRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RollbackRotationRequest) Reset() { + *x = RollbackRotationRequest{} + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RollbackRotationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RollbackRotationRequest) ProtoMessage() {} + +func (x *RollbackRotationRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RollbackRotationRequest.ProtoReflect.Descriptor instead. +func (*RollbackRotationRequest) Descriptor() ([]byte, []int) { + return file_teleport_recordingencryption_v1_recording_encryption_service_proto_rawDescGZIP(), []int{15} +} + +// The return value of a RollbackRotation request +type RollbackRotationResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RollbackRotationResponse) Reset() { + *x = RollbackRotationResponse{} + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RollbackRotationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RollbackRotationResponse) ProtoMessage() {} + +func (x *RollbackRotationResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes[16] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RollbackRotationResponse.ProtoReflect.Descriptor instead. +func (*RollbackRotationResponse) Descriptor() ([]byte, []int) { + return file_teleport_recordingencryption_v1_recording_encryption_service_proto_rawDescGZIP(), []int{16} +} + var File_teleport_recordingencryption_v1_recording_encryption_service_proto protoreflect.FileDescriptor const file_teleport_recordingencryption_v1_recording_encryption_service_proto_rawDesc = "" + "\n" + - "Bteleport/recordingencryption/v1/recording_encryption_service.proto\x12\x1fteleport.recordingencryption.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"\x83\x01\n" + + "Bteleport/recordingencryption/v1/recording_encryption_service.proto\x12\x1fteleport.recordingencryption.v1\x1a\x1fgoogle/protobuf/timestamp.proto\x1a:teleport/recordingencryption/v1/recording_encryption.proto\"\x83\x01\n" + "\x06Upload\x12\x1b\n" + "\tupload_id\x18\x01 \x01(\tR\buploadId\x12\x1d\n" + "\n" + @@ -479,12 +863,32 @@ const file_teleport_recordingencryption_v1_recording_encryption_service_proto_ra "\x15CompleteUploadRequest\x12?\n" + "\x06upload\x18\x01 \x01(\v2'.teleport.recordingencryption.v1.UploadR\x06upload\x12;\n" + "\x05parts\x18\x02 \x03(\v2%.teleport.recordingencryption.v1.PartR\x05parts\"\x18\n" + - "\x16CompleteUploadResponse2\x94\x03\n" + + "\x16CompleteUploadResponse\"\x12\n" + + "\x10RotateKeyRequest\"\x13\n" + + "\x11RotateKeyResponse\"U\n" + + "\x17GetRotationStateRequest\x12\x1b\n" + + "\tpage_size\x18\x01 \x01(\x05R\bpageSize\x12\x1d\n" + + "\n" + + "page_token\x18\x02 \x01(\tR\tpageToken\"}\n" + + "\x14FingerprintWithState\x12 \n" + + "\vfingerprint\x18\x01 \x01(\tR\vfingerprint\x12C\n" + + "\x05state\x18\x02 \x01(\x0e2-.teleport.recordingencryption.v1.KeyPairStateR\x05state\"\xa1\x01\n" + + "\x18GetRotationStateResponse\x12&\n" + + "\x0fnext_page_token\x18\x01 \x01(\tR\rnextPageToken\x12]\n" + + "\x0fkey_pair_states\x18\x02 \x03(\v25.teleport.recordingencryption.v1.FingerprintWithStateR\rkeyPairStates\"\x19\n" + + "\x17CompleteRotationRequest\"\x1a\n" + + "\x18CompleteRotationResponse\"\x19\n" + + "\x17RollbackRotationRequest\"\x1a\n" + + "\x18RollbackRotationResponse2\xa6\a\n" + "\x1aRecordingEncryptionService\x12{\n" + "\fCreateUpload\x124.teleport.recordingencryption.v1.CreateUploadRequest\x1a5.teleport.recordingencryption.v1.CreateUploadResponse\x12u\n" + "\n" + "UploadPart\x122.teleport.recordingencryption.v1.UploadPartRequest\x1a3.teleport.recordingencryption.v1.UploadPartResponse\x12\x81\x01\n" + - "\x0eCompleteUpload\x126.teleport.recordingencryption.v1.CompleteUploadRequest\x1a7.teleport.recordingencryption.v1.CompleteUploadResponseBjZhgithub.com/gravitational/teleport/api/gen/proto/go/teleport/recordingencryption/v1;recordingencryptionv1b\x06proto3" + "\x0eCompleteUpload\x126.teleport.recordingencryption.v1.CompleteUploadRequest\x1a7.teleport.recordingencryption.v1.CompleteUploadResponse\x12r\n" + + "\tRotateKey\x121.teleport.recordingencryption.v1.RotateKeyRequest\x1a2.teleport.recordingencryption.v1.RotateKeyResponse\x12\x87\x01\n" + + "\x10GetRotationState\x128.teleport.recordingencryption.v1.GetRotationStateRequest\x1a9.teleport.recordingencryption.v1.GetRotationStateResponse\x12\x87\x01\n" + + "\x10CompleteRotation\x128.teleport.recordingencryption.v1.CompleteRotationRequest\x1a9.teleport.recordingencryption.v1.CompleteRotationResponse\x12\x87\x01\n" + + "\x10RollbackRotation\x128.teleport.recordingencryption.v1.RollbackRotationRequest\x1a9.teleport.recordingencryption.v1.RollbackRotationResponseBjZhgithub.com/gravitational/teleport/api/gen/proto/go/teleport/recordingencryption/v1;recordingencryptionv1b\x06proto3" var ( file_teleport_recordingencryption_v1_recording_encryption_service_proto_rawDescOnce sync.Once @@ -498,36 +902,56 @@ func file_teleport_recordingencryption_v1_recording_encryption_service_proto_raw return file_teleport_recordingencryption_v1_recording_encryption_service_proto_rawDescData } -var file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_teleport_recordingencryption_v1_recording_encryption_service_proto_msgTypes = make([]protoimpl.MessageInfo, 17) var file_teleport_recordingencryption_v1_recording_encryption_service_proto_goTypes = []any{ - (*Upload)(nil), // 0: teleport.recordingencryption.v1.Upload - (*CreateUploadRequest)(nil), // 1: teleport.recordingencryption.v1.CreateUploadRequest - (*CreateUploadResponse)(nil), // 2: teleport.recordingencryption.v1.CreateUploadResponse - (*UploadPartRequest)(nil), // 3: teleport.recordingencryption.v1.UploadPartRequest - (*Part)(nil), // 4: teleport.recordingencryption.v1.Part - (*UploadPartResponse)(nil), // 5: teleport.recordingencryption.v1.UploadPartResponse - (*CompleteUploadRequest)(nil), // 6: teleport.recordingencryption.v1.CompleteUploadRequest - (*CompleteUploadResponse)(nil), // 7: teleport.recordingencryption.v1.CompleteUploadResponse - (*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp + (*Upload)(nil), // 0: teleport.recordingencryption.v1.Upload + (*CreateUploadRequest)(nil), // 1: teleport.recordingencryption.v1.CreateUploadRequest + (*CreateUploadResponse)(nil), // 2: teleport.recordingencryption.v1.CreateUploadResponse + (*UploadPartRequest)(nil), // 3: teleport.recordingencryption.v1.UploadPartRequest + (*Part)(nil), // 4: teleport.recordingencryption.v1.Part + (*UploadPartResponse)(nil), // 5: teleport.recordingencryption.v1.UploadPartResponse + (*CompleteUploadRequest)(nil), // 6: teleport.recordingencryption.v1.CompleteUploadRequest + (*CompleteUploadResponse)(nil), // 7: teleport.recordingencryption.v1.CompleteUploadResponse + (*RotateKeyRequest)(nil), // 8: teleport.recordingencryption.v1.RotateKeyRequest + (*RotateKeyResponse)(nil), // 9: teleport.recordingencryption.v1.RotateKeyResponse + (*GetRotationStateRequest)(nil), // 10: teleport.recordingencryption.v1.GetRotationStateRequest + (*FingerprintWithState)(nil), // 11: teleport.recordingencryption.v1.FingerprintWithState + (*GetRotationStateResponse)(nil), // 12: teleport.recordingencryption.v1.GetRotationStateResponse + (*CompleteRotationRequest)(nil), // 13: teleport.recordingencryption.v1.CompleteRotationRequest + (*CompleteRotationResponse)(nil), // 14: teleport.recordingencryption.v1.CompleteRotationResponse + (*RollbackRotationRequest)(nil), // 15: teleport.recordingencryption.v1.RollbackRotationRequest + (*RollbackRotationResponse)(nil), // 16: teleport.recordingencryption.v1.RollbackRotationResponse + (*timestamppb.Timestamp)(nil), // 17: google.protobuf.Timestamp + (KeyPairState)(0), // 18: teleport.recordingencryption.v1.KeyPairState } var file_teleport_recordingencryption_v1_recording_encryption_service_proto_depIdxs = []int32{ - 8, // 0: teleport.recordingencryption.v1.Upload.initiated_at:type_name -> google.protobuf.Timestamp - 0, // 1: teleport.recordingencryption.v1.CreateUploadResponse.upload:type_name -> teleport.recordingencryption.v1.Upload - 0, // 2: teleport.recordingencryption.v1.UploadPartRequest.upload:type_name -> teleport.recordingencryption.v1.Upload - 4, // 3: teleport.recordingencryption.v1.UploadPartResponse.part:type_name -> teleport.recordingencryption.v1.Part - 0, // 4: teleport.recordingencryption.v1.CompleteUploadRequest.upload:type_name -> teleport.recordingencryption.v1.Upload - 4, // 5: teleport.recordingencryption.v1.CompleteUploadRequest.parts:type_name -> teleport.recordingencryption.v1.Part - 1, // 6: teleport.recordingencryption.v1.RecordingEncryptionService.CreateUpload:input_type -> teleport.recordingencryption.v1.CreateUploadRequest - 3, // 7: teleport.recordingencryption.v1.RecordingEncryptionService.UploadPart:input_type -> teleport.recordingencryption.v1.UploadPartRequest - 6, // 8: teleport.recordingencryption.v1.RecordingEncryptionService.CompleteUpload:input_type -> teleport.recordingencryption.v1.CompleteUploadRequest - 2, // 9: teleport.recordingencryption.v1.RecordingEncryptionService.CreateUpload:output_type -> teleport.recordingencryption.v1.CreateUploadResponse - 5, // 10: teleport.recordingencryption.v1.RecordingEncryptionService.UploadPart:output_type -> teleport.recordingencryption.v1.UploadPartResponse - 7, // 11: teleport.recordingencryption.v1.RecordingEncryptionService.CompleteUpload:output_type -> teleport.recordingencryption.v1.CompleteUploadResponse - 9, // [9:12] is the sub-list for method output_type - 6, // [6:9] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 17, // 0: teleport.recordingencryption.v1.Upload.initiated_at:type_name -> google.protobuf.Timestamp + 0, // 1: teleport.recordingencryption.v1.CreateUploadResponse.upload:type_name -> teleport.recordingencryption.v1.Upload + 0, // 2: teleport.recordingencryption.v1.UploadPartRequest.upload:type_name -> teleport.recordingencryption.v1.Upload + 4, // 3: teleport.recordingencryption.v1.UploadPartResponse.part:type_name -> teleport.recordingencryption.v1.Part + 0, // 4: teleport.recordingencryption.v1.CompleteUploadRequest.upload:type_name -> teleport.recordingencryption.v1.Upload + 4, // 5: teleport.recordingencryption.v1.CompleteUploadRequest.parts:type_name -> teleport.recordingencryption.v1.Part + 18, // 6: teleport.recordingencryption.v1.FingerprintWithState.state:type_name -> teleport.recordingencryption.v1.KeyPairState + 11, // 7: teleport.recordingencryption.v1.GetRotationStateResponse.key_pair_states:type_name -> teleport.recordingencryption.v1.FingerprintWithState + 1, // 8: teleport.recordingencryption.v1.RecordingEncryptionService.CreateUpload:input_type -> teleport.recordingencryption.v1.CreateUploadRequest + 3, // 9: teleport.recordingencryption.v1.RecordingEncryptionService.UploadPart:input_type -> teleport.recordingencryption.v1.UploadPartRequest + 6, // 10: teleport.recordingencryption.v1.RecordingEncryptionService.CompleteUpload:input_type -> teleport.recordingencryption.v1.CompleteUploadRequest + 8, // 11: teleport.recordingencryption.v1.RecordingEncryptionService.RotateKey:input_type -> teleport.recordingencryption.v1.RotateKeyRequest + 10, // 12: teleport.recordingencryption.v1.RecordingEncryptionService.GetRotationState:input_type -> teleport.recordingencryption.v1.GetRotationStateRequest + 13, // 13: teleport.recordingencryption.v1.RecordingEncryptionService.CompleteRotation:input_type -> teleport.recordingencryption.v1.CompleteRotationRequest + 15, // 14: teleport.recordingencryption.v1.RecordingEncryptionService.RollbackRotation:input_type -> teleport.recordingencryption.v1.RollbackRotationRequest + 2, // 15: teleport.recordingencryption.v1.RecordingEncryptionService.CreateUpload:output_type -> teleport.recordingencryption.v1.CreateUploadResponse + 5, // 16: teleport.recordingencryption.v1.RecordingEncryptionService.UploadPart:output_type -> teleport.recordingencryption.v1.UploadPartResponse + 7, // 17: teleport.recordingencryption.v1.RecordingEncryptionService.CompleteUpload:output_type -> teleport.recordingencryption.v1.CompleteUploadResponse + 9, // 18: teleport.recordingencryption.v1.RecordingEncryptionService.RotateKey:output_type -> teleport.recordingencryption.v1.RotateKeyResponse + 12, // 19: teleport.recordingencryption.v1.RecordingEncryptionService.GetRotationState:output_type -> teleport.recordingencryption.v1.GetRotationStateResponse + 14, // 20: teleport.recordingencryption.v1.RecordingEncryptionService.CompleteRotation:output_type -> teleport.recordingencryption.v1.CompleteRotationResponse + 16, // 21: teleport.recordingencryption.v1.RecordingEncryptionService.RollbackRotation:output_type -> teleport.recordingencryption.v1.RollbackRotationResponse + 15, // [15:22] is the sub-list for method output_type + 8, // [8:15] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_teleport_recordingencryption_v1_recording_encryption_service_proto_init() } @@ -535,13 +959,14 @@ func file_teleport_recordingencryption_v1_recording_encryption_service_proto_ini if File_teleport_recordingencryption_v1_recording_encryption_service_proto != nil { return } + file_teleport_recordingencryption_v1_recording_encryption_proto_init() type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_teleport_recordingencryption_v1_recording_encryption_service_proto_rawDesc), len(file_teleport_recordingencryption_v1_recording_encryption_service_proto_rawDesc)), NumEnums: 0, - NumMessages: 8, + NumMessages: 17, NumExtensions: 0, NumServices: 1, }, diff --git a/api/gen/proto/go/teleport/recordingencryption/v1/recording_encryption_service_grpc.pb.go b/api/gen/proto/go/teleport/recordingencryption/v1/recording_encryption_service_grpc.pb.go index 25d49c6d2beea..2227d21eea726 100644 --- a/api/gen/proto/go/teleport/recordingencryption/v1/recording_encryption_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/recordingencryption/v1/recording_encryption_service_grpc.pb.go @@ -33,9 +33,13 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - RecordingEncryptionService_CreateUpload_FullMethodName = "/teleport.recordingencryption.v1.RecordingEncryptionService/CreateUpload" - RecordingEncryptionService_UploadPart_FullMethodName = "/teleport.recordingencryption.v1.RecordingEncryptionService/UploadPart" - RecordingEncryptionService_CompleteUpload_FullMethodName = "/teleport.recordingencryption.v1.RecordingEncryptionService/CompleteUpload" + RecordingEncryptionService_CreateUpload_FullMethodName = "/teleport.recordingencryption.v1.RecordingEncryptionService/CreateUpload" + RecordingEncryptionService_UploadPart_FullMethodName = "/teleport.recordingencryption.v1.RecordingEncryptionService/UploadPart" + RecordingEncryptionService_CompleteUpload_FullMethodName = "/teleport.recordingencryption.v1.RecordingEncryptionService/CompleteUpload" + RecordingEncryptionService_RotateKey_FullMethodName = "/teleport.recordingencryption.v1.RecordingEncryptionService/RotateKey" + RecordingEncryptionService_GetRotationState_FullMethodName = "/teleport.recordingencryption.v1.RecordingEncryptionService/GetRotationState" + RecordingEncryptionService_CompleteRotation_FullMethodName = "/teleport.recordingencryption.v1.RecordingEncryptionService/CompleteRotation" + RecordingEncryptionService_RollbackRotation_FullMethodName = "/teleport.recordingencryption.v1.RecordingEncryptionService/RollbackRotation" ) // RecordingEncryptionServiceClient is the client API for RecordingEncryptionService service. @@ -51,6 +55,14 @@ type RecordingEncryptionServiceClient interface { UploadPart(ctx context.Context, in *UploadPartRequest, opts ...grpc.CallOption) (*UploadPartResponse, error) // CompleteUploadRequest marks a multipart upload as complete. CompleteUpload(ctx context.Context, in *CompleteUploadRequest, opts ...grpc.CallOption) (*CompleteUploadResponse, error) + // RotateKey rotates the key pair used for encrypting session recording data. + RotateKey(ctx context.Context, in *RotateKeyRequest, opts ...grpc.CallOption) (*RotateKeyResponse, error) + // GetRotationState returns whether or not a rotation is in progress. + GetRotationState(ctx context.Context, in *GetRotationStateRequest, opts ...grpc.CallOption) (*GetRotationStateResponse, error) + // CompleteRotation moves rotated keys out of the active set. + CompleteRotation(ctx context.Context, in *CompleteRotationRequest, opts ...grpc.CallOption) (*CompleteRotationResponse, error) + // RollbackRotation removes active keys and reverts rotating keys back to being active. + RollbackRotation(ctx context.Context, in *RollbackRotationRequest, opts ...grpc.CallOption) (*RollbackRotationResponse, error) } type recordingEncryptionServiceClient struct { @@ -91,6 +103,46 @@ func (c *recordingEncryptionServiceClient) CompleteUpload(ctx context.Context, i return out, nil } +func (c *recordingEncryptionServiceClient) RotateKey(ctx context.Context, in *RotateKeyRequest, opts ...grpc.CallOption) (*RotateKeyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RotateKeyResponse) + err := c.cc.Invoke(ctx, RecordingEncryptionService_RotateKey_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *recordingEncryptionServiceClient) GetRotationState(ctx context.Context, in *GetRotationStateRequest, opts ...grpc.CallOption) (*GetRotationStateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetRotationStateResponse) + err := c.cc.Invoke(ctx, RecordingEncryptionService_GetRotationState_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *recordingEncryptionServiceClient) CompleteRotation(ctx context.Context, in *CompleteRotationRequest, opts ...grpc.CallOption) (*CompleteRotationResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CompleteRotationResponse) + err := c.cc.Invoke(ctx, RecordingEncryptionService_CompleteRotation_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *recordingEncryptionServiceClient) RollbackRotation(ctx context.Context, in *RollbackRotationRequest, opts ...grpc.CallOption) (*RollbackRotationResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RollbackRotationResponse) + err := c.cc.Invoke(ctx, RecordingEncryptionService_RollbackRotation_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // RecordingEncryptionServiceServer is the server API for RecordingEncryptionService service. // All implementations must embed UnimplementedRecordingEncryptionServiceServer // for forward compatibility. @@ -104,6 +156,14 @@ type RecordingEncryptionServiceServer interface { UploadPart(context.Context, *UploadPartRequest) (*UploadPartResponse, error) // CompleteUploadRequest marks a multipart upload as complete. CompleteUpload(context.Context, *CompleteUploadRequest) (*CompleteUploadResponse, error) + // RotateKey rotates the key pair used for encrypting session recording data. + RotateKey(context.Context, *RotateKeyRequest) (*RotateKeyResponse, error) + // GetRotationState returns whether or not a rotation is in progress. + GetRotationState(context.Context, *GetRotationStateRequest) (*GetRotationStateResponse, error) + // CompleteRotation moves rotated keys out of the active set. + CompleteRotation(context.Context, *CompleteRotationRequest) (*CompleteRotationResponse, error) + // RollbackRotation removes active keys and reverts rotating keys back to being active. + RollbackRotation(context.Context, *RollbackRotationRequest) (*RollbackRotationResponse, error) mustEmbedUnimplementedRecordingEncryptionServiceServer() } @@ -123,6 +183,18 @@ func (UnimplementedRecordingEncryptionServiceServer) UploadPart(context.Context, func (UnimplementedRecordingEncryptionServiceServer) CompleteUpload(context.Context, *CompleteUploadRequest) (*CompleteUploadResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CompleteUpload not implemented") } +func (UnimplementedRecordingEncryptionServiceServer) RotateKey(context.Context, *RotateKeyRequest) (*RotateKeyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RotateKey not implemented") +} +func (UnimplementedRecordingEncryptionServiceServer) GetRotationState(context.Context, *GetRotationStateRequest) (*GetRotationStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetRotationState not implemented") +} +func (UnimplementedRecordingEncryptionServiceServer) CompleteRotation(context.Context, *CompleteRotationRequest) (*CompleteRotationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CompleteRotation not implemented") +} +func (UnimplementedRecordingEncryptionServiceServer) RollbackRotation(context.Context, *RollbackRotationRequest) (*RollbackRotationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RollbackRotation not implemented") +} func (UnimplementedRecordingEncryptionServiceServer) mustEmbedUnimplementedRecordingEncryptionServiceServer() { } func (UnimplementedRecordingEncryptionServiceServer) testEmbeddedByValue() {} @@ -199,6 +271,78 @@ func _RecordingEncryptionService_CompleteUpload_Handler(srv interface{}, ctx con return interceptor(ctx, in, info, handler) } +func _RecordingEncryptionService_RotateKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RotateKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RecordingEncryptionServiceServer).RotateKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RecordingEncryptionService_RotateKey_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RecordingEncryptionServiceServer).RotateKey(ctx, req.(*RotateKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _RecordingEncryptionService_GetRotationState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetRotationStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RecordingEncryptionServiceServer).GetRotationState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RecordingEncryptionService_GetRotationState_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RecordingEncryptionServiceServer).GetRotationState(ctx, req.(*GetRotationStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _RecordingEncryptionService_CompleteRotation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CompleteRotationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RecordingEncryptionServiceServer).CompleteRotation(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RecordingEncryptionService_CompleteRotation_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RecordingEncryptionServiceServer).CompleteRotation(ctx, req.(*CompleteRotationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _RecordingEncryptionService_RollbackRotation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RollbackRotationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RecordingEncryptionServiceServer).RollbackRotation(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RecordingEncryptionService_RollbackRotation_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RecordingEncryptionServiceServer).RollbackRotation(ctx, req.(*RollbackRotationRequest)) + } + return interceptor(ctx, in, info, handler) +} + // RecordingEncryptionService_ServiceDesc is the grpc.ServiceDesc for RecordingEncryptionService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -218,6 +362,22 @@ var RecordingEncryptionService_ServiceDesc = grpc.ServiceDesc{ MethodName: "CompleteUpload", Handler: _RecordingEncryptionService_CompleteUpload_Handler, }, + { + MethodName: "RotateKey", + Handler: _RecordingEncryptionService_RotateKey_Handler, + }, + { + MethodName: "GetRotationState", + Handler: _RecordingEncryptionService_GetRotationState_Handler, + }, + { + MethodName: "CompleteRotation", + Handler: _RecordingEncryptionService_CompleteRotation_Handler, + }, + { + MethodName: "RollbackRotation", + Handler: _RecordingEncryptionService_RollbackRotation_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "teleport/recordingencryption/v1/recording_encryption_service.proto", diff --git a/api/proto/teleport/recordingencryption/v1/recording_encryption.proto b/api/proto/teleport/recordingencryption/v1/recording_encryption.proto index 0999f68168e6b..e8addac57d980 100644 --- a/api/proto/teleport/recordingencryption/v1/recording_encryption.proto +++ b/api/proto/teleport/recordingencryption/v1/recording_encryption.proto @@ -21,10 +21,25 @@ import "teleport/legacy/types/types.proto"; option go_package = "github.com/gravitational/teleport/api/gen/proto/go/teleport/recordingencryption/v1;recordingencryptionv1"; +// The possible states a KeyPair can be in. +enum KeyPairState { + // Unspecified value + KEY_PAIR_STATE_UNSPECIFIED = 0; + // Represents an active key. + KEY_PAIR_STATE_ACTIVE = 1; + // Represents a key in the process of being rotated. + KEY_PAIR_STATE_ROTATING = 2; + // Represents a key being rotated in that is inaccessible to at least one + // auth server. + KEY_PAIR_STATE_INACCESSIBLE = 3; +} + // A key pair used with age to wrap and unwrap file keys for session recording encryption. message KeyPair { // A key pair used with age to wrap and unwrap file keys for session recording encryption. types.EncryptionKeyPair key_pair = 1; + // The current state of the key pair. + KeyPairState state = 2; } // RecordingEncryptionSpec contains the active key set for encrypted session recording. @@ -51,3 +66,26 @@ message RecordingEncryption { RecordingEncryptionSpec spec = 5; RecordingEncryptionStatus status = 6; } + +// A rotated key pair previously used with age to wrap and unwrap file keys for session recording +// encryption. +message RotatedKeySpec { + // The rotated key pair previously used with age to wrap and unwrap file keys for session recording + // encryption. + types.EncryptionKeyPair encryption_key_pair = 2; +} + +// The empty status of a RotatedKey. +message RotatedKeyStatus {} + +// A previously rotated encryption key for session recordings kept for future replay. The metadata.name +// is expected to be the fingerprint of the public key contained in the spec, which is a hex encoded +// SHA256 hash of its PKIX form. +message RotatedKey { + string kind = 1; + string sub_kind = 2; + string version = 3; + teleport.header.v1.Metadata metadata = 4; + RotatedKeySpec spec = 5; + RotatedKeyStatus status = 6; +} diff --git a/api/proto/teleport/recordingencryption/v1/recording_encryption_service.proto b/api/proto/teleport/recordingencryption/v1/recording_encryption_service.proto index 32852198f6c57..130eb2eba0144 100644 --- a/api/proto/teleport/recordingencryption/v1/recording_encryption_service.proto +++ b/api/proto/teleport/recordingencryption/v1/recording_encryption_service.proto @@ -17,6 +17,7 @@ syntax = "proto3"; package teleport.recordingencryption.v1; import "google/protobuf/timestamp.proto"; +import "teleport/recordingencryption/v1/recording_encryption.proto"; option go_package = "github.com/gravitational/teleport/api/gen/proto/go/teleport/recordingencryption/v1;recordingencryptionv1"; @@ -29,6 +30,15 @@ service RecordingEncryptionService { rpc UploadPart(UploadPartRequest) returns (UploadPartResponse); // CompleteUploadRequest marks a multipart upload as complete. rpc CompleteUpload(CompleteUploadRequest) returns (CompleteUploadResponse); + + // RotateKey rotates the key pair used for encrypting session recording data. + rpc RotateKey(RotateKeyRequest) returns (RotateKeyResponse); + // GetRotationState returns whether or not a rotation is in progress. + rpc GetRotationState(GetRotationStateRequest) returns (GetRotationStateResponse); + // CompleteRotation moves rotated keys out of the active set. + rpc CompleteRotation(CompleteRotationRequest) returns (CompleteRotationResponse); + // RollbackRotation removes active keys and reverts rotating keys back to being active. + rpc RollbackRotation(RollbackRotationRequest) returns (RollbackRotationResponse); } // The handle to an upload for an encrypted session. @@ -86,5 +96,44 @@ message CompleteUploadRequest { repeated Part parts = 2; } -// CompleteUploadResponse is the empty return value of a CompleteUpload request. +// The body of a CompleteUpload request. message CompleteUploadResponse {} + +// The body of a RotateKey request. +message RotateKeyRequest {} + +// The return value of a RotateKey request. +message RotateKeyResponse {} + +// The body of a GetRotationState request. +message GetRotationStateRequest { + int32 page_size = 1; + string page_token = 2; +} + +// A public key fingerprint coupled with its current state. +message FingerprintWithState { + // A fingerprint identifying the public key of a KeyPair. + string fingerprint = 1; + // The state associated with the identified KeyPair. + v1.KeyPairState state = 2; +} + +// The current state of all active encryption key pairs. +message GetRotationStateResponse { + string next_page_token = 1; + // The state of all active encryption key pairs. + repeated FingerprintWithState key_pair_states = 2; +} + +// The body of a CompleteRotation request. +message CompleteRotationRequest {} + +// The return value of a CompleteRotation request. +message CompleteRotationResponse {} + +// The body of a RollbackRotation request. +message RollbackRotationRequest {} + +// The return value of a RollbackRotation request +message RollbackRotationResponse {} diff --git a/api/types/constants.go b/api/types/constants.go index 45da69dfd945b..5b4cbb0ca65f4 100644 --- a/api/types/constants.go +++ b/api/types/constants.go @@ -316,6 +316,9 @@ const ( // KindRecordingEncryption is the collection of active session recording encryption keys. KindRecordingEncryption = "recording_encryption" + // KindRotatedKey is a previously rotated session recording encryption key kept for future replay. + KindRotatedKey = "rotated_key" + // MetaNameSessionRecordingConfig is the exact name of the singleton resource for // session recording configuration. MetaNameSessionRecordingConfig = "session-recording-config" diff --git a/lib/auth/recordingencryption/manager.go b/lib/auth/recordingencryption/manager.go index f05e998ed118e..e8be139896c3a 100644 --- a/lib/auth/recordingencryption/manager.go +++ b/lib/auth/recordingencryption/manager.go @@ -21,7 +21,7 @@ import ( "crypto" "crypto/sha256" "crypto/x509" - "encoding/base64" + "encoding/hex" "iter" "log/slog" "slices" @@ -531,7 +531,7 @@ func Fingerprint(pubKey crypto.PublicKey) (string, error) { } fp := sha256.Sum256(derPub) - return base64.StdEncoding.EncodeToString(fp[:]), nil + return hex.EncodeToString(fp[:]), nil } // fingerprints a public RSA key encoded as PEM-wrapped PKIX. diff --git a/lib/services/local/events.go b/lib/services/local/events.go index 02a7008323e25..6e301a8e5ec60 100644 --- a/lib/services/local/events.go +++ b/lib/services/local/events.go @@ -266,6 +266,8 @@ func (e *EventsService) NewWatcher(ctx context.Context, watch types.Watch) (type parser = newHealthCheckConfigParser() case types.KindRecordingEncryption: parser = newRecordingEncryptionParser() + case types.KindRotatedKey: + parser = newRotatedKeyParser() case types.KindRelayServer: parser = newRelayServerParser() default: diff --git a/lib/services/local/recording_encryption.go b/lib/services/local/recording_encryption.go index f69af026c3473..a0ff6434f247c 100644 --- a/lib/services/local/recording_encryption.go +++ b/lib/services/local/recording_encryption.go @@ -24,6 +24,8 @@ import ( headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" recordingencryptionv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/recordingencryption/v1" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/lib/auth/recordingencryption" "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/services/local/generic" @@ -31,12 +33,14 @@ import ( const ( recordingEncryptionPrefix = "recording_encryption" + rotatedKeyPrefix = "recording_encryption_rotated" ) // RecordingEncryptionService exposes backend functionality for working with the // cluster's RecordingEncryption resource. type RecordingEncryptionService struct { encryption *generic.ServiceWrapper[*recordingencryptionv1.RecordingEncryption] + rotatedKey *generic.ServiceWrapper[*recordingencryptionv1.RotatedKey] } var _ services.RecordingEncryption = (*RecordingEncryptionService)(nil) @@ -56,8 +60,21 @@ func NewRecordingEncryptionService(b backend.Backend) (*RecordingEncryptionServi return nil, trace.Wrap(err) } + rotatedKey, err := generic.NewServiceWrapper(generic.ServiceConfig[*recordingencryptionv1.RotatedKey]{ + Backend: b, + PageLimit: pageLimit, + ResourceKind: types.KindRotatedKey, + BackendPrefix: backend.NewKey(rotatedKeyPrefix), + MarshalFunc: services.MarshalProtoResource[*recordingencryptionv1.RotatedKey], + UnmarshalFunc: services.UnmarshalProtoResource[*recordingencryptionv1.RotatedKey], + }) + if err != nil { + return nil, trace.Wrap(err) + } + return &RecordingEncryptionService{ encryption: encryption, + rotatedKey: rotatedKey, }, nil } @@ -94,6 +111,40 @@ func (s *RecordingEncryptionService) GetRecordingEncryption(ctx context.Context) return encryption, trace.Wrap(err) } +// CreateRotatedKey creates a new RotatedKey in the backend keyed on the fingerprint of the given public key. +func (s *RecordingEncryptionService) CreateRotatedKey(ctx context.Context, key *types.EncryptionKeyPair) (*recordingencryptionv1.RotatedKey, error) { + parsed, err := keys.ParsePublicKey(key.PublicKey) + if err != nil { + return nil, trace.Wrap(err) + } + + fp, err := recordingencryption.Fingerprint(parsed) + if err != nil { + return nil, trace.Wrap(err) + } + created, err := s.rotatedKey.CreateResource(ctx, &recordingencryptionv1.RotatedKey{ + Metadata: &headerv1.Metadata{ + Name: fp, + }, + Kind: types.KindRotatedKey, + Spec: &recordingencryptionv1.RotatedKeySpec{ + EncryptionKeyPair: key, + }, + }) + return created, trace.Wrap(err) +} + +// GetRotatedKey retrieves the RotatedKey related to the given public key fingerprint from the backend. +func (s *RecordingEncryptionService) GetRotatedKey(ctx context.Context, fingerprint string) (*recordingencryptionv1.RotatedKey, error) { + rotatedKey, err := s.rotatedKey.GetResource(ctx, fingerprint) + return rotatedKey, trace.Wrap(err) +} + +// DeleteRotatedKey removes the RotatedKey related with the given public key fingerprint from the backend. +func (s *RecordingEncryptionService) DeleteRotatedKey(ctx context.Context, fingerprint string) error { + return trace.Wrap(s.rotatedKey.DeleteResource(ctx, fingerprint)) +} + type recordingEncryptionParser struct { baseParser } @@ -107,15 +158,15 @@ func newRecordingEncryptionParser() *recordingEncryptionParser { func (p *recordingEncryptionParser) parse(event backend.Event) (types.Resource, error) { switch event.Type { case types.OpPut: - resource, err := services.UnmarshalProtoResource[*recordingencryptionv1.RecordingEncryption]( + recordingEncryption, err := services.UnmarshalProtoResource[*recordingencryptionv1.RecordingEncryption]( event.Item.Value, services.WithExpires(event.Item.Expires), services.WithRevision(event.Item.Revision), ) if err != nil { - return nil, trace.Wrap(err, "unmarshalling resource from event") + return nil, trace.Wrap(err, "unmarshaling resource from event") } - return types.Resource153ToLegacy(resource), nil + return types.Resource153ToLegacy(recordingEncryption), nil case types.OpDelete: return &types.ResourceHeader{ Kind: types.KindRecordingEncryption, @@ -128,3 +179,41 @@ func (p *recordingEncryptionParser) parse(event backend.Event) (types.Resource, return nil, trace.BadParameter("event %v is not supported", event.Type) } } + +type rotatedKeyParser struct { + baseParser +} + +func newRotatedKeyParser() *rotatedKeyParser { + return &rotatedKeyParser{ + baseParser: newBaseParser(backend.NewKey(rotatedKeyPrefix)), + } +} + +func (p *rotatedKeyParser) parse(event backend.Event) (types.Resource, error) { + switch event.Type { + case types.OpPut: + rotatedKey, err := services.UnmarshalProtoResource[*recordingencryptionv1.RotatedKey]( + event.Item.Value, + services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), + ) + if err != nil { + return nil, trace.Wrap(err, "unmarshaling resource from event") + } + + return types.Resource153ToLegacy(rotatedKey), nil + case types.OpDelete: + header, err := services.UnmarshalProtoResource[*headerv1.ResourceHeader]( + event.Item.Value, + services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), + ) + if err != nil { + return nil, trace.Wrap(err, "unmarshaling deleted resource header") + } + return types.Resource153ToLegacy(header), nil + default: + return nil, trace.BadParameter("event %v is not supported", event.Type) + } +} diff --git a/lib/services/local/recording_encryption_test.go b/lib/services/local/recording_encryption_test.go index c8ce0fc25466b..038d81dd51102 100644 --- a/lib/services/local/recording_encryption_test.go +++ b/lib/services/local/recording_encryption_test.go @@ -24,6 +24,8 @@ import ( pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/recordingencryption/v1" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/lib/auth/recordingencryption" "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/backend/memory" ) @@ -80,3 +82,100 @@ func TestRecordingEncryption(t *testing.T) { _, err = service.GetRecordingEncryption(ctx) require.Error(t, err) } + +func TestRotatedKeys(t *testing.T) { + bk, err := memory.New(memory.Config{}) + require.NoError(t, err) + service, err := NewRecordingEncryptionService(backend.NewSanitizer(bk)) + require.NoError(t, err) + + ctx := context.Background() + + privateKey, err := keys.ParsePrivateKey(testRSA4096PrivateKeyPEM) + require.NoError(t, err) + publicKey := privateKey.Public() + + fingerprint, err := recordingencryption.Fingerprint(publicKey) + require.NoError(t, err) + + publicKeyPEM, err := keys.MarshalPublicKey(publicKey) + require.NoError(t, err) + + // get should fail when there's no rotated key + _, err = service.GetRotatedKey(ctx, fingerprint) + require.Error(t, err) + + created, err := service.CreateRotatedKey(ctx, &types.EncryptionKeyPair{ + PrivateKey: testRSA4096PrivateKeyPEM, + PublicKey: publicKeyPEM, + PrivateKeyType: types.PrivateKeyType_RAW, + }) + require.NoError(t, err) + require.Equal(t, testRSA4096PrivateKeyPEM, created.Spec.EncryptionKeyPair.PrivateKey) + require.Equal(t, publicKeyPEM, created.Spec.EncryptionKeyPair.PublicKey) + require.Equal(t, types.PrivateKeyType_RAW, created.Spec.EncryptionKeyPair.PrivateKeyType) + + rotatedKey, err := service.GetRotatedKey(ctx, fingerprint) + require.NoError(t, err) + + require.Equal(t, testRSA4096PrivateKeyPEM, rotatedKey.Spec.EncryptionKeyPair.PrivateKey) + require.Equal(t, publicKeyPEM, rotatedKey.Spec.EncryptionKeyPair.PublicKey) + require.Equal(t, types.PrivateKeyType_RAW, rotatedKey.Spec.EncryptionKeyPair.PrivateKeyType) + + err = service.DeleteRotatedKey(ctx, fingerprint) + require.NoError(t, err) + _, err = service.GetRotatedKey(ctx, fingerprint) + require.Error(t, err) +} + +var testRSA4096PrivateKeyPEM = []byte(`-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY-----`) diff --git a/lib/services/recording_encryption.go b/lib/services/recording_encryption.go index 49216df69749a..5ddeb0b7bb442 100644 --- a/lib/services/recording_encryption.go +++ b/lib/services/recording_encryption.go @@ -20,6 +20,7 @@ import ( "context" recordingencryptionv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/recordingencryption/v1" + "github.com/gravitational/teleport/api/types" ) // RecordingEncryption handles CRUD operations for the RecordingEncryption resource. @@ -33,4 +34,11 @@ type RecordingEncryption interface { DeleteRecordingEncryption(ctx context.Context) error // GetRecordingEncryption retrieves the RecordingEncryption for the cluster. GetRecordingEncryption(ctx context.Context) (*recordingencryptionv1.RecordingEncryption, error) + + // CreateRotatedKey creates a new RotatedKey in the backend. + CreateRotatedKey(ctx context.Context, key *types.EncryptionKeyPair) (*recordingencryptionv1.RotatedKey, error) + // GetRotatedKey retrieves the RotatedKey related to the given fingerprint. + GetRotatedKey(ctx context.Context, fingerprint string) (*recordingencryptionv1.RotatedKey, error) + // DeleteRotatedKey retrieves the RotatedKey related to the given fingerprint. + DeleteRotatedKey(ctx context.Context, fingerprint string) error }