Skip to content

Commit

Permalink
Update tbot proxy to use the new host resolution RPC
Browse files Browse the repository at this point in the history
  • Loading branch information
rosstimothy committed Dec 16, 2024
1 parent fb2bd26 commit b85e58f
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 45 deletions.
43 changes: 25 additions & 18 deletions lib/client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1609,11 +1609,19 @@ func (tc *TeleportClient) GetTargetNode(ctx context.Context, clt authclient.Clie
return nil, trace.Wrap(err)
}

if len(resources) == 0 {
switch len(resources) {
case 0:
return nil, trace.NotFound("no matching SSH hosts found for search terms or query expression")
}

if len(resources) > 1 {
case 1:
node, ok := resources[0].ResourceWithLabels.(*types.ServerV2)
if !ok {
return nil, trace.BadParameter("expected node resource, got %T", resources[0].ResourceWithLabels)
}
return &TargetNode{
Hostname: node.GetHostname(),
Addr: node.GetName() + ":0",
}, nil
default:
// If routing does not allow choosing the most recent host, then abort with
// an ambiguous host error.
cnc, err := clt.GetClusterNetworkingConfig(ctx)
Expand All @@ -1626,22 +1634,21 @@ func (tc *TeleportClient) GetTargetNode(ctx context.Context, clt authclient.Clie
return a.Expiry().Compare(b.Expiry())
})

}
// Sorting above is oldest expiry to newest expiry, so proceed
// with the last item server in the slice.
server, ok := resources[len(resources)-1].ResourceWithLabels.(types.Server)
if !ok {
return nil, trace.BadParameter("received unexpected resource type %T", resources[0].ResourceWithLabels)
}

// Sorting above is oldest expiry to newest expiry, so proceed
// with the last item server in the slice.
server, ok := resources[len(resources)-1].ResourceWithLabels.(types.Server)
if !ok {
return nil, trace.BadParameter("received unexpected resource type %T", resources[0].ResourceWithLabels)
// Dialing is happening by UUID but a port is still required by
// the Proxy dial request. Zero is an indicator to the Proxy that
// it may chose the appropriate port based on the target server.
return &TargetNode{
Hostname: server.GetHostname(),
Addr: server.GetName() + ":0",
}, nil
}

// Dialing is happening by UUID but a port is still required by
// the Proxy dial request. Zero is an indicator to the Proxy that
// it may chose the appropriate port based on the target server.
return &TargetNode{
Hostname: server.GetHostname(),
Addr: server.GetName() + ":0",
}, nil
case err == nil:
if resp.GetServer() == nil {
return nil, trace.NotFound("no matching SSH hosts found")
Expand Down
2 changes: 1 addition & 1 deletion lib/tbot/service_ssh_multiplexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ func (s *SSHMultiplexerService) handleConn(
host = cleanTargetHost(host, proxyHost, clusterName)
target = net.JoinHostPort(host, port)
} else {
node, err := resolveTargetHostWithClient(ctx, authClient, expanded.Search, expanded.Query)
node, err := resolveTargetHostWithClient(ctx, authClient.APIClient, expanded.Search, expanded.Query)
if err != nil {
return trace.Wrap(err, "resolving target host")
}
Expand Down
76 changes: 50 additions & 26 deletions lib/tbot/ssh_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"log/slog"
"net"
"path/filepath"
"slices"
"strings"

"github.com/gravitational/trace"
Expand Down Expand Up @@ -220,38 +221,61 @@ func resolveTargetHost(ctx context.Context, cfg client.Config, search, query str
// resolveTargetHostWithClient resolves the target host using the provided
// client and search and query parameters.
func resolveTargetHostWithClient(
ctx context.Context, clt client.ListUnifiedResourcesClient, search, query string,
ctx context.Context, clt *client.Client, search, query string,
) (types.Server, error) {
resources, _, err := client.GetUnifiedResourcePage(ctx, clt, &proto.ListUnifiedResourcesRequest{
// We only want a single node, but, we set limit=2 so we can throw a
// helpful error when multiple match. In the happy path, where a single
// node matches, this does not degrade performance because even if
// limit=1 the UnifiedResource cache will still iterate to the end to
// determine if there is a NextKey to return.
Limit: 2,
Kinds: []string{types.KindNode},
resp, err := clt.ResolveSSHTarget(ctx, &proto.ResolveSSHTargetRequest{
SearchKeywords: libclient.ParseSearchKeywords(search, ','),
PredicateExpression: query,
SortBy: types.SortBy{Field: types.ResourceKind},
})
if err != nil {
return nil, trace.Wrap(err)
}
if len(resources) == 0 {
return nil, trace.NotFound("no matching SSH hosts found for search terms or query expression")
}
if len(resources) > 1 {
names := make([]string, len(resources))
for i, res := range resources {
names[i] = res.GetName()
switch {
//TODO(tross): DELETE IN v20.0.0
case trace.IsNotImplemented(err):
resources, err := client.GetAllUnifiedResources(ctx, clt, &proto.ListUnifiedResourcesRequest{
Kinds: []string{types.KindNode},
SearchKeywords: libclient.ParseSearchKeywords(search, ','),
PredicateExpression: query,
SortBy: types.SortBy{Field: types.ResourceMetadataName},
})
if err != nil {
return nil, trace.Wrap(err)
}
return nil, trace.BadParameter("found multiple matching SSH hosts %v", names)
}
node := resources[0].ResourceWithLabels.(*types.ServerV2)
if node == nil {
return nil, trace.BadParameter("expected node resource, got %T", resources[0].ResourceWithLabels)

switch len(resources) {
case 0:
return nil, trace.NotFound("no matching SSH hosts found for search terms or query expression")
case 1:
node, ok := resources[0].ResourceWithLabels.(*types.ServerV2)
if !ok {
return nil, trace.BadParameter("expected node resource, got %T", resources[0].ResourceWithLabels)
}
return node, nil
default:
// If routing does not allow choosing the most recent host, then abort with
// an ambiguous host error.
cnc, err := clt.GetClusterNetworkingConfig(ctx)
if err != nil || cnc.GetRoutingStrategy() != types.RoutingStrategy_MOST_RECENT {
return nil, trace.BadParameter("found multiple matching SSH hosts %v", resources[:2])
}

// Sort the resource by expiry so we can identify the most "recent".
slices.SortFunc(resources, func(a, b *types.EnrichedResource) int {
return a.Expiry().Compare(b.Expiry())
})

// Sorting above is oldest expiry to newest expiry, so proceed
// with the last item server in the slice.
server, ok := resources[len(resources)-1].ResourceWithLabels.(types.Server)
if !ok {
return nil, trace.BadParameter("received unexpected resource type %T", resources[0].ResourceWithLabels)
}

return server, nil
}
case err == nil:
return resp.GetServer(), nil
default:
return nil, trace.Wrap(err)
}
return node, nil
}

func parseIdentity(destPath, proxy, cluster string, insecure, fips bool) (*identity.Facade, agent.ExtendedAgent, error) {
Expand Down

0 comments on commit b85e58f

Please sign in to comment.