From 6a03e94c53599875793bbc1835886cac5e08ed7d Mon Sep 17 00:00:00 2001 From: Maxim Dietz Date: Fri, 21 Nov 2025 19:35:38 -0500 Subject: [PATCH 1/2] types: Add `ListAuthServers`/`ListProxyServers` to PresenceService - Add `ListAuthServers` and `ListProxyServers` RPCs to PresenceService --- .../go/teleport/presence/v1/service.pb.go | 334 +++++++++++++++--- .../teleport/presence/v1/service_grpc.pb.go | 80 +++++ api/proto/teleport/presence/v1/service.proto | 41 +++ 3 files changed, 414 insertions(+), 41 deletions(-) diff --git a/api/gen/proto/go/teleport/presence/v1/service.pb.go b/api/gen/proto/go/teleport/presence/v1/service.pb.go index 72cd62e46b615..2cef7822e3061 100644 --- a/api/gen/proto/go/teleport/presence/v1/service.pb.go +++ b/api/gen/proto/go/teleport/presence/v1/service.pb.go @@ -788,6 +788,230 @@ func (*DeleteRelayServerResponse) Descriptor() ([]byte, []int) { return file_teleport_presence_v1_service_proto_rawDescGZIP(), []int{14} } +// Request message for the PresenceService.ListAuthServers rpc. +type ListAuthServersRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The maximum number of items to return. + // The server may impose a different page size at its discretion. + PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + // The next_page_token value returned from a previous List request, if any. + PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListAuthServersRequest) Reset() { + *x = ListAuthServersRequest{} + mi := &file_teleport_presence_v1_service_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAuthServersRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAuthServersRequest) ProtoMessage() {} + +func (x *ListAuthServersRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_presence_v1_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 ListAuthServersRequest.ProtoReflect.Descriptor instead. +func (*ListAuthServersRequest) Descriptor() ([]byte, []int) { + return file_teleport_presence_v1_service_proto_rawDescGZIP(), []int{15} +} + +func (x *ListAuthServersRequest) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +func (x *ListAuthServersRequest) GetPageToken() string { + if x != nil { + return x.PageToken + } + return "" +} + +// Response message for the PresenceService.ListAuthServers rpc. +type ListAuthServersResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // A list of auth server resources. + Servers []*types.ServerV2 `protobuf:"bytes,1,rep,name=servers,proto3" json:"servers,omitempty"` + // Token to retrieve the next page of results, or empty if there are no + // more results in the list. + NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListAuthServersResponse) Reset() { + *x = ListAuthServersResponse{} + mi := &file_teleport_presence_v1_service_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAuthServersResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAuthServersResponse) ProtoMessage() {} + +func (x *ListAuthServersResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_presence_v1_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 ListAuthServersResponse.ProtoReflect.Descriptor instead. +func (*ListAuthServersResponse) Descriptor() ([]byte, []int) { + return file_teleport_presence_v1_service_proto_rawDescGZIP(), []int{16} +} + +func (x *ListAuthServersResponse) GetServers() []*types.ServerV2 { + if x != nil { + return x.Servers + } + return nil +} + +func (x *ListAuthServersResponse) GetNextPageToken() string { + if x != nil { + return x.NextPageToken + } + return "" +} + +// Request message for the PresenceService.ListProxyServers rpc. +type ListProxyServersRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The maximum number of items to return. + // The server may impose a different page size at its discretion. + PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + // The next_page_token value returned from a previous List request, if any. + PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListProxyServersRequest) Reset() { + *x = ListProxyServersRequest{} + mi := &file_teleport_presence_v1_service_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListProxyServersRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListProxyServersRequest) ProtoMessage() {} + +func (x *ListProxyServersRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_presence_v1_service_proto_msgTypes[17] + 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 ListProxyServersRequest.ProtoReflect.Descriptor instead. +func (*ListProxyServersRequest) Descriptor() ([]byte, []int) { + return file_teleport_presence_v1_service_proto_rawDescGZIP(), []int{17} +} + +func (x *ListProxyServersRequest) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +func (x *ListProxyServersRequest) GetPageToken() string { + if x != nil { + return x.PageToken + } + return "" +} + +// Response message for the PresenceService.ListProxyServers rpc. +type ListProxyServersResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // A list of proxy server resources. + Servers []*types.ServerV2 `protobuf:"bytes,1,rep,name=servers,proto3" json:"servers,omitempty"` + // Token to retrieve the next page of results, or empty if there are no + // more results in the list. + NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListProxyServersResponse) Reset() { + *x = ListProxyServersResponse{} + mi := &file_teleport_presence_v1_service_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListProxyServersResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListProxyServersResponse) ProtoMessage() {} + +func (x *ListProxyServersResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_presence_v1_service_proto_msgTypes[18] + 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 ListProxyServersResponse.ProtoReflect.Descriptor instead. +func (*ListProxyServersResponse) Descriptor() ([]byte, []int) { + return file_teleport_presence_v1_service_proto_rawDescGZIP(), []int{18} +} + +func (x *ListProxyServersResponse) GetServers() []*types.ServerV2 { + if x != nil { + return x.Servers + } + return nil +} + +func (x *ListProxyServersResponse) GetNextPageToken() string { + if x != nil { + return x.NextPageToken + } + return "" +} + var File_teleport_presence_v1_service_proto protoreflect.FileDescriptor const file_teleport_presence_v1_service_proto_rawDesc = "" + @@ -832,7 +1056,22 @@ const file_teleport_presence_v1_service_proto_rawDesc = "" + "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\".\n" + "\x18DeleteRelayServerRequest\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\"\x1b\n" + - "\x19DeleteRelayServerResponse2\xb8\b\n" + + "\x19DeleteRelayServerResponse\"T\n" + + "\x16ListAuthServersRequest\x12\x1b\n" + + "\tpage_size\x18\x01 \x01(\x05R\bpageSize\x12\x1d\n" + + "\n" + + "page_token\x18\x02 \x01(\tR\tpageToken\"l\n" + + "\x17ListAuthServersResponse\x12)\n" + + "\aservers\x18\x01 \x03(\v2\x0f.types.ServerV2R\aservers\x12&\n" + + "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"U\n" + + "\x17ListProxyServersRequest\x12\x1b\n" + + "\tpage_size\x18\x01 \x01(\x05R\bpageSize\x12\x1d\n" + + "\n" + + "page_token\x18\x02 \x01(\tR\tpageToken\"m\n" + + "\x18ListProxyServersResponse\x12)\n" + + "\aservers\x18\x01 \x03(\v2\x0f.types.ServerV2R\aservers\x12&\n" + + "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken2\x9b\n" + + "\n" + "\x0fPresenceService\x12Y\n" + "\x10GetRemoteCluster\x12-.teleport.presence.v1.GetRemoteClusterRequest\x1a\x16.types.RemoteClusterV3\x12w\n" + "\x12ListRemoteClusters\x12/.teleport.presence.v1.ListRemoteClustersRequest\x1a0.teleport.presence.v1.ListRemoteClustersResponse\x12_\n" + @@ -843,7 +1082,9 @@ const file_teleport_presence_v1_service_proto_rawDesc = "" + "\x13DeleteReverseTunnel\x120.teleport.presence.v1.DeleteReverseTunnelRequest\x1a\x16.google.protobuf.Empty\x12k\n" + "\x0eGetRelayServer\x12+.teleport.presence.v1.GetRelayServerRequest\x1a,.teleport.presence.v1.GetRelayServerResponse\x12q\n" + "\x10ListRelayServers\x12-.teleport.presence.v1.ListRelayServersRequest\x1a..teleport.presence.v1.ListRelayServersResponse\x12t\n" + - "\x11DeleteRelayServer\x12..teleport.presence.v1.DeleteRelayServerRequest\x1a/.teleport.presence.v1.DeleteRelayServerResponseBTZRgithub.com/gravitational/teleport/api/gen/proto/go/teleport/presence/v1;presencev1b\x06proto3" + "\x11DeleteRelayServer\x12..teleport.presence.v1.DeleteRelayServerRequest\x1a/.teleport.presence.v1.DeleteRelayServerResponse\x12n\n" + + "\x0fListAuthServers\x12,.teleport.presence.v1.ListAuthServersRequest\x1a-.teleport.presence.v1.ListAuthServersResponse\x12q\n" + + "\x10ListProxyServers\x12-.teleport.presence.v1.ListProxyServersRequest\x1a..teleport.presence.v1.ListProxyServersResponseBTZRgithub.com/gravitational/teleport/api/gen/proto/go/teleport/presence/v1;presencev1b\x06proto3" var ( file_teleport_presence_v1_service_proto_rawDescOnce sync.Once @@ -857,7 +1098,7 @@ func file_teleport_presence_v1_service_proto_rawDescGZIP() []byte { return file_teleport_presence_v1_service_proto_rawDescData } -var file_teleport_presence_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_teleport_presence_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 19) var file_teleport_presence_v1_service_proto_goTypes = []any{ (*GetRemoteClusterRequest)(nil), // 0: teleport.presence.v1.GetRemoteClusterRequest (*ListRemoteClustersRequest)(nil), // 1: teleport.presence.v1.ListRemoteClustersRequest @@ -874,45 +1115,56 @@ var file_teleport_presence_v1_service_proto_goTypes = []any{ (*ListRelayServersResponse)(nil), // 12: teleport.presence.v1.ListRelayServersResponse (*DeleteRelayServerRequest)(nil), // 13: teleport.presence.v1.DeleteRelayServerRequest (*DeleteRelayServerResponse)(nil), // 14: teleport.presence.v1.DeleteRelayServerResponse - (*types.RemoteClusterV3)(nil), // 15: types.RemoteClusterV3 - (*fieldmaskpb.FieldMask)(nil), // 16: google.protobuf.FieldMask - (*types.ReverseTunnelV2)(nil), // 17: types.ReverseTunnelV2 - (*RelayServer)(nil), // 18: teleport.presence.v1.RelayServer - (*emptypb.Empty)(nil), // 19: google.protobuf.Empty + (*ListAuthServersRequest)(nil), // 15: teleport.presence.v1.ListAuthServersRequest + (*ListAuthServersResponse)(nil), // 16: teleport.presence.v1.ListAuthServersResponse + (*ListProxyServersRequest)(nil), // 17: teleport.presence.v1.ListProxyServersRequest + (*ListProxyServersResponse)(nil), // 18: teleport.presence.v1.ListProxyServersResponse + (*types.RemoteClusterV3)(nil), // 19: types.RemoteClusterV3 + (*fieldmaskpb.FieldMask)(nil), // 20: google.protobuf.FieldMask + (*types.ReverseTunnelV2)(nil), // 21: types.ReverseTunnelV2 + (*RelayServer)(nil), // 22: teleport.presence.v1.RelayServer + (*types.ServerV2)(nil), // 23: types.ServerV2 + (*emptypb.Empty)(nil), // 24: google.protobuf.Empty } var file_teleport_presence_v1_service_proto_depIdxs = []int32{ - 15, // 0: teleport.presence.v1.ListRemoteClustersResponse.remote_clusters:type_name -> types.RemoteClusterV3 - 15, // 1: teleport.presence.v1.UpdateRemoteClusterRequest.remote_cluster:type_name -> types.RemoteClusterV3 - 16, // 2: teleport.presence.v1.UpdateRemoteClusterRequest.update_mask:type_name -> google.protobuf.FieldMask - 17, // 3: teleport.presence.v1.ListReverseTunnelsResponse.reverse_tunnels:type_name -> types.ReverseTunnelV2 - 17, // 4: teleport.presence.v1.UpsertReverseTunnelRequest.reverse_tunnel:type_name -> types.ReverseTunnelV2 - 18, // 5: teleport.presence.v1.GetRelayServerResponse.relay_server:type_name -> teleport.presence.v1.RelayServer - 18, // 6: teleport.presence.v1.ListRelayServersResponse.relays:type_name -> teleport.presence.v1.RelayServer - 0, // 7: teleport.presence.v1.PresenceService.GetRemoteCluster:input_type -> teleport.presence.v1.GetRemoteClusterRequest - 1, // 8: teleport.presence.v1.PresenceService.ListRemoteClusters:input_type -> teleport.presence.v1.ListRemoteClustersRequest - 3, // 9: teleport.presence.v1.PresenceService.UpdateRemoteCluster:input_type -> teleport.presence.v1.UpdateRemoteClusterRequest - 4, // 10: teleport.presence.v1.PresenceService.DeleteRemoteCluster:input_type -> teleport.presence.v1.DeleteRemoteClusterRequest - 5, // 11: teleport.presence.v1.PresenceService.ListReverseTunnels:input_type -> teleport.presence.v1.ListReverseTunnelsRequest - 7, // 12: teleport.presence.v1.PresenceService.UpsertReverseTunnel:input_type -> teleport.presence.v1.UpsertReverseTunnelRequest - 8, // 13: teleport.presence.v1.PresenceService.DeleteReverseTunnel:input_type -> teleport.presence.v1.DeleteReverseTunnelRequest - 9, // 14: teleport.presence.v1.PresenceService.GetRelayServer:input_type -> teleport.presence.v1.GetRelayServerRequest - 11, // 15: teleport.presence.v1.PresenceService.ListRelayServers:input_type -> teleport.presence.v1.ListRelayServersRequest - 13, // 16: teleport.presence.v1.PresenceService.DeleteRelayServer:input_type -> teleport.presence.v1.DeleteRelayServerRequest - 15, // 17: teleport.presence.v1.PresenceService.GetRemoteCluster:output_type -> types.RemoteClusterV3 - 2, // 18: teleport.presence.v1.PresenceService.ListRemoteClusters:output_type -> teleport.presence.v1.ListRemoteClustersResponse - 15, // 19: teleport.presence.v1.PresenceService.UpdateRemoteCluster:output_type -> types.RemoteClusterV3 - 19, // 20: teleport.presence.v1.PresenceService.DeleteRemoteCluster:output_type -> google.protobuf.Empty - 6, // 21: teleport.presence.v1.PresenceService.ListReverseTunnels:output_type -> teleport.presence.v1.ListReverseTunnelsResponse - 17, // 22: teleport.presence.v1.PresenceService.UpsertReverseTunnel:output_type -> types.ReverseTunnelV2 - 19, // 23: teleport.presence.v1.PresenceService.DeleteReverseTunnel:output_type -> google.protobuf.Empty - 10, // 24: teleport.presence.v1.PresenceService.GetRelayServer:output_type -> teleport.presence.v1.GetRelayServerResponse - 12, // 25: teleport.presence.v1.PresenceService.ListRelayServers:output_type -> teleport.presence.v1.ListRelayServersResponse - 14, // 26: teleport.presence.v1.PresenceService.DeleteRelayServer:output_type -> teleport.presence.v1.DeleteRelayServerResponse - 17, // [17:27] is the sub-list for method output_type - 7, // [7:17] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 19, // 0: teleport.presence.v1.ListRemoteClustersResponse.remote_clusters:type_name -> types.RemoteClusterV3 + 19, // 1: teleport.presence.v1.UpdateRemoteClusterRequest.remote_cluster:type_name -> types.RemoteClusterV3 + 20, // 2: teleport.presence.v1.UpdateRemoteClusterRequest.update_mask:type_name -> google.protobuf.FieldMask + 21, // 3: teleport.presence.v1.ListReverseTunnelsResponse.reverse_tunnels:type_name -> types.ReverseTunnelV2 + 21, // 4: teleport.presence.v1.UpsertReverseTunnelRequest.reverse_tunnel:type_name -> types.ReverseTunnelV2 + 22, // 5: teleport.presence.v1.GetRelayServerResponse.relay_server:type_name -> teleport.presence.v1.RelayServer + 22, // 6: teleport.presence.v1.ListRelayServersResponse.relays:type_name -> teleport.presence.v1.RelayServer + 23, // 7: teleport.presence.v1.ListAuthServersResponse.servers:type_name -> types.ServerV2 + 23, // 8: teleport.presence.v1.ListProxyServersResponse.servers:type_name -> types.ServerV2 + 0, // 9: teleport.presence.v1.PresenceService.GetRemoteCluster:input_type -> teleport.presence.v1.GetRemoteClusterRequest + 1, // 10: teleport.presence.v1.PresenceService.ListRemoteClusters:input_type -> teleport.presence.v1.ListRemoteClustersRequest + 3, // 11: teleport.presence.v1.PresenceService.UpdateRemoteCluster:input_type -> teleport.presence.v1.UpdateRemoteClusterRequest + 4, // 12: teleport.presence.v1.PresenceService.DeleteRemoteCluster:input_type -> teleport.presence.v1.DeleteRemoteClusterRequest + 5, // 13: teleport.presence.v1.PresenceService.ListReverseTunnels:input_type -> teleport.presence.v1.ListReverseTunnelsRequest + 7, // 14: teleport.presence.v1.PresenceService.UpsertReverseTunnel:input_type -> teleport.presence.v1.UpsertReverseTunnelRequest + 8, // 15: teleport.presence.v1.PresenceService.DeleteReverseTunnel:input_type -> teleport.presence.v1.DeleteReverseTunnelRequest + 9, // 16: teleport.presence.v1.PresenceService.GetRelayServer:input_type -> teleport.presence.v1.GetRelayServerRequest + 11, // 17: teleport.presence.v1.PresenceService.ListRelayServers:input_type -> teleport.presence.v1.ListRelayServersRequest + 13, // 18: teleport.presence.v1.PresenceService.DeleteRelayServer:input_type -> teleport.presence.v1.DeleteRelayServerRequest + 15, // 19: teleport.presence.v1.PresenceService.ListAuthServers:input_type -> teleport.presence.v1.ListAuthServersRequest + 17, // 20: teleport.presence.v1.PresenceService.ListProxyServers:input_type -> teleport.presence.v1.ListProxyServersRequest + 19, // 21: teleport.presence.v1.PresenceService.GetRemoteCluster:output_type -> types.RemoteClusterV3 + 2, // 22: teleport.presence.v1.PresenceService.ListRemoteClusters:output_type -> teleport.presence.v1.ListRemoteClustersResponse + 19, // 23: teleport.presence.v1.PresenceService.UpdateRemoteCluster:output_type -> types.RemoteClusterV3 + 24, // 24: teleport.presence.v1.PresenceService.DeleteRemoteCluster:output_type -> google.protobuf.Empty + 6, // 25: teleport.presence.v1.PresenceService.ListReverseTunnels:output_type -> teleport.presence.v1.ListReverseTunnelsResponse + 21, // 26: teleport.presence.v1.PresenceService.UpsertReverseTunnel:output_type -> types.ReverseTunnelV2 + 24, // 27: teleport.presence.v1.PresenceService.DeleteReverseTunnel:output_type -> google.protobuf.Empty + 10, // 28: teleport.presence.v1.PresenceService.GetRelayServer:output_type -> teleport.presence.v1.GetRelayServerResponse + 12, // 29: teleport.presence.v1.PresenceService.ListRelayServers:output_type -> teleport.presence.v1.ListRelayServersResponse + 14, // 30: teleport.presence.v1.PresenceService.DeleteRelayServer:output_type -> teleport.presence.v1.DeleteRelayServerResponse + 16, // 31: teleport.presence.v1.PresenceService.ListAuthServers:output_type -> teleport.presence.v1.ListAuthServersResponse + 18, // 32: teleport.presence.v1.PresenceService.ListProxyServers:output_type -> teleport.presence.v1.ListProxyServersResponse + 21, // [21:33] is the sub-list for method output_type + 9, // [9:21] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_teleport_presence_v1_service_proto_init() } @@ -927,7 +1179,7 @@ func file_teleport_presence_v1_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_teleport_presence_v1_service_proto_rawDesc), len(file_teleport_presence_v1_service_proto_rawDesc)), NumEnums: 0, - NumMessages: 15, + NumMessages: 19, NumExtensions: 0, NumServices: 1, }, diff --git a/api/gen/proto/go/teleport/presence/v1/service_grpc.pb.go b/api/gen/proto/go/teleport/presence/v1/service_grpc.pb.go index 1802ba6da9b42..fa595702a3f14 100644 --- a/api/gen/proto/go/teleport/presence/v1/service_grpc.pb.go +++ b/api/gen/proto/go/teleport/presence/v1/service_grpc.pb.go @@ -45,6 +45,8 @@ const ( PresenceService_GetRelayServer_FullMethodName = "/teleport.presence.v1.PresenceService/GetRelayServer" PresenceService_ListRelayServers_FullMethodName = "/teleport.presence.v1.PresenceService/ListRelayServers" PresenceService_DeleteRelayServer_FullMethodName = "/teleport.presence.v1.PresenceService/DeleteRelayServer" + PresenceService_ListAuthServers_FullMethodName = "/teleport.presence.v1.PresenceService/ListAuthServers" + PresenceService_ListProxyServers_FullMethodName = "/teleport.presence.v1.PresenceService/ListProxyServers" ) // PresenceServiceClient is the client API for PresenceService service. @@ -73,6 +75,10 @@ type PresenceServiceClient interface { ListRelayServers(ctx context.Context, in *ListRelayServersRequest, opts ...grpc.CallOption) (*ListRelayServersResponse, error) // DeleteRelayServer deletes a relay_server resource by name. DeleteRelayServer(ctx context.Context, in *DeleteRelayServerRequest, opts ...grpc.CallOption) (*DeleteRelayServerResponse, error) + // ListAuthServers returns a page of Auth servers. + ListAuthServers(ctx context.Context, in *ListAuthServersRequest, opts ...grpc.CallOption) (*ListAuthServersResponse, error) + // ListProxyServers returns a page of Proxy servers. + ListProxyServers(ctx context.Context, in *ListProxyServersRequest, opts ...grpc.CallOption) (*ListProxyServersResponse, error) } type presenceServiceClient struct { @@ -183,6 +189,26 @@ func (c *presenceServiceClient) DeleteRelayServer(ctx context.Context, in *Delet return out, nil } +func (c *presenceServiceClient) ListAuthServers(ctx context.Context, in *ListAuthServersRequest, opts ...grpc.CallOption) (*ListAuthServersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListAuthServersResponse) + err := c.cc.Invoke(ctx, PresenceService_ListAuthServers_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *presenceServiceClient) ListProxyServers(ctx context.Context, in *ListProxyServersRequest, opts ...grpc.CallOption) (*ListProxyServersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListProxyServersResponse) + err := c.cc.Invoke(ctx, PresenceService_ListProxyServers_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // PresenceServiceServer is the server API for PresenceService service. // All implementations must embed UnimplementedPresenceServiceServer // for forward compatibility. @@ -209,6 +235,10 @@ type PresenceServiceServer interface { ListRelayServers(context.Context, *ListRelayServersRequest) (*ListRelayServersResponse, error) // DeleteRelayServer deletes a relay_server resource by name. DeleteRelayServer(context.Context, *DeleteRelayServerRequest) (*DeleteRelayServerResponse, error) + // ListAuthServers returns a page of Auth servers. + ListAuthServers(context.Context, *ListAuthServersRequest) (*ListAuthServersResponse, error) + // ListProxyServers returns a page of Proxy servers. + ListProxyServers(context.Context, *ListProxyServersRequest) (*ListProxyServersResponse, error) mustEmbedUnimplementedPresenceServiceServer() } @@ -249,6 +279,12 @@ func (UnimplementedPresenceServiceServer) ListRelayServers(context.Context, *Lis func (UnimplementedPresenceServiceServer) DeleteRelayServer(context.Context, *DeleteRelayServerRequest) (*DeleteRelayServerResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteRelayServer not implemented") } +func (UnimplementedPresenceServiceServer) ListAuthServers(context.Context, *ListAuthServersRequest) (*ListAuthServersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListAuthServers not implemented") +} +func (UnimplementedPresenceServiceServer) ListProxyServers(context.Context, *ListProxyServersRequest) (*ListProxyServersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListProxyServers not implemented") +} func (UnimplementedPresenceServiceServer) mustEmbedUnimplementedPresenceServiceServer() {} func (UnimplementedPresenceServiceServer) testEmbeddedByValue() {} @@ -450,6 +486,42 @@ func _PresenceService_DeleteRelayServer_Handler(srv interface{}, ctx context.Con return interceptor(ctx, in, info, handler) } +func _PresenceService_ListAuthServers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListAuthServersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PresenceServiceServer).ListAuthServers(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: PresenceService_ListAuthServers_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PresenceServiceServer).ListAuthServers(ctx, req.(*ListAuthServersRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _PresenceService_ListProxyServers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListProxyServersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PresenceServiceServer).ListProxyServers(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: PresenceService_ListProxyServers_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PresenceServiceServer).ListProxyServers(ctx, req.(*ListProxyServersRequest)) + } + return interceptor(ctx, in, info, handler) +} + // PresenceService_ServiceDesc is the grpc.ServiceDesc for PresenceService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -497,6 +569,14 @@ var PresenceService_ServiceDesc = grpc.ServiceDesc{ MethodName: "DeleteRelayServer", Handler: _PresenceService_DeleteRelayServer_Handler, }, + { + MethodName: "ListAuthServers", + Handler: _PresenceService_ListAuthServers_Handler, + }, + { + MethodName: "ListProxyServers", + Handler: _PresenceService_ListProxyServers_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "teleport/presence/v1/service.proto", diff --git a/api/proto/teleport/presence/v1/service.proto b/api/proto/teleport/presence/v1/service.proto index fdb101145beb6..68e07a2fb49b6 100644 --- a/api/proto/teleport/presence/v1/service.proto +++ b/api/proto/teleport/presence/v1/service.proto @@ -47,6 +47,11 @@ service PresenceService { rpc ListRelayServers(ListRelayServersRequest) returns (ListRelayServersResponse); // DeleteRelayServer deletes a relay_server resource by name. rpc DeleteRelayServer(DeleteRelayServerRequest) returns (DeleteRelayServerResponse); + + // ListAuthServers returns a page of Auth servers. + rpc ListAuthServers(ListAuthServersRequest) returns (ListAuthServersResponse); + // ListProxyServers returns a page of Proxy servers. + rpc ListProxyServers(ListProxyServersRequest) returns (ListProxyServersResponse); } // Request for GetRemoteCluster @@ -161,3 +166,39 @@ message DeleteRelayServerRequest { // Response message for the PresenceService.DeleteRelayServer rpc. message DeleteRelayServerResponse {} + +// Request message for the PresenceService.ListAuthServers rpc. +message ListAuthServersRequest { + // The maximum number of items to return. + // The server may impose a different page size at its discretion. + int32 page_size = 1; + // The next_page_token value returned from a previous List request, if any. + string page_token = 2; +} + +// Response message for the PresenceService.ListAuthServers rpc. +message ListAuthServersResponse { + // A list of auth server resources. + repeated types.ServerV2 servers = 1; + // Token to retrieve the next page of results, or empty if there are no + // more results in the list. + string next_page_token = 2; +} + +// Request message for the PresenceService.ListProxyServers rpc. +message ListProxyServersRequest { + // The maximum number of items to return. + // The server may impose a different page size at its discretion. + int32 page_size = 1; + // The next_page_token value returned from a previous List request, if any. + string page_token = 2; +} + +// Response message for the PresenceService.ListProxyServers rpc. +message ListProxyServersResponse { + // A list of proxy server resources. + repeated types.ServerV2 servers = 1; + // Token to retrieve the next page of results, or empty if there are no + // more results in the list. + string next_page_token = 2; +} From de9a26530c8f1b4bf8987e72ff5df7140ec0d54e Mon Sep 17 00:00:00 2001 From: Maxim Dietz Date: Fri, 21 Nov 2025 19:36:07 -0500 Subject: [PATCH 2/2] feat: Implement `ListAuthServers`/`ListProxyServers` - Implement new funcs - Mark `GetAuthServers`/`GetProxies` deprecated - Replace existing usages --- api/client/client.go | 32 +++++++++ integration/ec2_test.go | 6 +- lib/auth/accountrecovery_test.go | 2 +- lib/auth/apiserver.go | 8 +++ lib/auth/apiserver_test.go | 2 + lib/auth/auth.go | 12 ++-- lib/auth/auth_test.go | 8 +-- lib/auth/auth_with_roles.go | 67 +++++++++++++++++-- lib/auth/authclient/api.go | 63 +++++++++++++++++ lib/auth/authclient/http_client.go | 42 ------------ lib/auth/authclient/httpfallback.go | 51 ++++++++++++++ lib/auth/export_test.go | 6 +- lib/auth/gitserver/gitserverv1/github.go | 2 +- lib/auth/gitserver/gitserverv1/service.go | 2 +- .../gitserver/gitserverv1/service_test.go | 2 +- lib/auth/grpcserver.go | 54 +++++++++++++++ lib/auth/integration/integrationv1/awsoidc.go | 6 +- .../integration/integrationv1/awsoidc_test.go | 3 +- lib/auth/integration/integrationv1/service.go | 7 ++ .../integration/integrationv1/service_test.go | 7 ++ .../machineidv1/workload_identity_service.go | 4 ++ .../workloadidentityv1/issuer_service.go | 4 ++ lib/auth/tls_test.go | 6 ++ lib/auth/usertoken.go | 23 ++++--- lib/auth/usertoken_test.go | 10 ++- lib/cache/auth_server.go | 29 +++++++- lib/cache/auth_server_test.go | 6 +- lib/cache/proxy_server.go | 29 +++++++- lib/cache/proxy_server_test.go | 6 +- .../integration_config_provider_test.go | 4 ++ lib/integrations/awsoidc/token_generator.go | 7 ++ .../awsra/generate_credentials_test.go | 8 +++ lib/integrations/awsra/profile_syncer.go | 12 +++- lib/integrations/azureoidc/token_generator.go | 7 ++ lib/join/ec2join/ec2.go | 8 ++- lib/proxy/peer/client.go | 6 +- lib/reversetunnel/localsite_test.go | 4 ++ lib/service/service.go | 6 +- lib/services/app.go | 6 +- lib/services/app_test.go | 18 +++++ lib/services/local/presence.go | 61 +++++++++++++++++ lib/services/local/suite_test.go | 6 ++ lib/services/presence.go | 13 ++++ lib/services/watcher.go | 5 +- lib/services/watcher_test.go | 4 ++ lib/srv/app/connections_handler.go | 10 ++- lib/srv/app/watcher.go | 17 ++++- lib/srv/discovery/discovery.go | 1 + .../kube_integration_watcher_test.go | 5 ++ lib/utils/oidc/issuer.go | 14 +++- lib/utils/oidc/issuer_test.go | 7 ++ lib/web/apiserver_test.go | 2 + lib/web/app/handler_test.go | 4 ++ lib/web/databases.go | 6 +- lib/web/ui/cluster.go | 9 ++- lib/web/ui/perf_test.go | 10 +++ tool/tctl/common/auth_command.go | 17 ++++- tool/tctl/common/auth_command_test.go | 4 ++ tool/tctl/common/auth_rotate_command.go | 52 +++++++++----- tool/tctl/common/bots_command.go | 17 +++-- tool/tctl/common/node_command.go | 13 +++- tool/tctl/common/proxy_command.go | 7 +- tool/tctl/common/resource_command.go | 2 + tool/tctl/common/token_command.go | 27 ++++++-- tool/tctl/sso/configure/github.go | 6 +- tool/tctl/sso/tester/command.go | 6 +- 66 files changed, 768 insertions(+), 142 deletions(-) diff --git a/api/client/client.go b/api/client/client.go index bdd0871eb8341..a699c50505f78 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -3674,6 +3674,38 @@ func (c *Client) DeleteAllApps(ctx context.Context) error { return trace.Wrap(err) } +// ListAuthServers returns a paginated list of auth servers registered in the cluster. +func (c *Client) ListAuthServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) { + resp, err := c.PresenceServiceClient().ListAuthServers(ctx, &presencepb.ListAuthServersRequest{ + PageSize: int32(pageSize), + PageToken: pageToken, + }) + if err != nil { + return nil, "", trace.Wrap(err) + } + servers := make([]types.Server, 0, len(resp.Servers)) + for _, server := range resp.Servers { + servers = append(servers, server) + } + return servers, resp.NextPageToken, nil +} + +// ListProxyServers returns a paginated list of proxy servers registered in the cluster. +func (c *Client) ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) { + resp, err := c.PresenceServiceClient().ListProxyServers(ctx, &presencepb.ListProxyServersRequest{ + PageSize: int32(pageSize), + PageToken: pageToken, + }) + if err != nil { + return nil, "", trace.Wrap(err) + } + servers := make([]types.Server, 0, len(resp.Servers)) + for _, server := range resp.Servers { + servers = append(servers, server) + } + return servers, resp.NextPageToken, nil +} + // CreateKubernetesCluster creates a new kubernetes cluster resource. func (c *Client) CreateKubernetesCluster(ctx context.Context, cluster types.KubeCluster) error { kubeClusterV3, ok := cluster.(*types.KubernetesClusterV3) diff --git a/integration/ec2_test.go b/integration/ec2_test.go index bf0fa04671d72..81fc6adab6bbd 100644 --- a/integration/ec2_test.go +++ b/integration/ec2_test.go @@ -39,6 +39,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/integration/helpers" "github.com/gravitational/teleport/lib" "github.com/gravitational/teleport/lib/backend" @@ -46,6 +47,7 @@ import ( cloudimds "github.com/gravitational/teleport/lib/cloud/imds" cloudaws "github.com/gravitational/teleport/lib/cloud/imds/aws" "github.com/gravitational/teleport/lib/defaults" + "github.com/gravitational/teleport/lib/itertools/stream" "github.com/gravitational/teleport/lib/labels" "github.com/gravitational/teleport/lib/service" "github.com/gravitational/teleport/lib/service/servicecfg" @@ -260,7 +262,7 @@ func TestIAMNodeJoin(t *testing.T) { require.NoError(t, err) // sanity check there are no proxies to start with - proxies, err := authServer.GetProxies() + proxies, err := stream.Collect(clientutils.Resources(ctx, authServer.ListProxyServers)) require.NoError(t, err) require.Empty(t, proxies) @@ -274,7 +276,7 @@ func TestIAMNodeJoin(t *testing.T) { // the proxy should eventually join the cluster and heartbeat require.EventuallyWithT(t, func(t *assert.CollectT) { - proxies, err := authServer.GetProxies() + proxies, err := stream.Collect(clientutils.Resources(ctx, authServer.ListProxyServers)) assert.NoError(t, err) assert.NotEmpty(t, proxies) }, 10*time.Second, 50*time.Millisecond, "waiting for proxy to join cluster") diff --git a/lib/auth/accountrecovery_test.go b/lib/auth/accountrecovery_test.go index 084e4acf7e92a..4ca60cae0f97b 100644 --- a/lib/auth/accountrecovery_test.go +++ b/lib/auth/accountrecovery_test.go @@ -1032,7 +1032,7 @@ func TestGetAccountRecoveryToken(t *testing.T) { name: "invalid token type", wantErr: true, getRequest: func() *proto.GetAccountRecoveryTokenRequest { - wrongTokenType, err := srv.Auth().NewUserToken(authclient.CreateUserTokenRequest{ + wrongTokenType, err := srv.Auth().NewUserToken(ctx, authclient.CreateUserTokenRequest{ Name: "llama", TTL: 5 * time.Minute, Type: authclient.UserTokenTypeResetPassword, diff --git a/lib/auth/apiserver.go b/lib/auth/apiserver.go index 351e6b2687724..bc391cf1f842e 100644 --- a/lib/auth/apiserver.go +++ b/lib/auth/apiserver.go @@ -123,8 +123,10 @@ func NewAPIServer(config *APIConfig) (http.Handler, error) { // Servers and presence heartbeat srv.POST("/:version/namespaces/:namespace/nodes/keepalive", srv.WithAuth(srv.keepAliveNode)) srv.POST("/:version/authservers", srv.WithAuth(srv.upsertAuthServer)) + // TODO(kiosion) DELETE IN 21.0.0 srv.GET("/:version/authservers", srv.WithScopedAuth(srv.getAuthServers)) srv.POST("/:version/proxies", srv.WithAuth(srv.upsertProxy)) + // TODO(kiosion) DELETE IN 21.0.0 srv.GET("/:version/proxies", srv.WithScopedAuth(srv.getProxies)) srv.DELETE("/:version/proxies", srv.WithAuth(srv.deleteAllProxies)) srv.DELETE("/:version/proxies/:name", srv.WithAuth(srv.deleteProxy)) @@ -313,7 +315,10 @@ func (s *APIServer) upsertProxy(auth *ServerWithRoles, w http.ResponseWriter, r } // getProxies returns registered proxies +// +// TODO(kiosion) DELETE IN 21.0.0 func (s *APIServer) getProxies(auth *ServerWithRoles, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 servers, err := auth.GetProxies() if err != nil { return nil, trace.Wrap(err) @@ -349,7 +354,10 @@ func (s *APIServer) upsertAuthServer(auth *ServerWithRoles, w http.ResponseWrite } // getAuthServers returns registered auth servers +// +// TODO(kiosion) DELETE IN 21.0.0 func (s *APIServer) getAuthServers(auth *ServerWithRoles, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 servers, err := auth.GetAuthServers() if err != nil { return nil, trace.Wrap(err) diff --git a/lib/auth/apiserver_test.go b/lib/auth/apiserver_test.go index b87366a83f11c..66740b4e324a4 100644 --- a/lib/auth/apiserver_test.go +++ b/lib/auth/apiserver_test.go @@ -134,8 +134,10 @@ func TestUpsertServer(t *testing.T) { require.NoError(t, err) allServers = append(allServers, servers...) } + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 addServers(s.GetAuthServers()) addServers(s.GetNodes(ctx, apidefaults.Namespace)) + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 addServers(s.GetProxies()) require.Empty(t, cmp.Diff(allServers, []types.Server{tt.wantServer}, cmpopts.IgnoreFields(types.Metadata{}, "Revision"))) }) diff --git a/lib/auth/auth.go b/lib/auth/auth.go index a8acdc1631dc9..81223d6e6f526 100644 --- a/lib/auth/auth.go +++ b/lib/auth/auth.go @@ -4425,7 +4425,7 @@ func (a *Server) createTOTPPrivilegeToken(ctx context.Context, username string) return nil, trace.Wrap(err) } - token, err := a.newUserToken(tokenReq) + token, err := a.newUserToken(ctx, tokenReq) if err != nil { return nil, trace.Wrap(err) } @@ -4464,7 +4464,7 @@ func (a *Server) createRegisterChallenge(ctx context.Context, req *newRegisterCh return nil, trace.BadParameter("all TOTP registrations require a privilege token") } - otpKey, otpOpts, err := a.newTOTPKey(req.username) + otpKey, otpOpts, err := a.newTOTPKey(ctx, req.username) if err != nil { return nil, trace.Wrap(err) } @@ -7489,7 +7489,7 @@ func (a *Server) Ping(ctx context.Context) (proto.PingResponse, error) { ClusterName: cn.GetClusterName(), ServerVersion: teleport.Version, ServerFeatures: features, - ProxyPublicAddr: a.getProxyPublicAddr(), + ProxyPublicAddr: a.getProxyPublicAddr(ctx), IsBoring: modules.GetModules().IsBoringBinary(), LoadAllCAs: a.loadAllCAs, SignatureAlgorithmSuite: authPref.GetSignatureAlgorithmSuite(), @@ -8413,8 +8413,10 @@ func (a *Server) verifyAccessRequestMonthlyLimit(ctx context.Context) error { // getProxyPublicAddr returns the first valid, non-empty proxy public address it // finds, or empty otherwise. -func (a *Server) getProxyPublicAddr() string { - if proxies, err := a.GetProxies(); err == nil { +func (a *Server) getProxyPublicAddr(ctx context.Context) string { + if proxies, err := iterstream.Collect( + clientutils.Resources(ctx, a.ListProxyServers), + ); err == nil { for _, p := range proxies { addr := p.GetPublicAddr() if addr == "" { diff --git a/lib/auth/auth_test.go b/lib/auth/auth_test.go index 88b0f47af273b..e3e9db5c66b76 100644 --- a/lib/auth/auth_test.go +++ b/lib/auth/auth_test.go @@ -3190,7 +3190,7 @@ func TestDeleteMFADeviceSync(t *testing.T) { deleteReqUsingToken := func(tokenReq authclient.CreateUserTokenRequest) func(t *testing.T) *proto.DeleteMFADeviceSyncRequest { return func(t *testing.T) *proto.DeleteMFADeviceSyncRequest { - token, err := authServer.NewUserToken(tokenReq) + token, err := authServer.NewUserToken(ctx, tokenReq) require.NoError(t, err, "newUserToken") _, err = authServer.CreateUserToken(ctx, token) @@ -3395,7 +3395,7 @@ func TestDeleteMFADeviceSync_WithErrors(t *testing.T) { deleteReq := test.deleteReq if test.tokenRequest != nil { - token, err := authServer.NewUserToken(*test.tokenRequest) + token, err := authServer.NewUserToken(ctx, *test.tokenRequest) require.NoError(t, err) _, err = authServer.CreateUserToken(context.Background(), token) require.NoError(t, err) @@ -3613,7 +3613,7 @@ func TestAddMFADeviceSync(t *testing.T) { wantErr: true, getReq: func(t *testing.T, deviceName string) *proto.AddMFADeviceSyncRequest { // Obtain a non privilege token. - token, err := authServer.NewUserToken(authclient.CreateUserTokenRequest{ + token, err := authServer.NewUserToken(ctx, authclient.CreateUserTokenRequest{ Name: u.username, TTL: 5 * time.Minute, Type: authclient.UserTokenTypeResetPassword, @@ -3803,7 +3803,7 @@ func TestGetMFADevices_WithToken(t *testing.T) { tokenID := "test-token-not-found" if tc.tokenRequest != nil { - token, err := srv.Auth().NewUserToken(*tc.tokenRequest) + token, err := srv.Auth().NewUserToken(ctx, *tc.tokenRequest) require.NoError(t, err) _, err = srv.Auth().CreateUserToken(context.Background(), token) require.NoError(t, err) diff --git a/lib/auth/auth_with_roles.go b/lib/auth/auth_with_roles.go index 7e593ecb67191..a3a3834214e84 100644 --- a/lib/auth/auth_with_roles.go +++ b/lib/auth/auth_with_roles.go @@ -52,6 +52,7 @@ import ( apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/api/types/wrappers" apiutils "github.com/gravitational/teleport/api/utils" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/api/utils/keys/hardwarekey" "github.com/gravitational/teleport/entitlements" "github.com/gravitational/teleport/lib/auth/authclient" @@ -2299,6 +2300,9 @@ func (a *ServerWithRoles) UpsertAuthServer(ctx context.Context, s types.Server) return a.authServer.UpsertAuthServer(ctx, s) } +// Deprecated: Prefer paginated variant [ListAuthServers]. +// +// TODO(kiosion) DELETE IN 21.0.0 func (a *ServerWithRoles) GetAuthServers() ([]types.Server, error) { if a.scopedContext != nil { ruleCtx := a.scopedContext.RuleContext() @@ -2312,7 +2316,8 @@ func (a *ServerWithRoles) GetAuthServers() ([]types.Server, error) { return nil, trace.Wrap(err) } - return a.authServer.GetAuthServers() + out, err := iterstream.Collect(clientutils.Resources(context.TODO(), a.authServer.ListAuthServers)) + return out, trace.Wrap(err) } if err := a.authorizeAction(types.KindAuthServer, types.VerbList, types.VerbRead); err != nil { @@ -2321,7 +2326,32 @@ func (a *ServerWithRoles) GetAuthServers() ([]types.Server, error) { } } - return a.authServer.GetAuthServers() + + out, err := iterstream.Collect(clientutils.Resources(context.TODO(), a.authServer.ListAuthServers)) + return out, trace.Wrap(err) +} + +func (a *ServerWithRoles) ListAuthServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) { + if a.scopedContext != nil { + ruleCtx := a.scopedContext.RuleContext() + // For auth server reads we do not enforce scope pinning. This ensures that auths are readable for + // all scoped identities regardless of their current scope pinning. This pattern should not + // be used for any checks save essential global configuration reads that are necessary for basic + // teleport functionality. + if err := a.scopedContext.CheckerContext.RiskyUnpinnedDecision(a.CloseContext(), scopes.Root, func(checker *services.SplitAccessChecker) error { + return checker.Common().CheckAccessToRules(&ruleCtx, types.KindAuthServer, types.VerbList, types.VerbRead) + }); err != nil { + return nil, "", trace.Wrap(err) + } + + return a.authServer.ListAuthServers(ctx, pageSize, pageToken) + } + + if err := a.authorizeAction(types.KindAuthServer, types.VerbList, types.VerbRead); err != nil { + return nil, "", trace.Wrap(err) + } + + return a.authServer.ListAuthServers(ctx, pageSize, pageToken) } // DeleteAuthServer deletes auth server by name @@ -2339,6 +2369,9 @@ func (a *ServerWithRoles) UpsertProxy(ctx context.Context, s types.Server) error return a.authServer.UpsertProxy(ctx, s) } +// Deprecated: Prefer paginated variant [ListProxyServers]. +// +// TODO(kiosion) DELETE IN 21.0.0 func (a *ServerWithRoles) GetProxies() ([]types.Server, error) { if a.scopedContext != nil { ruleCtx := a.scopedContext.RuleContext() @@ -2352,13 +2385,39 @@ func (a *ServerWithRoles) GetProxies() ([]types.Server, error) { return nil, trace.Wrap(err) } - return a.authServer.GetProxies() + out, err := iterstream.Collect(clientutils.Resources(context.TODO(), a.authServer.ListProxyServers)) + return out, trace.Wrap(err) } if err := a.authorizeAction(types.KindProxy, types.VerbList, types.VerbRead); err != nil { return nil, trace.Wrap(err) } - return a.authServer.GetProxies() + + out, err := iterstream.Collect(clientutils.Resources(context.TODO(), a.authServer.ListProxyServers)) + return out, trace.Wrap(err) +} + +func (a *ServerWithRoles) ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) { + if a.scopedContext != nil { + ruleCtx := a.scopedContext.RuleContext() + // For proxy reads we do not enforce scope pinning. This ensures that proxies are readable for + // all scoped identities regardless of their current scope pinning. This pattern should not + // be used for any checks save essential global configuration reads that are necessary for basic + // teleport functionality. + if err := a.scopedContext.CheckerContext.RiskyUnpinnedDecision(a.CloseContext(), scopes.Root, func(checker *services.SplitAccessChecker) error { + return checker.Common().CheckAccessToRules(&ruleCtx, types.KindProxy, types.VerbList, types.VerbRead) + }); err != nil { + return nil, "", trace.Wrap(err) + } + + return a.authServer.ListProxyServers(ctx, pageSize, pageToken) + } + + if err := a.authorizeAction(types.KindProxy, types.VerbList, types.VerbRead); err != nil { + return nil, "", trace.Wrap(err) + } + + return a.authServer.ListProxyServers(ctx, pageSize, pageToken) } // DeleteAllProxies deletes all proxies diff --git a/lib/auth/authclient/api.go b/lib/auth/authclient/api.go index a719ba79ed038..1e908a7ae209e 100644 --- a/lib/auth/authclient/api.go +++ b/lib/auth/authclient/api.go @@ -221,11 +221,25 @@ type ReadProxyAccessPoint interface { GetNodes(ctx context.Context, namespace string) ([]types.Server, error) // GetProxies returns a list of proxy servers registered in the cluster + // + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + // ListProxyServers returns a paginated list of proxy servers registered in the cluster + ListProxyServers(ctx context.Context, pageSize int, nextToken string) ([]types.Server, string, error) + // GetAuthServers returns a list of auth servers registered in the cluster + // + // Deprecated: Prefer paginated variant [ListAuthServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetAuthServers() ([]types.Server, error) + // ListAuthServers returns a paginated list of auth servers registered in the cluster + ListAuthServers(ctx context.Context, pageSize int, nextToken string) ([]types.Server, string, error) + // ListReverseTunnels returns a list of reverse tunnels with pagination. ListReverseTunnels(ctx context.Context, pageSize int, nextToken string) ([]types.ReverseTunnel, string, error) @@ -438,11 +452,25 @@ type ReadRemoteProxyAccessPoint interface { GetNodes(ctx context.Context, namespace string) ([]types.Server, error) // GetProxies returns a list of proxy servers registered in the cluster + // + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + // ListProxyServers returns a paginated list of proxy servers registered in the cluster + ListProxyServers(ctx context.Context, pageSize int, nextToken string) ([]types.Server, string, error) + // GetAuthServers returns a list of auth servers registered in the cluster + // + // Deprecated: Prefer paginated variant [ListAuthServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetAuthServers() ([]types.Server, error) + // ListAuthServers returns a paginated list of auth servers registered in the cluster + ListAuthServers(ctx context.Context, pageSize int, nextToken string) ([]types.Server, string, error) + // GetAllTunnelConnections returns all tunnel connections GetAllTunnelConnections(opts ...services.MarshalOption) ([]types.TunnelConnection, error) @@ -601,8 +629,15 @@ type ReadAppsAccessPoint interface { GetRoles(ctx context.Context) ([]types.Role, error) // GetProxies returns a list of proxy servers registered in the cluster + // + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + // ListProxyServers returns a paginated list of proxy servers registered in the cluster + ListProxyServers(ctx context.Context, pageSize int, nextToken string) ([]types.Server, string, error) + // GetApps returns all application resources. GetApps(ctx context.Context) ([]types.Application, error) @@ -828,8 +863,15 @@ type ReadDiscoveryAccessPoint interface { GetIntegration(ctx context.Context, name string) (types.Integration, error) // GetProxies returns a list of registered proxies. + // + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + // ListProxyServers returns a paginated list of proxy servers registered in the cluster + ListProxyServers(ctx context.Context, pageSize int, nextToken string) ([]types.Server, string, error) + // GetUserTask gets a single User Task by its name. GetUserTask(ctx context.Context, name string) (*usertasksv1.UserTask, error) } @@ -914,8 +956,15 @@ type ReadOktaAccessPoint interface { NewWatcher(ctx context.Context, watch types.Watch) (types.Watcher, error) // GetProxies returns a list of proxy servers registered in the cluster + // + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + // ListProxyServers returns a paginated list of proxy servers registered in the cluster + ListProxyServers(ctx context.Context, pageSize int, nextToken string) ([]types.Server, string, error) + // GetAuthPreference returns the cluster authentication configuration. GetAuthPreference(ctx context.Context) (types.AuthPreference, error) @@ -1075,11 +1124,25 @@ type Cache interface { GetNodes(ctx context.Context, namespace string) ([]types.Server, error) // GetProxies returns a list of proxy servers registered in the cluster + // + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + // ListProxyServers returns a paginated list of proxy servers registered in the cluster + ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) + // GetAuthServers returns a list of auth servers registered in the cluster + // + // Deprecated: Prefer paginated variant [ListAuthServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetAuthServers() ([]types.Server, error) + // ListAuthServers returns a paginated list of auth servers registered in the cluster + ListAuthServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) + // GetCertAuthority returns cert authority by id GetCertAuthority(ctx context.Context, id types.CertAuthID, loadKeys bool) (types.CertAuthority, error) diff --git a/lib/auth/authclient/http_client.go b/lib/auth/authclient/http_client.go index 4a5874f82e4ff..4a3abe7573b9b 100644 --- a/lib/auth/authclient/http_client.go +++ b/lib/auth/authclient/http_client.go @@ -423,27 +423,6 @@ func (c *HTTPClient) UpsertAuthServer(ctx context.Context, s types.Server) error return trace.Wrap(err) } -// GetAuthServers returns the list of auth servers registered in the cluster. -func (c *HTTPClient) GetAuthServers() ([]types.Server, error) { - out, err := c.Get(context.TODO(), c.Endpoint("authservers"), url.Values{}) - if err != nil { - return nil, trace.Wrap(err) - } - var items []json.RawMessage - if err := json.Unmarshal(out.Bytes(), &items); err != nil { - return nil, trace.Wrap(err) - } - re := make([]types.Server, len(items)) - for i, raw := range items { - server, err := services.UnmarshalServer(raw, types.KindAuthServer) - if err != nil { - return nil, trace.Wrap(err) - } - re[i] = server - } - return re, nil -} - // UpsertProxy is used by proxies to report their presence // to other auth servers in form of heartbeat expiring after ttl period. func (c *HTTPClient) UpsertProxy(ctx context.Context, s types.Server) error { @@ -458,27 +437,6 @@ func (c *HTTPClient) UpsertProxy(ctx context.Context, s types.Server) error { return trace.Wrap(err) } -// GetProxies returns the list of auth servers registered in the cluster. -func (c *HTTPClient) GetProxies() ([]types.Server, error) { - out, err := c.Get(context.TODO(), c.Endpoint("proxies"), url.Values{}) - if err != nil { - return nil, trace.Wrap(err) - } - var items []json.RawMessage - if err := json.Unmarshal(out.Bytes(), &items); err != nil { - return nil, trace.Wrap(err) - } - re := make([]types.Server, len(items)) - for i, raw := range items { - server, err := services.UnmarshalServer(raw, types.KindProxy) - if err != nil { - return nil, trace.Wrap(err) - } - re[i] = server - } - return re, nil -} - // DeleteAllProxies deletes all proxies func (c *HTTPClient) DeleteAllProxies() error { _, err := c.Delete(context.TODO(), c.Endpoint("proxies")) diff --git a/lib/auth/authclient/httpfallback.go b/lib/auth/authclient/httpfallback.go index 0fd2125f79aaf..023a10dfa0825 100644 --- a/lib/auth/authclient/httpfallback.go +++ b/lib/auth/authclient/httpfallback.go @@ -20,6 +20,7 @@ package authclient import ( "context" + "encoding/json" "net/url" "github.com/gravitational/trace" @@ -58,3 +59,53 @@ func (c *HTTPClient) getClusterName(ctx context.Context) (types.ClusterName, err return cn, err } + +// GetAuthServers returns the list of auth servers registered in the cluster. +// +// Deprecated: Prefer paginated variant [APIClient.ListAuthServers]. +// +// TODO(kiosion): DELETE IN 21.0.0 +func (c *HTTPClient) GetAuthServers() ([]types.Server, error) { + out, err := c.Get(context.TODO(), c.Endpoint("authservers"), url.Values{}) + if err != nil { + return nil, trace.Wrap(err) + } + var items []json.RawMessage + if err := json.Unmarshal(out.Bytes(), &items); err != nil { + return nil, trace.Wrap(err) + } + re := make([]types.Server, len(items)) + for i, raw := range items { + server, err := services.UnmarshalServer(raw, types.KindAuthServer) + if err != nil { + return nil, trace.Wrap(err) + } + re[i] = server + } + return re, nil +} + +// GetProxies returns the list of auth servers registered in the cluster. +// +// Deprecated: Prefer paginated variant [APIClient.ListProxyServers]. +// +// TODO(kiosion): DELETE IN 21.0.0 +func (c *HTTPClient) GetProxies() ([]types.Server, error) { + out, err := c.Get(context.TODO(), c.Endpoint("proxies"), url.Values{}) + if err != nil { + return nil, trace.Wrap(err) + } + var items []json.RawMessage + if err := json.Unmarshal(out.Bytes(), &items); err != nil { + return nil, trace.Wrap(err) + } + re := make([]types.Server, len(items)) + for i, raw := range items { + server, err := services.UnmarshalServer(raw, types.KindProxy) + if err != nil { + return nil, trace.Wrap(err) + } + re[i] = server + } + return re, nil +} diff --git a/lib/auth/export_test.go b/lib/auth/export_test.go index a2eb46bc3a737..5e391bff83187 100644 --- a/lib/auth/export_test.go +++ b/lib/auth/export_test.go @@ -98,8 +98,8 @@ func (a *Server) CreateRecoveryToken(ctx context.Context, username, tokenType st return a.createRecoveryToken(ctx, username, tokenType, usage) } -func (a *Server) NewUserToken(req authclient.CreateUserTokenRequest) (types.UserToken, error) { - return a.newUserToken(req) +func (a *Server) NewUserToken(ctx context.Context, req authclient.CreateUserTokenRequest) (types.UserToken, error) { + return a.newUserToken(ctx, req) } func CreatePrivilegeToken(ctx context.Context, srv *Server, username, tokenKind string) (*types.UserTokenV3, error) { @@ -248,7 +248,7 @@ func ValidServerHostname(hostname string) bool { } func FormatAccountName(s proxyDomainGetter, username string, authHostname string) (string, error) { - return formatAccountName(s, username, authHostname) + return formatAccountName(context.TODO(), s, username, authHostname) } func ConfigureCAsForTrustedCluster(tc types.TrustedCluster, cas []types.CertAuthority) { diff --git a/lib/auth/gitserver/gitserverv1/github.go b/lib/auth/gitserver/gitserverv1/github.go index 17aaa60436c67..66463b764da9e 100644 --- a/lib/auth/gitserver/gitserverv1/github.go +++ b/lib/auth/gitserver/gitserverv1/github.go @@ -124,7 +124,7 @@ func (s *Service) makeGithubConnectorSpec(ctx context.Context, uuid, org, integr return &types.GithubConnectorSpecV3{ ClientID: clientID, ClientSecret: clientSecret, - RedirectURL: fmt.Sprintf("https://%s/v1/webapi/github/callback", s.cfg.ProxyPublicAddrGetter()), + RedirectURL: fmt.Sprintf("https://%s/v1/webapi/github/callback", s.cfg.ProxyPublicAddrGetter(ctx)), EndpointURL: types.GithubURL, APIEndpointURL: types.GithubAPIURL, // TODO(greedy52) the GitHub OAuth flow for authenticated user does not diff --git a/lib/auth/gitserver/gitserverv1/service.go b/lib/auth/gitserver/gitserverv1/service.go index e263e7259f97f..795841dcb23a3 100644 --- a/lib/auth/gitserver/gitserverv1/service.go +++ b/lib/auth/gitserver/gitserverv1/service.go @@ -56,7 +56,7 @@ type Config struct { // Log is the slog logger. Log *slog.Logger // ProxyPublicAddrGetter gets the public proxy address. - ProxyPublicAddrGetter func() string + ProxyPublicAddrGetter func(context.Context) string // GitHubAuthRequestCreator is a callback to create the prepared request in // the backend. GitHubAuthRequestCreator GitHubAuthRequestCreator diff --git a/lib/auth/gitserver/gitserverv1/service_test.go b/lib/auth/gitserver/gitserverv1/service_test.go index 96db35882cb77..e0ba093ecbb32 100644 --- a/lib/auth/gitserver/gitserverv1/service_test.go +++ b/lib/auth/gitserver/gitserverv1/service_test.go @@ -345,7 +345,7 @@ func newService(t *testing.T, checker services.AccessChecker, existing ...*types Backend: testBackend{ GitServers: gitServersService, }, - ProxyPublicAddrGetter: func() string { + ProxyPublicAddrGetter: func(context.Context) string { return fakeProxyAddr }, GitHubAuthRequestCreator: func(_ context.Context, req types.GithubAuthRequest) (*types.GithubAuthRequest, error) { diff --git a/lib/auth/grpcserver.go b/lib/auth/grpcserver.go index a8e6e3c59bf63..ef4ae62bfdb3c 100644 --- a/lib/auth/grpcserver.go +++ b/lib/auth/grpcserver.go @@ -3635,6 +3635,60 @@ func (g *GRPCServer) DeleteToken(ctx context.Context, req *types.ResourceRequest return &emptypb.Empty{}, nil } +// ListAuthServers returns a paginated list of auth servers. +func (g *GRPCServer) ListAuthServers(ctx context.Context, req *presencev1pb.ListAuthServersRequest) (*presencev1pb.ListAuthServersResponse, error) { + auth, err := g.authenticate(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + servers, next, err := auth.ListAuthServers(ctx, int(req.PageSize), req.PageToken) + if err != nil { + return nil, trace.Wrap(err) + } + + serverV2s := make([]*types.ServerV2, 0, len(servers)) + for _, server := range servers { + srv, ok := server.(*types.ServerV2) + if !ok { + return nil, trace.Errorf("encountered unexpected server type: %T", server) + } + serverV2s = append(serverV2s, srv) + } + + return &presencev1pb.ListAuthServersResponse{ + Servers: serverV2s, + NextPageToken: next, + }, nil +} + +// ListProxyServers returns a paginated list of proxy servers. +func (g *GRPCServer) ListProxyServers(ctx context.Context, req *presencev1pb.ListProxyServersRequest) (*presencev1pb.ListProxyServersResponse, error) { + auth, err := g.authenticate(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + servers, next, err := auth.ListProxyServers(ctx, int(req.PageSize), req.PageToken) + if err != nil { + return nil, trace.Wrap(err) + } + + serverV2s := make([]*types.ServerV2, 0, len(servers)) + for _, server := range servers { + srv, ok := server.(*types.ServerV2) + if !ok { + return nil, trace.Errorf("encountered unexpected server type: %T", server) + } + serverV2s = append(serverV2s, srv) + } + + return &presencev1pb.ListProxyServersResponse{ + Servers: serverV2s, + NextPageToken: next, + }, nil +} + // GetNode retrieves a node by name and namespace. func (g *GRPCServer) GetNode(ctx context.Context, req *types.ResourceInNamespaceRequest) (*types.ServerV2, error) { if req.Namespace == "" { diff --git a/lib/auth/integration/integrationv1/awsoidc.go b/lib/auth/integration/integrationv1/awsoidc.go index 62e68d707057e..bb5098df90496 100644 --- a/lib/auth/integration/integrationv1/awsoidc.go +++ b/lib/auth/integration/integrationv1/awsoidc.go @@ -86,7 +86,7 @@ type AWSOIDCServiceConfig struct { Cache CacheAWSOIDC TokenCreator TokenCreator Clock clockwork.Clock - ProxyPublicAddrGetter func() string + ProxyPublicAddrGetter func(context.Context) string Logger *slog.Logger } @@ -225,7 +225,7 @@ type AWSOIDCService struct { authorizer authz.Authorizer logger *slog.Logger clock clockwork.Clock - proxyPublicAddrGetter func() string + proxyPublicAddrGetter func(context.Context) string cache CacheAWSOIDC tokenCreator TokenCreator } @@ -669,7 +669,7 @@ func (s *AWSOIDCService) EnrollEKSClusters(ctx context.Context, req *integration return nil, trace.Wrap(err) } - publicProxyAddr := s.proxyPublicAddrGetter() + publicProxyAddr := s.proxyPublicAddrGetter(ctx) if publicProxyAddr == "" { return nil, trace.BadParameter("could not get public proxy address.") } diff --git a/lib/auth/integration/integrationv1/awsoidc_test.go b/lib/auth/integration/integrationv1/awsoidc_test.go index 8630e8fe91e9e..3fbbf83facdb6 100644 --- a/lib/auth/integration/integrationv1/awsoidc_test.go +++ b/lib/auth/integration/integrationv1/awsoidc_test.go @@ -19,6 +19,7 @@ package integrationv1 import ( + "context" "testing" "github.com/gravitational/trace" @@ -221,7 +222,7 @@ func TestRBAC(t *testing.T) { awsoidService, err := NewAWSOIDCService(&AWSOIDCServiceConfig{ IntegrationService: resourceSvc, Authorizer: resourceSvc.authorizer, - ProxyPublicAddrGetter: func() string { return "128.0.0.1" }, + ProxyPublicAddrGetter: func(context.Context) string { return "128.0.0.1" }, Cache: backend, TokenCreator: backend, }) diff --git a/lib/auth/integration/integrationv1/service.go b/lib/auth/integration/integrationv1/service.go index 6e709d462b5d0..6aae9ed7d7a94 100644 --- a/lib/auth/integration/integrationv1/service.go +++ b/lib/auth/integration/integrationv1/service.go @@ -54,8 +54,15 @@ type Cache interface { GetCertAuthority(ctx context.Context, id types.CertAuthID, loadSigningKeys bool) (types.CertAuthority, error) // GetProxies returns a list of registered proxies. + // + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + // ListProxyServers returns a paginated list of registered proxies. + ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) + // IntegrationsGetter defines methods to access Integration resources. services.IntegrationsGetter diff --git a/lib/auth/integration/integrationv1/service_test.go b/lib/auth/integration/integrationv1/service_test.go index cc83a3db1eb1d..8c59f1a4f67bd 100644 --- a/lib/auth/integration/integrationv1/service_test.go +++ b/lib/auth/integration/integrationv1/service_test.go @@ -1075,6 +1075,13 @@ func (m *mockCache) GetProxies() ([]types.Server, error) { return m.proxies, nil } +func (m *mockCache) ListProxyServers(context.Context, int, string) ([]types.Server, string, error) { + if m.returnErr != nil { + return nil, "", m.returnErr + } + return m.proxies, "", nil +} + func (m *mockCache) GetToken(ctx context.Context, token string) (types.ProvisionToken, error) { return nil, nil } diff --git a/lib/auth/machineid/machineidv1/workload_identity_service.go b/lib/auth/machineid/machineidv1/workload_identity_service.go index d5fea526bc41b..a761f7553c366 100644 --- a/lib/auth/machineid/machineidv1/workload_identity_service.go +++ b/lib/auth/machineid/machineidv1/workload_identity_service.go @@ -73,7 +73,11 @@ type WorkloadIdentityServiceConfig struct { type WorkloadIdentityCacher interface { GetCertAuthority(ctx context.Context, id types.CertAuthID, loadKeys bool) (types.CertAuthority, error) GetClusterName(ctx context.Context) (types.ClusterName, error) + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) } // KeyStorer is an interface that provides methods to retrieve keys and diff --git a/lib/auth/machineid/workloadidentityv1/issuer_service.go b/lib/auth/machineid/workloadidentityv1/issuer_service.go index d03e83e1e30c9..a8707dda9e082 100644 --- a/lib/auth/machineid/workloadidentityv1/issuer_service.go +++ b/lib/auth/machineid/workloadidentityv1/issuer_service.go @@ -82,7 +82,11 @@ func (OSSSigstorePolicyEvaluator) Evaluate(_ context.Context, policyNames []stri type issuerCache interface { workloadIdentityReader + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + ListProxyServers(context.Context, int, string) ([]types.Server, string, error) GetCertAuthority(ctx context.Context, id types.CertAuthID, loadKeys bool) (types.CertAuthority, error) } diff --git a/lib/auth/tls_test.go b/lib/auth/tls_test.go index cb8ac02e90a43..4c393eba69673 100644 --- a/lib/auth/tls_test.go +++ b/lib/auth/tls_test.go @@ -1570,6 +1570,7 @@ func TestServersCRUD(t *testing.T) { require.Empty(t, out) // Proxy service. + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 out, err = clt.GetProxies() require.NoError(t, err) require.Empty(t, out) @@ -1578,6 +1579,7 @@ func TestServersCRUD(t *testing.T) { proxy.Spec.Hostname = "proxy.llama" require.NoError(t, clt.UpsertProxy(ctx, proxy)) + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 out, err = clt.GetProxies() require.NoError(t, err) require.Len(t, out, 1) @@ -1586,11 +1588,13 @@ func TestServersCRUD(t *testing.T) { err = clt.DeleteProxy(ctx, proxy.GetName()) require.NoError(t, err) + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 out, err = clt.GetProxies() require.NoError(t, err) require.Empty(t, out) // Auth service. + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 out, err = clt.GetAuthServers() require.NoError(t, err) require.Empty(t, out) @@ -1599,6 +1603,7 @@ func TestServersCRUD(t *testing.T) { auth.Spec.Hostname = "auth.llama" require.NoError(t, clt.UpsertAuthServer(ctx, auth)) + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 out, err = clt.GetAuthServers() require.NoError(t, err) require.Len(t, out, 1) @@ -4562,6 +4567,7 @@ func TestEvents(t *testing.T) { err := testSrv.Auth().UpsertProxy(ctx, srv) require.NoError(t, err) + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 out, err := testSrv.Auth().GetProxies() require.NoError(t, err) diff --git a/lib/auth/usertoken.go b/lib/auth/usertoken.go index 2598d3fe1ed5e..5f11a72950981 100644 --- a/lib/auth/usertoken.go +++ b/lib/auth/usertoken.go @@ -77,7 +77,7 @@ func (a *Server) CreateResetPasswordToken(ctx context.Context, req authclient.Cr return nil, trace.Wrap(err) } - token, err := a.newUserToken(req) + token, err := a.newUserToken(ctx, req) if err != nil { return nil, trace.Wrap(err) } @@ -125,19 +125,23 @@ func (a *Server) resetMFA(ctx context.Context, user string) error { // proxyDomainGetter is a reduced subset of the Auth API for formatAccountName. type proxyDomainGetter interface { + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + ListProxyServers(context.Context, int, string) ([]types.Server, string, error) GetDomainName() (string, error) } // formatAccountName builds the account name to display in OTP applications. // Format for accountName is user@address. User is passed in, this function // tries to find the best available address. -func formatAccountName(s proxyDomainGetter, username string, authHostname string) (string, error) { +func formatAccountName(ctx context.Context, s proxyDomainGetter, username string, authHostname string) (string, error) { var err error var proxyHost string // Get a list of proxies. - proxies, err := s.GetProxies() + proxies, err := stream.Collect(clientutils.Resources(ctx, s.ListProxyServers)) if err != nil { return "", trace.Wrap(err) } @@ -192,10 +196,9 @@ func (a *Server) createTOTPUserTokenSecrets(ctx context.Context, token types.Use return secrets, nil } -func (a *Server) newTOTPKey(user string) (*otp.Key, *totp.GenerateOpts, error) { - ctx := context.TODO() +func (a *Server) newTOTPKey(ctx context.Context, user string) (*otp.Key, *totp.GenerateOpts, error) { // Fetch account name to display in OTP apps. - accountName, err := formatAccountName(a, user, a.AuthServiceName) + accountName, err := formatAccountName(ctx, a, user, a.AuthServiceName) if err != nil { return nil, nil, trace.Wrap(err) } @@ -218,7 +221,7 @@ func (a *Server) newTOTPKey(user string) (*otp.Key, *totp.GenerateOpts, error) { return key, &opts, nil } -func (a *Server) newUserToken(req authclient.CreateUserTokenRequest) (types.UserToken, error) { +func (a *Server) newUserToken(ctx context.Context, req authclient.CreateUserTokenRequest) (types.UserToken, error) { var err error var proxyHost string @@ -234,7 +237,7 @@ func (a *Server) newUserToken(req authclient.CreateUserTokenRequest) (types.User // Get the list of proxies and try and guess the address of the proxy. If // failed to guess public address, use ":3080" as a fallback. - proxies, err := a.GetProxies() + proxies, err := stream.Collect(clientutils.Resources(ctx, a.ListProxyServers)) if err != nil { return nil, trace.Wrap(err) } @@ -342,7 +345,7 @@ func (a *Server) createRecoveryToken(ctx context.Context, username, tokenType st return nil, trace.Wrap(err) } - newToken, err := a.newUserToken(req) + newToken, err := a.newUserToken(ctx, req) if err != nil { return nil, trace.Wrap(err) } @@ -423,7 +426,7 @@ func (a *Server) createPrivilegeToken(ctx context.Context, username, tokenKind s return nil, trace.Wrap(err) } - newToken, err := a.newUserToken(req) + newToken, err := a.newUserToken(ctx, req) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/auth/usertoken_test.go b/lib/auth/usertoken_test.go index 23ded1e00fcb0..b1ae3216082cd 100644 --- a/lib/auth/usertoken_test.go +++ b/lib/auth/usertoken_test.go @@ -265,6 +265,7 @@ func TestUserTokenSecretsCreationSettings(t *testing.T) { func TestUserTokenCreationSettings(t *testing.T) { t.Parallel() srv := newTestTLSServer(t) + ctx := t.Context() username := "joe@example.com" _, _, err := authtest.CreateUserAndRole(srv.Auth(), username, []string{username}, nil) @@ -276,7 +277,7 @@ func TestUserTokenCreationSettings(t *testing.T) { Type: authclient.UserTokenTypeResetPasswordInvite, } - token, err := srv.Auth().NewUserToken(req) + token, err := srv.Auth().NewUserToken(ctx, req) require.NoError(t, err) require.Equal(t, req.Name, token.GetUser()) require.Equal(t, req.Type, token.GetSubKind()) @@ -461,6 +462,13 @@ func (s *debugAuth) GetProxies() ([]types.Server, error) { return s.proxies, nil } +func (s *debugAuth) ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) { + if s.proxiesError { + return nil, "", trace.BadParameter("failed to fetch proxies") + } + return s.proxies, "", nil +} + func (s *debugAuth) GetDomainName() (string, error) { if s.clusterName == "" { return "", trace.NotFound("no cluster name set") diff --git a/lib/cache/auth_server.go b/lib/cache/auth_server.go index db673329ebefb..6957cff90ed0a 100644 --- a/lib/cache/auth_server.go +++ b/lib/cache/auth_server.go @@ -21,6 +21,7 @@ import ( "github.com/gravitational/trace" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/lib/services" ) @@ -41,8 +42,11 @@ func newAuthServerCollection(p services.Presence, w types.WatchKind) (*collectio authServerNameIndex: types.Server.GetName, }), fetcher: func(ctx context.Context, loadSecrets bool) ([]types.Server, error) { - servers, err := p.GetAuthServers() - return servers, trace.Wrap(err) + out, err := clientutils.CollectWithFallback(ctx, p.ListAuthServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return p.GetAuthServers() + }) + return out, trace.Wrap(err) }, headerTransform: func(hdr *types.ResourceHeader) types.Server { return &types.ServerV2{ @@ -58,6 +62,10 @@ func newAuthServerCollection(p services.Presence, w types.WatchKind) (*collectio } // GetAuthServers returns a list of registered servers +// +// Deprecated: Prefer paginated gRPC variant [ListAuthServers]. +// +// TODO(kiosion): DELETE IN 21.0.0 func (c *Cache) GetAuthServers() ([]types.Server, error) { _, span := c.Tracer.Start(context.TODO(), "cache/GetAuthServers") defer span.End() @@ -69,6 +77,7 @@ func (c *Cache) GetAuthServers() ([]types.Server, error) { defer rg.Release() if !rg.ReadCache() { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 servers, err := c.Config.Presence.GetAuthServers() return servers, trace.Wrap(err) } @@ -80,3 +89,19 @@ func (c *Cache) GetAuthServers() ([]types.Server, error) { return servers, nil } + +// ListAuthServers returns a paginated list of registered auth servers. +func (c *Cache) ListAuthServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) { + ctx, span := c.Tracer.Start(ctx, "cache/ListAuthServers") + defer span.End() + + lister := genericLister[types.Server, authServerIndex]{ + cache: c, + collection: c.collections.authServers, + index: authServerNameIndex, + upstreamList: c.Config.Presence.ListAuthServers, + nextToken: types.Server.GetName, + } + out, next, err := lister.list(ctx, pageSize, pageToken) + return out, next, trace.Wrap(err) +} diff --git a/lib/cache/auth_server_test.go b/lib/cache/auth_server_test.go index 22019f7bf24ee..52822e11916e7 100644 --- a/lib/cache/auth_server_test.go +++ b/lib/cache/auth_server_test.go @@ -43,11 +43,11 @@ func TestAuthServers(t *testing.T) { }, nil }, create: p.presenceS.UpsertAuthServer, - list: getAllAdapter(func(context.Context) ([]types.Server, error) { return p.presenceS.GetAuthServers() }), - cacheList: getAllAdapter(func(context.Context) ([]types.Server, error) { return p.cache.GetAuthServers() }), + list: p.presenceS.ListAuthServers, + cacheList: p.cache.ListAuthServers, update: p.presenceS.UpsertAuthServer, deleteAll: func(_ context.Context) error { return p.presenceS.DeleteAllAuthServers() }, - }, withSkipPaginationTest()) + }) } diff --git a/lib/cache/proxy_server.go b/lib/cache/proxy_server.go index 73d78ebfbe021..068772e655ab1 100644 --- a/lib/cache/proxy_server.go +++ b/lib/cache/proxy_server.go @@ -22,6 +22,7 @@ import ( "github.com/gravitational/trace" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/lib/services" ) @@ -42,8 +43,11 @@ func newProxyServerCollection(p services.Presence, w types.WatchKind) (*collecti proxyServerNameIndex: types.Server.GetName, }), fetcher: func(ctx context.Context, loadSecrets bool) ([]types.Server, error) { - servers, err := p.GetProxies() - return servers, trace.Wrap(err) + out, err := clientutils.CollectWithFallback(ctx, p.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return p.GetProxies() + }) + return out, trace.Wrap(err) }, headerTransform: func(hdr *types.ResourceHeader) types.Server { return &types.ServerV2{ @@ -59,6 +63,10 @@ func newProxyServerCollection(p services.Presence, w types.WatchKind) (*collecti } // GetProxies is a part of auth.Cache implementation +// +// Deprecated: Prefer paginated gRPC variant [ListProxyServers]. +// +// TODO(kiosion): DELETE IN 21.0.0 func (c *Cache) GetProxies() ([]types.Server, error) { _, span := c.Tracer.Start(context.TODO(), "cache/GetProxies") defer span.End() @@ -70,6 +78,7 @@ func (c *Cache) GetProxies() ([]types.Server, error) { defer rg.Release() if !rg.ReadCache() { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 servers, err := c.Config.Presence.GetAuthServers() return servers, trace.Wrap(err) } @@ -81,3 +90,19 @@ func (c *Cache) GetProxies() ([]types.Server, error) { return servers, nil } + +// ListProxyServers returns a paginated list of registered proxy servers. +func (c *Cache) ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) { + ctx, span := c.Tracer.Start(ctx, "cache/ListProxyServers") + defer span.End() + + lister := genericLister[types.Server, proxyServerIndex]{ + cache: c, + collection: c.collections.proxyServers, + index: proxyServerNameIndex, + upstreamList: c.Config.Presence.ListProxyServers, + nextToken: types.Server.GetName, + } + out, next, err := lister.list(ctx, pageSize, pageToken) + return out, next, trace.Wrap(err) +} diff --git a/lib/cache/proxy_server_test.go b/lib/cache/proxy_server_test.go index 4fcf2ebe07576..13b8598b5d637 100644 --- a/lib/cache/proxy_server_test.go +++ b/lib/cache/proxy_server_test.go @@ -44,11 +44,11 @@ func TestProxies(t *testing.T) { }, nil }, create: p.presenceS.UpsertProxy, - list: getAllAdapter(func(_ context.Context) ([]types.Server, error) { return p.presenceS.GetProxies() }), - cacheList: getAllAdapter(func(_ context.Context) ([]types.Server, error) { return p.cache.GetProxies() }), + list: p.presenceS.ListProxyServers, + cacheList: p.cache.ListProxyServers, update: p.presenceS.UpsertProxy, deleteAll: func(_ context.Context) error { return p.presenceS.DeleteAllProxies() }, - }, withSkipPaginationTest()) + }) } diff --git a/lib/integrations/awsoidc/credprovider/integration_config_provider_test.go b/lib/integrations/awsoidc/credprovider/integration_config_provider_test.go index 7c66c5371fd28..d3293c93e4067 100644 --- a/lib/integrations/awsoidc/credprovider/integration_config_provider_test.go +++ b/lib/integrations/awsoidc/credprovider/integration_config_provider_test.go @@ -92,6 +92,10 @@ func (d *depsMock) GetProxies() ([]types.Server, error) { return d.proxies, nil } +func (d *depsMock) ListProxyServers(context.Context, int, string) ([]types.Server, string, error) { + return d.proxies, "", nil +} + func (d *depsMock) GetClusterName(_ context.Context) (types.ClusterName, error) { return types.NewClusterName(types.ClusterNameSpecV2{ClusterName: "teleport.example.com", ClusterID: "cluster-id"}) } diff --git a/lib/integrations/awsoidc/token_generator.go b/lib/integrations/awsoidc/token_generator.go index 61406cfbd09d3..14e67ee88bbf3 100644 --- a/lib/integrations/awsoidc/token_generator.go +++ b/lib/integrations/awsoidc/token_generator.go @@ -47,7 +47,14 @@ type Cache interface { GetCertAuthority(ctx context.Context, id types.CertAuthID, loadKeys bool) (types.CertAuthority, error) // GetProxies returns a list of registered proxies. + // + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + + // ListProxyServers returns a paginated list of registered proxies. + ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) } // KeyStoreManager defines methods to get signers using the server's keystore. diff --git a/lib/integrations/awsra/generate_credentials_test.go b/lib/integrations/awsra/generate_credentials_test.go index 75c1ee7688583..a4789e08aa92e 100644 --- a/lib/integrations/awsra/generate_credentials_test.go +++ b/lib/integrations/awsra/generate_credentials_test.go @@ -138,6 +138,14 @@ func (m *mockCache) GetProxies() ([]types.Server, error) { }}, nil } +func (m *mockCache) ListProxyServers(context.Context, int, string) ([]types.Server, string, error) { + return []types.Server{&types.ServerV2{ + Spec: types.ServerSpecV2{ + PublicAddrs: []string{"proxy.example.com"}, + }, + }}, "", nil +} + func (m *mockCache) ListIntegrations(ctx context.Context, pageSize int, nextKey string) ([]types.Integration, string, error) { if m.integrations == nil { m.integrations = map[string]types.Integration{} diff --git a/lib/integrations/awsra/profile_syncer.go b/lib/integrations/awsra/profile_syncer.go index 0a4e11970ff6d..6908b96f0fbb9 100644 --- a/lib/integrations/awsra/profile_syncer.go +++ b/lib/integrations/awsra/profile_syncer.go @@ -38,6 +38,7 @@ import ( "github.com/gravitational/teleport/api/defaults" integrationv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/api/utils/retryutils" "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/integrations/awsra/createsession" @@ -99,7 +100,13 @@ type SyncerCache interface { // GetClusterName returns the current cluster name. GetClusterName(ctx context.Context) (types.ClusterName, error) // GetProxies returns a list of proxy servers registered in the cluster + // + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + // ListProxyServers returns a paginated list of registered proxy servers. + ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) // ListIntegrations returns a paginated list of all integration resources. ListIntegrations(ctx context.Context, pageSize int, nextKey string) ([]types.Integration, string, error) } @@ -301,7 +308,10 @@ func truncateErrorMessage(err error) string { } func fetchProxyPublicAddr(cache SyncerCache) (string, error) { - proxies, err := cache.GetProxies() + proxies, err := clientutils.CollectWithFallback(context.TODO(), cache.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return cache.GetProxies() + }) if err != nil { return "", trace.Wrap(err) } diff --git a/lib/integrations/azureoidc/token_generator.go b/lib/integrations/azureoidc/token_generator.go index 9f3524f6bde80..fcb402b94eeb8 100644 --- a/lib/integrations/azureoidc/token_generator.go +++ b/lib/integrations/azureoidc/token_generator.go @@ -52,7 +52,14 @@ type Cache interface { GetClusterName(ctx context.Context) (types.ClusterName, error) // GetProxies returns a list of registered proxies. + // + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + + // ListProxyServers returns a paginated list of registered proxies. + ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) } // GenerateEntraOIDCToken returns a JWT suitable for OIDC authentication to MS Graph API. diff --git a/lib/join/ec2join/ec2.go b/lib/join/ec2join/ec2.go index 24cdae10a639a..f7bf53b6c999d 100644 --- a/lib/join/ec2join/ec2.go +++ b/lib/join/ec2join/ec2.go @@ -41,6 +41,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/lib/join/provision" "github.com/gravitational/teleport/lib/services" awsutils "github.com/gravitational/teleport/lib/utils/aws" @@ -183,8 +184,11 @@ func nodeExists(ctx context.Context, presence services.Presence, hostID string) } } -func proxyExists(_ context.Context, presence services.Presence, hostID string) (bool, error) { - proxies, err := presence.GetProxies() +func proxyExists(ctx context.Context, presence services.Presence, hostID string) (bool, error) { + proxies, err := clientutils.CollectWithFallback(ctx, presence.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return presence.GetProxies() + }) if err != nil { return false, trace.Wrap(err) } diff --git a/lib/proxy/peer/client.go b/lib/proxy/peer/client.go index 56feb4714a7db..41a027a1a8d58 100644 --- a/lib/proxy/peer/client.go +++ b/lib/proxy/peer/client.go @@ -40,6 +40,7 @@ import ( "github.com/gravitational/teleport/api/metadata" "github.com/gravitational/teleport/api/types" apiutils "github.com/gravitational/teleport/api/utils" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/api/utils/grpc/interceptors" streamutils "github.com/gravitational/teleport/api/utils/grpc/stream" "github.com/gravitational/teleport/lib/auth/authclient" @@ -676,7 +677,10 @@ func (c *Client) getConnections(proxyIDs []string) ([]internal.ClientConn, bool, c.metrics.reportTunnelError(errorProxyPeerTunnelNotFound) // try to establish new connections otherwise. - proxies, err := c.config.AuthClient.GetProxies() + proxies, err := clientutils.CollectWithFallback(c.ctx, c.config.AuthClient.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return c.config.AuthClient.GetProxies() + }) if err != nil { c.metrics.reportTunnelError(errorProxyPeerFetchProxies) return nil, false, trace.Wrap(err) diff --git a/lib/reversetunnel/localsite_test.go b/lib/reversetunnel/localsite_test.go index ab5c68ced2a51..5104edeb26fe6 100644 --- a/lib/reversetunnel/localsite_test.go +++ b/lib/reversetunnel/localsite_test.go @@ -356,6 +356,10 @@ func (m *mockLocalSiteClient) GetProxies() ([]types.Server, error) { return m.proxies, nil } +func (m *mockLocalSiteClient) ListProxyServers(context.Context, int, string) ([]types.Server, string, error) { + return m.proxies, "", nil +} + type mockWatcher struct { events chan types.Event } diff --git a/lib/service/service.go b/lib/service/service.go index 4b81d40b28f65..f6872824cbac3 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -6513,7 +6513,7 @@ func (process *TeleportProcess) initApps() { // Loop over each application and create a server. var applications types.Apps for _, app := range process.Config.Apps.Apps { - publicAddr, err := getPublicAddr(accessPoint, app) + publicAddr, err := getPublicAddr(process.ExitContext(), accessPoint, app) if err != nil { return trace.Wrap(err) } @@ -7048,7 +7048,7 @@ func dumperHandler(w http.ResponseWriter, r *http.Request) { } // getPublicAddr waits for a proxy to be registered with Teleport. -func getPublicAddr(authClient authclient.ReadAppsAccessPoint, a servicecfg.App) (string, error) { +func getPublicAddr(ctx context.Context, authClient authclient.ReadAppsAccessPoint, a servicecfg.App) (string, error) { ticker := time.NewTicker(500 * time.Millisecond) defer ticker.Stop() timeout := time.NewTimer(5 * time.Second) @@ -7057,7 +7057,7 @@ func getPublicAddr(authClient authclient.ReadAppsAccessPoint, a servicecfg.App) for { select { case <-ticker.C: - publicAddr, err := app.FindPublicAddr(authClient, a.PublicAddr, a.Name) + publicAddr, err := app.FindPublicAddr(ctx, authClient, a.PublicAddr, a.Name) if err == nil { return publicAddr, nil } diff --git a/lib/services/app.go b/lib/services/app.go index 53383f6fabdb7..e6028bbbd3312 100644 --- a/lib/services/app.go +++ b/lib/services/app.go @@ -39,6 +39,7 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/wrappers" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/lib/utils" ) @@ -90,7 +91,10 @@ func ValidateApp(app types.Application, proxyGetter ProxyGetter) error { return trace.Wrap(err, "app %q has an invalid IDN hostname %q", app.GetName(), appAddr.Host()) } - proxyServers, err := proxyGetter.GetProxies() + proxyServers, err := clientutils.CollectWithFallback(context.TODO(), proxyGetter.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return proxyGetter.GetProxies() + }) if err != nil { return trace.Wrap(err) } diff --git a/lib/services/app_test.go b/lib/services/app_test.go index fafa57d7cd17d..290b9071a9b7d 100644 --- a/lib/services/app_test.go +++ b/lib/services/app_test.go @@ -19,6 +19,7 @@ package services import ( + "context" "log/slog" "net/http" "net/http/httptest" @@ -203,6 +204,23 @@ func (m *mockProxyGetter) GetProxies() ([]types.Server, error) { return servers, nil } +func (m *mockProxyGetter) ListProxyServers(_ context.Context, _ int, _ string) ([]types.Server, string, error) { + servers := make([]types.Server, 0, len(m.addrs)) + + for _, addr := range m.addrs { + servers = append( + servers, + &types.ServerV2{ + Spec: types.ServerSpecV2{ + PublicAddrs: []string{addr}, + }, + }, + ) + } + + return servers, "", nil +} + // TestApplicationUnmarshal verifies an app resource can be unmarshaled. func TestApplicationUnmarshal(t *testing.T) { expected, err := types.NewAppV3(types.Metadata{ diff --git a/lib/services/local/presence.go b/lib/services/local/presence.go index 72617a0fadca6..0203c13a95f57 100644 --- a/lib/services/local/presence.go +++ b/lib/services/local/presence.go @@ -344,11 +344,63 @@ func (s *PresenceService) UpdateNode(ctx context.Context, server types.Server) ( return server, nil } +// rangeAuthServers returns auth servers within the range [start, end] +func (s *PresenceService) rangeAuthServers(ctx context.Context, start, end string) iter.Seq2[types.Server, error] { + mapFn := func(item backend.Item) (types.Server, bool) { + server, err := services.UnmarshalServer(item.Value, types.KindAuthServer, services.WithExpires(item.Expires), services.WithRevision(item.Revision)) + if err != nil { + s.logger.WarnContext(ctx, "Skipping item during ListAuthServers because conversion from backend item failed", "key", item.Key, "error", err) + return nil, false + } + return server, true + } + + startKey := backend.NewKey(authServersPrefix, start) + endKey := backend.RangeEnd(backend.NewKey(authServersPrefix)) + if end != "" { + endKey = backend.NewKey(authServersPrefix, end).ExactKey() + } + + return stream.FilterMap(s.Backend.Items(ctx, backend.ItemsParams{StartKey: startKey, EndKey: endKey}), mapFn) +} + +// rangeProxyServers returns proxy servers within the range [start, end] +func (s *PresenceService) rangeProxyServers(ctx context.Context, start, end string) iter.Seq2[types.Server, error] { + mapFn := func(item backend.Item) (types.Server, bool) { + server, err := services.UnmarshalServer(item.Value, types.KindProxy, services.WithExpires(item.Expires), services.WithRevision(item.Revision)) + if err != nil { + s.logger.WarnContext(ctx, "Skipping item during ListProxyServers because conversion from backend item failed", "key", item.Key, "error", err) + return nil, false + } + return server, true + } + startKey := backend.NewKey(proxiesPrefix, start) + endKey := backend.RangeEnd(backend.NewKey(proxiesPrefix)) + if end != "" { + endKey = backend.NewKey(proxiesPrefix, end).ExactKey() + } + + return stream.FilterMap(s.Backend.Items(ctx, backend.ItemsParams{StartKey: startKey, EndKey: endKey}), mapFn) +} + +func serverToPaginationKey(s types.Server) string { + return backend.GetPaginationKey(s) +} + // GetAuthServers returns a list of registered servers +// +// Deprecated: Prefer paginated variant [ListAuthServers]. +// +// TODO(kiosion): DELETE IN 21.0.0 func (s *PresenceService) GetAuthServers() ([]types.Server, error) { return s.getServers(context.TODO(), types.KindAuthServer, authServersPrefix) } +// ListAuthServers returns a paginated list of registered auth servers. +func (s *PresenceService) ListAuthServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) { + return generic.CollectPageAndCursor(s.rangeAuthServers(ctx, pageToken, ""), pageSize, serverToPaginationKey) +} + // UpsertAuthServer registers auth server presence, permanently if ttl is 0 or // for the specified duration with second resolution if it's >= 1 second func (s *PresenceService) UpsertAuthServer(ctx context.Context, server types.Server) error { @@ -374,10 +426,19 @@ func (s *PresenceService) UpsertProxy(ctx context.Context, server types.Server) } // GetProxies returns a list of registered proxies +// +// Deprecated: Prefer paginated variant [ListProxyServers]. +// +// TODO(kiosion): DELETE IN 21.0.0 func (s *PresenceService) GetProxies() ([]types.Server, error) { return s.getServers(context.TODO(), types.KindProxy, proxiesPrefix) } +// ListProxyServers returns a paginated list of registered proxy servers. +func (s *PresenceService) ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) { + return generic.CollectPageAndCursor(s.rangeProxyServers(ctx, pageToken, ""), pageSize, serverToPaginationKey) +} + // DeleteAllProxies deletes all proxies func (s *PresenceService) DeleteAllProxies() error { startKey := backend.ExactKey(proxiesPrefix) diff --git a/lib/services/local/suite_test.go b/lib/services/local/suite_test.go index ae505c12530b0..6b58887b3ce19 100644 --- a/lib/services/local/suite_test.go +++ b/lib/services/local/suite_test.go @@ -382,6 +382,7 @@ func (s *ServicesTestSuite) ServerCRUD(t *testing.T) { require.Empty(t, out) // Proxy service. + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 out, err = s.PresenceS.GetProxies() require.NoError(t, err) require.Empty(t, out) @@ -390,6 +391,7 @@ func (s *ServicesTestSuite) ServerCRUD(t *testing.T) { proxy.Spec.Hostname = "proxy.llama" require.NoError(t, s.PresenceS.UpsertProxy(ctx, proxy)) + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 out, err = s.PresenceS.GetProxies() require.NoError(t, err) require.Len(t, out, 1) @@ -398,11 +400,13 @@ func (s *ServicesTestSuite) ServerCRUD(t *testing.T) { err = s.PresenceS.DeleteProxy(ctx, proxy.GetName()) require.NoError(t, err) + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 out, err = s.PresenceS.GetProxies() require.NoError(t, err) require.Empty(t, out) // Auth service. + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 out, err = s.PresenceS.GetAuthServers() require.NoError(t, err) require.Empty(t, out) @@ -411,6 +415,7 @@ func (s *ServicesTestSuite) ServerCRUD(t *testing.T) { auth.Spec.Hostname = "auth.llama" require.NoError(t, s.PresenceS.UpsertAuthServer(ctx, auth)) + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 out, err = s.PresenceS.GetAuthServers() require.NoError(t, err) require.Len(t, out, 1) @@ -2158,6 +2163,7 @@ func (s *ServicesTestSuite) Events(t *testing.T) { err := s.PresenceS.UpsertProxy(ctx, srv) require.NoError(t, err) + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 out, err := s.PresenceS.GetProxies() require.NoError(t, err) diff --git a/lib/services/presence.go b/lib/services/presence.go index 2aa3fc68af896..78937247e5c0a 100644 --- a/lib/services/presence.go +++ b/lib/services/presence.go @@ -31,7 +31,13 @@ import ( // ProxyGetter is a service that gets proxies. type ProxyGetter interface { // GetProxies returns a list of registered proxies. + // + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + // ListProxyServers returns a paginated list of registered Proxy servers. + ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) } // NodesGetter is a service that gets nodes. @@ -83,8 +89,15 @@ type Presence interface { UpsertNode(ctx context.Context, server types.Server) (*types.KeepAlive, error) // GetAuthServers returns a list of registered servers + // + // Deprecated: Prefer paginated variant [ListAuthServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetAuthServers() ([]types.Server, error) + // ListAuthServers returns a paginated list of registered auth servers. + ListAuthServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) + // UpsertAuthServer registers auth server presence, permanently if ttl is 0 or // for the specified duration with second resolution if it's >= 1 second UpsertAuthServer(ctx context.Context, server types.Server) error diff --git a/lib/services/watcher.go b/lib/services/watcher.go index 4649058bf35f5..38437220cbed6 100644 --- a/lib/services/watcher.go +++ b/lib/services/watcher.go @@ -409,7 +409,10 @@ func NewProxyWatcher(ctx context.Context, cfg ProxyWatcherConfig) (*GenericWatch ResourceKind: types.KindProxy, ResourceKey: types.Server.GetName, ResourceGetter: func(ctx context.Context) ([]types.Server, error) { - return proxyGetter.GetProxies() + return clientutils.CollectWithFallback(ctx, proxyGetter.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return proxyGetter.GetProxies() + }) }, ResourcesC: cfg.ProxiesC, ResourceDiffer: cfg.ProxyDiffer, diff --git a/lib/services/watcher_test.go b/lib/services/watcher_test.go index 4517e80761f23..87d9d6c7ae5f9 100644 --- a/lib/services/watcher_test.go +++ b/lib/services/watcher_test.go @@ -70,6 +70,10 @@ func (n nopProxyGetter) GetProxies() ([]types.Server, error) { return nil, nil } +func (n nopProxyGetter) ListProxyServers(_ context.Context, _ int, _ string) ([]types.Server, string, error) { + return nil, "", nil +} + func TestResourceWatcher_Backoff(t *testing.T) { t.Parallel() ctx := context.Background() diff --git a/lib/srv/app/connections_handler.go b/lib/srv/app/connections_handler.go index b2981455cbfac..4afe47e1235fd 100644 --- a/lib/srv/app/connections_handler.go +++ b/lib/srv/app/connections_handler.go @@ -42,6 +42,7 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/events" apiutils "github.com/gravitational/teleport/api/utils" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/authz" @@ -297,7 +298,7 @@ func NewConnectionsHandler(closeContext context.Context, cfg *ConnectionsHandler c.tlsConfig = CopyAndConfigureTLS(c.log, c.cfg.AccessPoint, c.cfg.TLSConfig) // Figure out the port the proxy is running on. - c.proxyPort = c.getProxyPort() + c.proxyPort = c.getProxyPort(c.closeContext) return c, nil } @@ -459,8 +460,11 @@ func (c *ConnectionsHandler) serveHTTP(w http.ResponseWriter, r *http.Request) e } // getProxyPort tries to figure out the address the proxy is running at. -func (c *ConnectionsHandler) getProxyPort() string { - servers, err := c.cfg.AccessPoint.GetProxies() +func (c *ConnectionsHandler) getProxyPort(ctx context.Context) string { + servers, err := clientutils.CollectWithFallback(ctx, c.cfg.AccessPoint.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return c.cfg.AccessPoint.GetProxies() + }) if err != nil { return strconv.Itoa(defaults.HTTPListenPort) } diff --git a/lib/srv/app/watcher.go b/lib/srv/app/watcher.go index 509ebea87511a..673d9570fd48a 100644 --- a/lib/srv/app/watcher.go +++ b/lib/srv/app/watcher.go @@ -26,6 +26,7 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/services/readonly" "github.com/gravitational/teleport/lib/utils" @@ -90,7 +91,7 @@ func (s *Server) startResourceWatcher(ctx context.Context) (*services.GenericWat case apps := <-watcher.ResourcesC: for _, app := range apps { if app.GetPublicAddr() == "" { - pubAddr, err := FindPublicAddr(s.c.AccessPoint, app.GetPublicAddr(), app.GetName()) + pubAddr, err := FindPublicAddr(ctx, s.c.AccessPoint, app.GetPublicAddr(), app.GetName()) if err == nil { app.SetPublicAddr(pubAddr) } else { @@ -119,21 +120,31 @@ func (s *Server) startResourceWatcher(ctx context.Context) (*services.GenericWat // FindPublicAddrClient is a client used for finding public addresses. type FindPublicAddrClient interface { // GetProxies returns a list of proxy servers registered in the cluster + // + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + // ListProxyServers returns a paginated list of registered proxy servers. + ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) + // GetClusterName gets the name of the cluster from the backend. GetClusterName(ctx context.Context) (types.ClusterName, error) } // FindPublicAddr tries to resolve the public address of the proxy of this cluster. -func FindPublicAddr(client FindPublicAddrClient, appPublicAddr string, appName string) (string, error) { +func FindPublicAddr(ctx context.Context, client FindPublicAddrClient, appPublicAddr string, appName string) (string, error) { // If the application has a public address already set, use it. if appPublicAddr != "" { return appPublicAddr, nil } // Fetch list of proxies, if first has public address set, use it. - servers, err := client.GetProxies() + servers, err := clientutils.CollectWithFallback(ctx, client.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return client.GetProxies() + }) if err != nil { return "", trace.Wrap(err) } diff --git a/lib/srv/discovery/discovery.go b/lib/srv/discovery/discovery.go index 3b8c44982b8a9..457b55536ccaf 100644 --- a/lib/srv/discovery/discovery.go +++ b/lib/srv/discovery/discovery.go @@ -586,6 +586,7 @@ func (s *Server) startDynamicMatchersWatcher(ctx context.Context) error { // This is only used if the matcher does not specify a ProxyAddress. // Example: proxy.example.com:3080 or proxy.example.com func (s *Server) publicProxyAddress(ctx context.Context) (string, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 proxies, err := s.AccessPoint.GetProxies() if err != nil { return "", trace.Wrap(err) diff --git a/lib/srv/discovery/kube_integration_watcher_test.go b/lib/srv/discovery/kube_integration_watcher_test.go index 151c559c13f97..c6045037db720 100644 --- a/lib/srv/discovery/kube_integration_watcher_test.go +++ b/lib/srv/discovery/kube_integration_watcher_test.go @@ -521,6 +521,11 @@ func (m *mockIntegrationsTokenGenerator) GetProxies() ([]types.Server, error) { return m.proxies, nil } +// ListProxyServers returns a paginated list of registered proxies. +func (m *mockIntegrationsTokenGenerator) ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) { + return m.proxies, "", nil +} + // GenerateAWSOIDCToken generates a token to be used to execute an AWS OIDC Integration action. func (m *mockIntegrationsTokenGenerator) GenerateAWSOIDCToken(ctx context.Context, integration string) (string, error) { return uuid.NewString(), nil diff --git a/lib/utils/oidc/issuer.go b/lib/utils/oidc/issuer.go index a777dbebaac79..0c2f6b0c2fcf0 100644 --- a/lib/utils/oidc/issuer.go +++ b/lib/utils/oidc/issuer.go @@ -26,19 +26,29 @@ import ( "github.com/gravitational/trace" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/clientutils" ) -// ProxyGetter is a service that gets proxies. +// ProxiesGetter is a service that gets proxies. type ProxiesGetter interface { // GetProxies returns a list of registered proxies. + // + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + // ListProxyServers returns a paginated list of registered proxies. + ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) } // IssuerForCluster returns the issuer URL using the Cluster state. // Path is an optional element to append to the issuer to distinguish a // separate CA within the same cluster. func IssuerForCluster(ctx context.Context, clt ProxiesGetter, path string) (string, error) { - proxies, err := clt.GetProxies() + proxies, err := clientutils.CollectWithFallback(ctx, clt.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return clt.GetProxies() + }) if err != nil { return "", trace.Wrap(err) } diff --git a/lib/utils/oidc/issuer_test.go b/lib/utils/oidc/issuer_test.go index dd360ab937d4c..7045142f61792 100644 --- a/lib/utils/oidc/issuer_test.go +++ b/lib/utils/oidc/issuer_test.go @@ -94,6 +94,13 @@ func (m *mockProxyGetter) GetProxies() ([]types.Server, error) { return m.proxies, nil } +func (m *mockProxyGetter) ListProxyServers(_ context.Context, _ int, _ string) ([]types.Server, string, error) { + if m.returnErr != nil { + return nil, "", m.returnErr + } + return m.proxies, "", nil +} + func TestIssuerForCluster(t *testing.T) { ctx := context.Background() for _, tt := range []struct { diff --git a/lib/web/apiserver_test.go b/lib/web/apiserver_test.go index 96e10e2154a9a..4b6ae15b8eef8 100644 --- a/lib/web/apiserver_test.go +++ b/lib/web/apiserver_test.go @@ -573,6 +573,7 @@ func newWebSuiteWithConfig(t *testing.T, cfg webSuiteConfig) *WebSuite { // Wait for proxy to fully register before starting the test. for start := time.Now(); ; { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 proxies, err := s.proxyClient.GetProxies() require.NoError(t, err) if len(proxies) != 0 { @@ -8406,6 +8407,7 @@ func newWebPack(t *testing.T, numProxies int, opts ...webPackOptions) *webPack { // Wait for proxies to fully register before starting the test. for start := time.Now(); ; { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 proxies, err := proxies[0].client.GetProxies() require.NoError(t, err) if len(proxies) == numProxies { diff --git a/lib/web/app/handler_test.go b/lib/web/app/handler_test.go index 64a3bc7418161..4c4b581731361 100644 --- a/lib/web/app/handler_test.go +++ b/lib/web/app/handler_test.go @@ -577,6 +577,10 @@ func (c *mockAuthClient) GetProxies() ([]types.Server, error) { return []types.Server{}, nil } +func (c *mockAuthClient) ListProxyServers(context.Context, int, string) ([]types.Server, string, error) { + return []types.Server{}, "", nil +} + // fakeClusterListener Implements a `net.Listener` that return `net.Conn` from // the `FakeCluster`. type fakeClusterListener struct { diff --git a/lib/web/databases.go b/lib/web/databases.go index ec0a2576b0988..2d9f77a2b75a8 100644 --- a/lib/web/databases.go +++ b/lib/web/databases.go @@ -45,6 +45,7 @@ import ( "github.com/gravitational/teleport/api/constants" apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/api/utils/tlsutils" "github.com/gravitational/teleport/lib/auth/authclient" @@ -351,7 +352,10 @@ func (h *Handler) sqlServerConfigureADScriptHandle(w http.ResponseWriter, r *htt return "", trace.BadParameter("invalid token") } - proxyServers, err := h.GetProxyClient().GetProxies() + proxyServers, err := clientutils.CollectWithFallback(r.Context(), h.GetProxyClient().ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return h.GetProxyClient().GetProxies() + }) if err != nil { return "", trace.Wrap(err) } diff --git a/lib/web/ui/cluster.go b/lib/web/ui/cluster.go index 33f92f50b2198..d457de5b8f9b7 100644 --- a/lib/web/ui/cluster.go +++ b/lib/web/ui/cluster.go @@ -27,6 +27,7 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/lib/reversetunnelclient" "github.com/gravitational/teleport/lib/services" ) @@ -95,6 +96,7 @@ func GetClusterDetails(ctx context.Context, cluster reversetunnelclient.Cluster, return nil, trace.Wrap(err) } + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 proxies, err := clt.GetProxies() if err != nil { return nil, trace.Wrap(err) @@ -104,7 +106,12 @@ func GetClusterDetails(ctx context.Context, cluster reversetunnelclient.Cluster, return nil, trace.Wrap(err) } - authServers, err := clt.GetAuthServers() + authServers, err := clientutils.CollectWithFallback( + ctx, + clt.ListAuthServers, + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + func(context.Context) ([]types.Server, error) { return clt.GetAuthServers() }, + ) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/web/ui/perf_test.go b/lib/web/ui/perf_test.go index e53dbf6fec156..121cce7a81f8f 100644 --- a/lib/web/ui/perf_test.go +++ b/lib/web/ui/perf_test.go @@ -178,9 +178,19 @@ func (m *mockAccessPoint) GetNodes(ctx context.Context, namespace string) ([]typ } func (m *mockAccessPoint) GetProxies() ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 return m.presence.GetProxies() } +func (m *mockAccessPoint) ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) { + return m.presence.ListProxyServers(ctx, pageSize, pageToken) +} + func (m *mockAccessPoint) GetAuthServers() ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 return m.presence.GetAuthServers() } + +func (m *mockAccessPoint) ListAuthServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) { + return m.presence.ListAuthServers(ctx, pageSize, pageToken) +} diff --git a/tool/tctl/common/auth_command.go b/tool/tctl/common/auth_command.go index f12fbb3ca8ce0..f56d635aaf4a2 100644 --- a/tool/tctl/common/auth_command.go +++ b/tool/tctl/common/auth_command.go @@ -42,6 +42,7 @@ import ( apidefaults "github.com/gravitational/teleport/api/defaults" trustpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/trust/v1" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/client" @@ -340,6 +341,10 @@ type certificateSigner interface { GetClusterName(ctx context.Context) (types.ClusterName, error) GetClusterNetworkingConfig(ctx context.Context) (types.ClusterNetworkingConfig, error) GetDatabaseServers(ctx context.Context, namespace string, opts ...services.MarshalOption) ([]types.DatabaseServer, error) + ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) GetRemoteClusters(ctx context.Context) ([]types.RemoteCluster, error) TrustClient() trustpb.TrustServiceClient @@ -473,7 +478,12 @@ func (a *AuthCommand) generateSnowflakeKey(ctx context.Context, clusterAPI certi // ListAuthServers prints a list of connected auth servers func (a *AuthCommand) ListAuthServers(ctx context.Context, clusterAPI authCommandClient) error { - servers, err := clusterAPI.GetAuthServers() + servers, err := clientutils.CollectWithFallback( + ctx, + clusterAPI.ListAuthServers, + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + func(context.Context) ([]types.Server, error) { return clusterAPI.GetAuthServers() }, + ) if err != nil { return trace.Wrap(err) } @@ -1232,7 +1242,10 @@ func (a *AuthCommand) checkProxyAddr(ctx context.Context, clusterAPI certificate return trace.WrapWithMessage(err, "couldn't load cluster network configuration, try setting --proxy manually") } // Fetch proxies known to auth server and try to find a public address. - proxies, err := clusterAPI.GetProxies() + proxies, err := clientutils.CollectWithFallback(ctx, clusterAPI.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return clusterAPI.GetProxies() + }) if err != nil { return trace.WrapWithMessage(err, "couldn't load registered proxies, try setting --proxy manually") } diff --git a/tool/tctl/common/auth_command_test.go b/tool/tctl/common/auth_command_test.go index 758df99f715d9..80e5bcb1f6e60 100644 --- a/tool/tctl/common/auth_command_test.go +++ b/tool/tctl/common/auth_command_test.go @@ -438,6 +438,10 @@ func (c *mockClient) GetProxies() ([]types.Server, error) { return c.proxies, nil } +func (c *mockClient) ListProxyServers(context.Context, int, string) ([]types.Server, string, error) { + return c.proxies, "", nil +} + func (c *mockClient) GetRemoteClusters(ctx context.Context) ([]types.RemoteCluster, error) { return c.remoteClusters, nil } diff --git a/tool/tctl/common/auth_rotate_command.go b/tool/tctl/common/auth_rotate_command.go index ccecd5ee5856d..6e6beb459a0ee 100644 --- a/tool/tctl/common/auth_rotate_command.go +++ b/tool/tctl/common/auth_rotate_command.go @@ -44,6 +44,7 @@ import ( apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/mfa" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/api/utils/prompt" "github.com/gravitational/teleport/lib/auth/authclient" libmfa "github.com/gravitational/teleport/lib/client/mfa" @@ -135,7 +136,7 @@ func (c *authRotateCommand) runInteractive(ctx context.Context, client *authclie if err != nil { return trace.Wrap(err, "failed to ping cluster") } - m := newRotateModel(client, pingResp, types.CertAuthType(c.caType)) + m := newRotateModel(ctx, client, pingResp, types.CertAuthType(c.caType)) p := tea.NewProgram(m, tea.WithContext(ctx)) _, err = p.Run() return trace.Wrap(err) @@ -159,6 +160,7 @@ var authRotateTheme = authRotateStyle{ } type rotateModel struct { + ctx context.Context client *authclient.Client pingResp proto.PingResponse @@ -178,8 +180,9 @@ type rotateModel struct { help help.Model } -func newRotateModel(client *authclient.Client, pingResp proto.PingResponse, caType types.CertAuthType) *rotateModel { +func newRotateModel(ctx context.Context, client *authclient.Client, pingResp proto.PingResponse, caType types.CertAuthType) *rotateModel { m := &rotateModel{ + ctx: ctx, client: client, pingResp: pingResp, logsModel: newWriterModel(authRotateTheme.normal), @@ -248,7 +251,7 @@ func (m *rotateModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Now that we've got the current phase, init the waitForCurrentPhaseReady // model if we haven't yet and the current phase is not standby. if m.waitForCurrentPhaseReadyModel == nil && m.currentPhaseModel.phase != "standby" { - m.waitForCurrentPhaseReadyModel = newWaitForReadyModel(m.client, m.currentPhaseModel.caID, m.currentPhaseModel.phase) + m.waitForCurrentPhaseReadyModel = newWaitForReadyModel(m.ctx, m.client, m.currentPhaseModel.caID, m.currentPhaseModel.phase) cmds = append(cmds, m.waitForCurrentPhaseReadyModel.init()) } if m.waitForCurrentPhaseReadyModel != nil { @@ -277,7 +280,7 @@ func (m *rotateModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg.String() { case "n", "N": // Go back to the beginning. - m = newRotateModel(m.client, m.pingResp, "") + m = newRotateModel(m.ctx, m.client, m.pingResp, "") return m, m.Init() case "y", "Y": m.confirmed = true @@ -303,7 +306,7 @@ func (m *rotateModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Now that we've sent the rotate request, init the waitForTargetPhaseReady model if we haven't yet. if m.waitForTargetPhaseReadyModel == nil { - m.waitForTargetPhaseReadyModel = newWaitForReadyModel(m.client, m.currentPhaseModel.caID, m.targetPhaseModel.targetPhase) + m.waitForTargetPhaseReadyModel = newWaitForReadyModel(m.ctx, m.client, m.currentPhaseModel.caID, m.targetPhaseModel.targetPhase) cmds = append(cmds, m.waitForTargetPhaseReadyModel.init()) } cmds = append(cmds, m.waitForTargetPhaseReadyModel.update(msg)) @@ -313,11 +316,11 @@ func (m *rotateModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyMsg: switch { case key.Matches(msg, m.continueBinding): - newModel := newRotateModel(m.client, m.pingResp, m.caTypeModel.caType) + newModel := newRotateModel(m.ctx, m.client, m.pingResp, m.caTypeModel.caType) newModel.waitForCurrentPhaseReadyModel = m.waitForTargetPhaseReadyModel return newModel, newModel.Init() case key.Matches(msg, m.newBinding): - newModel := newRotateModel(m.client, m.pingResp, "") + newModel := newRotateModel(m.ctx, m.client, m.pingResp, "") return newModel, newModel.Init() } } @@ -759,7 +762,7 @@ type waitForReadyModel struct { help help.Model } -func newWaitForReadyModel(client *authclient.Client, caID types.CertAuthID, targetPhase string) *waitForReadyModel { +func newWaitForReadyModel(ctx context.Context, client *authclient.Client, caID types.CertAuthID, targetPhase string) *waitForReadyModel { m := &waitForReadyModel{ client: client, targetPhase: targetPhase, @@ -774,12 +777,31 @@ func newWaitForReadyModel(client *authclient.Client, caID types.CertAuthID, targ } m.kindReadyModels = []*waitForKindReadyModel{ newWaitForKindReadyModel( - targetPhase, "auth_servers", adaptServerGetter(client.GetAuthServers)).withMinReady(1), + targetPhase, "auth_servers", adaptServerGetter(func() ([]types.Server, error) { + return clientutils.CollectWithFallback( + ctx, + client.ListAuthServers, + func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return client.GetAuthServers() + }, + ) + }), + ).withMinReady(1), newWaitForKindReadyModel( - targetPhase, "proxies", adaptServerGetter(client.GetProxies)), + targetPhase, "proxies", adaptServerGetter(func() ([]types.Server, error) { + return clientutils.CollectWithFallback( + ctx, + client.ListProxyServers, + func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return client.GetProxies() + }, + ) + })), newWaitForKindReadyModel( targetPhase, "nodes", adaptServerGetter(func() ([]types.Server, error) { - return apiclient.GetAllResources[types.Server](context.TODO(), client, &proto.ListResourcesRequest{ + return apiclient.GetAllResources[types.Server](ctx, client, &proto.ListResourcesRequest{ ResourceType: types.KindNode, Namespace: apidefaults.Namespace, PredicateExpression: `resource.sub_kind == ""`, @@ -787,15 +809,11 @@ func newWaitForReadyModel(client *authclient.Client, caID types.CertAuthID, targ })), newWaitForKindReadyModel( targetPhase, "app_servers", adaptServerGetter(func() ([]types.AppServer, error) { - return client.GetApplicationServers(context.TODO(), apidefaults.Namespace) + return client.GetApplicationServers(ctx, apidefaults.Namespace) })), newWaitForKindReadyModel( targetPhase, "db_servers", adaptServerGetter(func() ([]types.DatabaseServer, error) { - return client.GetDatabaseServers(context.TODO(), apidefaults.Namespace) - })), - newWaitForKindReadyModel( - targetPhase, "kube_servers", adaptServerGetter(func() ([]types.KubeServer, error) { - return client.GetKubernetesServers(context.TODO()) + return client.GetDatabaseServers(ctx, apidefaults.Namespace) })), } return m diff --git a/tool/tctl/common/bots_command.go b/tool/tctl/common/bots_command.go index f2fd47054697d..c5a08a9443427 100644 --- a/tool/tctl/common/bots_command.go +++ b/tool/tctl/common/bots_command.go @@ -194,7 +194,11 @@ type botsCommandClient interface { GetUser(ctx context.Context, name string, withSecrets bool) (types.User, error) GetRole(context.Context, string) (types.Role, error) UpsertLock(ctx context.Context, lock types.Lock) error + // Deprecated: Prefer paginated variant [ListProxyServers]. + // + // TODO(kiosion): DELETE IN 21.0.0 GetProxies() ([]types.Server, error) + ListProxyServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) PerformMFACeremony(ctx context.Context, in *proto.CreateAuthenticateChallengeRequest, promptOpts ...mfa.PromptOpt) (*proto.MFAAuthenticateResponse, error) } @@ -369,7 +373,7 @@ func (c *BotsCommand) AddBot(ctx context.Context, client botsCommandClient) erro return trace.Wrap(err) } - return trace.Wrap(outputToken(c.stdout, c.format, client, bot, token)) + return trace.Wrap(outputToken(ctx, c.stdout, c.format, client, bot, token)) } func (c *BotsCommand) RemoveBot(ctx context.Context, client botsCommandClient) error { @@ -758,7 +762,7 @@ func (c *BotsCommand) AddBotInstance(ctx context.Context, client botsCommandClie return trace.Wrap(err) } - return trace.Wrap(outputToken(c.stdout, c.format, client, bot, token)) + return trace.Wrap(outputToken(ctx, c.stdout, c.format, client, bot, token)) } // There's not much to do in this case, but we can validate the token. @@ -783,7 +787,7 @@ func (c *BotsCommand) AddBotInstance(ctx context.Context, client botsCommandClie c.tokenID, token.GetBotName(), c.botName) } - return trace.Wrap(outputToken(c.stdout, c.format, client, bot, token)) + return trace.Wrap(outputToken(ctx, c.stdout, c.format, client, bot, token)) } var showMessageTemplate = template.Must(template.New("show").Funcs(template.FuncMap{ @@ -876,7 +880,7 @@ type botJSONResponse struct { } // outputToken writes token information to stdout, depending on the token format. -func outputToken(wr io.Writer, format string, client botsCommandClient, bot *machineidv1pb.Bot, token types.ProvisionToken) error { +func outputToken(ctx context.Context, wr io.Writer, format string, client botsCommandClient, bot *machineidv1pb.Bot, token types.ProvisionToken) error { if format == teleport.JSON { tokenTTL := time.Duration(0) if exp := token.Expiry(); !exp.IsZero() { @@ -900,7 +904,10 @@ func outputToken(wr io.Writer, format string, client botsCommandClient, bot *mac return nil } - proxies, err := client.GetProxies() + proxies, err := clientutils.CollectWithFallback(ctx, client.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return client.GetProxies() + }) if err != nil { return trace.Wrap(err) } diff --git a/tool/tctl/common/node_command.go b/tool/tctl/common/node_command.go index 0be12463b8184..dd5c21c0c70c9 100644 --- a/tool/tctl/common/node_command.go +++ b/tool/tctl/common/node_command.go @@ -36,6 +36,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/lib/auth/authclient" libclient "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/defaults" @@ -185,7 +186,12 @@ func (c *NodeCommand) Invite(ctx context.Context, client *authclient.Client) err return trace.Wrap(err) } - authServers, err := client.GetAuthServers() + authServers, err := clientutils.CollectWithFallback( + ctx, + client.ListAuthServers, + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + func(context.Context) ([]types.Server, error) { return client.GetAuthServers() }, + ) if err != nil { return trace.Wrap(err) } @@ -206,7 +212,10 @@ func (c *NodeCommand) Invite(ctx context.Context, client *authclient.Client) err } if err == nil && pingResponse.GetServerFeatures().Cloud { - proxies, err := client.GetProxies() + proxies, err := clientutils.CollectWithFallback(ctx, client.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return client.GetProxies() + }) if err != nil { return trace.Wrap(err) } diff --git a/tool/tctl/common/proxy_command.go b/tool/tctl/common/proxy_command.go index cd8f868fa77a1..fcb7bda7d1493 100644 --- a/tool/tctl/common/proxy_command.go +++ b/tool/tctl/common/proxy_command.go @@ -26,6 +26,8 @@ import ( "github.com/gravitational/trace" "github.com/gravitational/teleport" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/service/servicecfg" commonclient "github.com/gravitational/teleport/tool/tctl/common/client" @@ -52,7 +54,10 @@ func (p *ProxyCommand) Initialize(app *kingpin.Application, _ *tctlcfg.GlobalCLI // ListProxies prints currently connected proxies func (p *ProxyCommand) ListProxies(ctx context.Context, clusterAPI *authclient.Client) error { - proxies, err := clusterAPI.GetProxies() + proxies, err := clientutils.CollectWithFallback(ctx, clusterAPI.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return clusterAPI.GetProxies() + }) if err != nil { return trace.Wrap(err) } diff --git a/tool/tctl/common/resource_command.go b/tool/tctl/common/resource_command.go index 9b73740ab167a..9e3159fb34a4c 100644 --- a/tool/tctl/common/resource_command.go +++ b/tool/tctl/common/resource_command.go @@ -2641,6 +2641,7 @@ func (rc *ResourceCommand) getCollection(ctx context.Context, client *authclient return &collection, nil case types.KindAuthServer: + //nolint:staticcheck // TODO(kiosion): DELETE IN 21.0.0 servers, err := client.GetAuthServers() if err != nil { return nil, trace.Wrap(err) @@ -2655,6 +2656,7 @@ func (rc *ResourceCommand) getCollection(ctx context.Context, client *authclient } return nil, trace.NotFound("auth server with ID %q not found", rc.ref.Name) case types.KindProxy: + //nolint:staticcheck // TODO(kiosion): DELETE IN 21.0.0 servers, err := client.GetProxies() if err != nil { return nil, trace.Wrap(err) diff --git a/tool/tctl/common/token_command.go b/tool/tctl/common/token_command.go index 29b2fc0818253..d6b0c4fcdcf4f 100644 --- a/tool/tctl/common/token_command.go +++ b/tool/tctl/common/token_command.go @@ -981,7 +981,12 @@ func showJoinInstructions(ctx context.Context, in joinInstructionsInput) error { } // Get list of auth servers. Used to print friendly signup message. - authServers, err := in.client.GetAuthServers() + authServers, err := clientutils.CollectWithFallback( + ctx, + in.client.ListAuthServers, + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + func(context.Context) ([]types.Server, error) { return in.client.GetAuthServers() }, + ) if err != nil { return trace.Wrap(err) } @@ -992,7 +997,10 @@ func showJoinInstructions(ctx context.Context, in joinInstructionsInput) error { // Print signup message. switch { case in.roles.Include(types.RoleKube): - proxies, err := in.client.GetProxies() + proxies, err := clientutils.CollectWithFallback(ctx, in.client.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return in.client.GetProxies() + }) if err != nil { return trace.Wrap(err) } @@ -1009,7 +1017,10 @@ func showJoinInstructions(ctx context.Context, in joinInstructionsInput) error { "version": proxies[0].GetTeleportVersion(), }) case in.roles.Include(types.RoleApp): - proxies, err := in.client.GetProxies() + proxies, err := clientutils.CollectWithFallback(ctx, in.client.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return in.client.GetProxies() + }) if err != nil { return trace.Wrap(err) } @@ -1029,7 +1040,10 @@ func showJoinInstructions(ctx context.Context, in joinInstructionsInput) error { "app_public_addr": appPublicAddr, }) case in.roles.Include(types.RoleDatabase): - proxies, err := in.client.GetProxies() + proxies, err := clientutils.CollectWithFallback(ctx, in.client.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return in.client.GetProxies() + }) if err != nil { return trace.Wrap(err) } @@ -1071,7 +1085,10 @@ func showJoinInstructions(ctx context.Context, in joinInstructionsInput) error { } if err == nil && pingResponse.GetServerFeatures().Cloud { - proxies, err := in.client.GetProxies() + proxies, err := clientutils.CollectWithFallback(ctx, in.client.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return in.client.GetProxies() + }) if err != nil { return trace.Wrap(err) } diff --git a/tool/tctl/sso/configure/github.go b/tool/tctl/sso/configure/github.go index 09287ea6ebf7b..5bc7c2b427380 100644 --- a/tool/tctl/sso/configure/github.go +++ b/tool/tctl/sso/configure/github.go @@ -28,6 +28,7 @@ import ( "github.com/gravitational/trace" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/utils" ) @@ -119,7 +120,10 @@ func ResolveCallbackURL(ctx context.Context, logger *slog.Logger, clt *authclien var callbackURL string logger.InfoContext(ctx, "resolving callback url automatically", "field_name", fieldName) - proxies, err := clt.GetProxies() + proxies, err := clientutils.CollectWithFallback(ctx, clt.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return clt.GetProxies() + }) if err != nil { logger.ErrorContext(ctx, "unable to get proxy list", "error", err) } diff --git a/tool/tctl/sso/tester/command.go b/tool/tctl/sso/tester/command.go index 654d3b12531f1..4da75702c1dfb 100644 --- a/tool/tctl/sso/tester/command.go +++ b/tool/tctl/sso/tester/command.go @@ -32,6 +32,7 @@ import ( kyaml "k8s.io/apimachinery/pkg/util/yaml" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/clientutils" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/client" @@ -186,7 +187,10 @@ type AuthRequestInfo struct { type SSOLoginConsoleRequestFn func(req client.SSOLoginConsoleReq) (*client.SSOLoginConsoleResponse, error) func (cmd *SSOTestCommand) runSSOLoginFlow(ctx context.Context, connectorType string, c *authclient.Client, initiateSSOLoginFn SSOLoginConsoleRequestFn) (*authclient.SSHLoginResponse, error) { - proxies, err := c.GetProxies() + proxies, err := clientutils.CollectWithFallback(ctx, c.ListProxyServers, func(context.Context) ([]types.Server, error) { + //nolint:staticcheck // TODO(kiosion) DELETE IN 21.0.0 + return c.GetProxies() + }) if err != nil { return nil, trace.Wrap(err) }