Skip to content

Commit c64bb4c

Browse files
Add Scope Namespacing support for SSH Servers (Nodes) (#69213)
Consume New SSHServer funcs (#69215)
1 parent b887107 commit c64bb4c

49 files changed

Lines changed: 3207 additions & 655 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

api/client/client.go

Lines changed: 136 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2754,16 +2754,30 @@ func (c *Client) DeleteToken(ctx context.Context, name string) error {
27542754
return trace.Wrap(err)
27552755
}
27562756

2757-
// GetNode returns a node by name and namespace.
2757+
// GetNode returns an unscoped node by name and namespace.
2758+
//
2759+
// Deprecated: Use [Client.GetSSHServer] instead, which supports scoped nodes.
2760+
// TODO(williamo): Remove in v20
27582761
func (c *Client) GetNode(ctx context.Context, namespace, name string) (types.Server, error) {
2759-
resp, err := c.grpc.GetNode(ctx, &types.ResourceInNamespaceRequest{
2760-
Name: name,
2761-
Namespace: namespace,
2762-
})
2762+
return c.GetSSHServer(ctx, presencepb.GetSSHServerRequest_builder{Name: name}.Build())
2763+
}
2764+
2765+
// GetSSHServer returns a scoped or unscoped ssh servers by name.
2766+
func (c *Client) GetSSHServer(ctx context.Context, req *presencepb.GetSSHServerRequest) (types.Server, error) {
2767+
resp, err := c.PresenceServiceClient().GetSSHServer(ctx, req)
27632768
if err != nil {
2764-
return nil, trace.Wrap(err)
2769+
if !trace.IsNotImplemented(trace.Wrap(err)) {
2770+
return nil, trace.Wrap(err)
2771+
}
2772+
if req.GetScope() != "" {
2773+
return nil, trace.BadParameter("requesting a scoped node from an outdated Teleport control plane that does not support it")
2774+
}
2775+
return c.grpc.GetNode(ctx, &types.ResourceInNamespaceRequest{
2776+
Name: req.Name,
2777+
Namespace: defaults.Namespace,
2778+
})
27652779
}
2766-
return resp, nil
2780+
return resp.GetServer(), nil
27672781
}
27682782

27692783
// GetNodes returns a complete list of nodes that the user has access to in the given namespace.
@@ -2776,6 +2790,94 @@ func (c *Client) GetNodes(ctx context.Context, namespace string) ([]types.Server
27762790
return servers, trace.Wrap(err)
27772791
}
27782792

2793+
// ListSSHServers returns a page of registered ssh servers respecting scope filters.
2794+
func (c *Client) ListSSHServers(ctx context.Context, req *presencepb.ListSSHServersRequest) ([]types.Server, string, error) {
2795+
res, err := c.PresenceServiceClient().ListSSHServers(ctx, req)
2796+
if err != nil {
2797+
if !trace.IsNotImplemented(err) {
2798+
return nil, "", trace.Wrap(err)
2799+
}
2800+
2801+
// only allow fallback if the request is not expecting results to be scope filtered
2802+
if req.GetScopeFilter().GetScope() != "" {
2803+
return nil, "", trace.BadParameter("requesting list of scoped nodes from an outdated Teleport control plane that does not support it")
2804+
}
2805+
2806+
return c.listNodesFallback(ctx, int(req.GetPageSize()), req.GetPageToken())
2807+
}
2808+
2809+
servers := make([]types.Server, 0, len(res.GetServers()))
2810+
for _, server := range res.GetServers() {
2811+
servers = append(servers, server)
2812+
}
2813+
return servers, res.GetNextPageToken(), nil
2814+
}
2815+
2816+
func (c *Client) listNodesFallback(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) {
2817+
resp, err := c.ListResources(ctx, proto.ListResourcesRequest{
2818+
ResourceType: types.KindNode,
2819+
Namespace: defaults.Namespace,
2820+
Limit: int32(pageSize),
2821+
StartKey: pageToken,
2822+
})
2823+
if err != nil {
2824+
return nil, "", trace.Wrap(err)
2825+
}
2826+
2827+
servers := make([]types.Server, 0, len(resp.Resources))
2828+
for _, resource := range resp.Resources {
2829+
server, ok := resource.(types.Server)
2830+
if !ok {
2831+
return nil, "", trace.BadParameter("expected types.Server, got %T", resource)
2832+
}
2833+
servers = append(servers, server)
2834+
}
2835+
return servers, resp.NextKey, nil
2836+
}
2837+
2838+
// RangeSSHServers returns a sequence of ssh servers filtered by the given
2839+
// [*presencepb.ListSSHServersRequest].
2840+
func (c *Client) RangeSSHServers(ctx context.Context, req *presencepb.ListSSHServersRequest) iter.Seq2[types.Server, error] {
2841+
if req == nil {
2842+
req = presencepb.ListSSHServersRequest_builder{}.Build()
2843+
}
2844+
2845+
pageFn := func(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) {
2846+
req.SetPageToken(pageToken)
2847+
req.SetPageSize(int32(pageSize))
2848+
return c.ListSSHServers(ctx, req)
2849+
}
2850+
return func(yield func(cluster types.Server, err error) bool) {
2851+
var fallback bool
2852+
for cluster, err := range clientutils.RangeResources(ctx, req.GetPageToken(), "", pageFn, types.Server.GetName) {
2853+
if trace.IsNotImplemented(err) {
2854+
// if control plane does not support ListNodes, we should try to fallback to the
2855+
// ListResources API
2856+
fallback = true
2857+
break
2858+
}
2859+
if !yield(cluster, err) {
2860+
return
2861+
}
2862+
}
2863+
if !fallback {
2864+
return
2865+
}
2866+
if req.GetScopeFilter().GetScope() != "" {
2867+
// only allow fallback if the request is not expecting results to be scope filtered
2868+
yield(nil, trace.BadParameter("requesting range of scoped kube cluster from an outdated Teleport control plane that does not support it"))
2869+
return
2870+
}
2871+
// fallback iterator
2872+
//nolint:staticcheck // TODO(eriktate): deprecated, to be removed in v20
2873+
for cluster, err := range clientutils.RangeResources(ctx, req.GetPageToken(), "", c.listNodesFallback, nil) {
2874+
if !yield(cluster, err) {
2875+
return
2876+
}
2877+
}
2878+
}
2879+
}
2880+
27792881
// UpsertNode is used by SSH servers to report their presence
27802882
// to the auth servers in form of heartbeat expiring after ttl period.
27812883
func (c *Client) UpsertNode(ctx context.Context, node types.Server) (*types.KeepAlive, error) {
@@ -2793,19 +2895,40 @@ func (c *Client) UpsertNode(ctx context.Context, node types.Server) (*types.Keep
27932895
return keepAlive, nil
27942896
}
27952897

2796-
// DeleteNode deletes a node by name and namespace.
2898+
// DeleteNode deletes an unscoped node by name and namespace.
2899+
//
2900+
// Deprecated: Use [Client.DeleteSSHServer] instead, which supports scoped nodes.
2901+
// TODO(williamo): Remove in v20
27972902
func (c *Client) DeleteNode(ctx context.Context, namespace, name string) error {
27982903
if namespace == "" {
27992904
return trace.BadParameter("missing parameter namespace")
28002905
}
28012906
if name == "" {
28022907
return trace.BadParameter("missing parameter name")
28032908
}
2804-
_, err := c.grpc.DeleteNode(ctx, &types.ResourceInNamespaceRequest{
2805-
Name: name,
2806-
Namespace: namespace,
2807-
})
2808-
return trace.Wrap(err)
2909+
return trace.Wrap(c.DeleteSSHServer(ctx, presencepb.DeleteSSHServerRequest_builder{Name: name}.Build()))
2910+
}
2911+
2912+
// DeleteSSHServer deletes a scoped or unscoped ssh server by name.
2913+
func (c *Client) DeleteSSHServer(ctx context.Context, req *presencepb.DeleteSSHServerRequest) error {
2914+
_, err := c.PresenceServiceClient().DeleteSSHServer(ctx, req)
2915+
if err != nil {
2916+
if !trace.IsNotImplemented(trace.Wrap(err)) {
2917+
return trace.Wrap(err)
2918+
}
2919+
if req.GetScope() != "" {
2920+
return trace.BadParameter("requesting deletion of a scoped node from an outdated Teleport control plane that does not support it")
2921+
}
2922+
2923+
if _, err := c.grpc.DeleteNode(ctx, &types.ResourceInNamespaceRequest{
2924+
Namespace: defaults.Namespace,
2925+
Name: req.Name,
2926+
}); err != nil {
2927+
return trace.Wrap(err)
2928+
}
2929+
2930+
}
2931+
return nil
28092932
}
28102933

28112934
// DeleteAllNodes deletes all nodes in a given namespace.

api/gen/proto/go/teleport/presence/v1/service_grpc.pb.go

Lines changed: 122 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)