-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle duplicate node names & support UUID based proxying. #3340
Conversation
5aa69eb
to
dcd84d9
Compare
lib/srv/regular/proxy.go
Outdated
for i := range servers { | ||
// If nodeId is supplied, we must unambiguously match |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking this logic where we loop over servers with all the breaks and continues is getting too unwieldy due to all the special cases we now have. What do you think about breaking it up? So something like:
var servers services.Server
if t.nodeID != "" {
servers = getServersByUUID(...)
} else {
servers = getServers(...)
}
// If multiple servers matched, return an error that the client
can use to present a "server-is-ambiguous" error.
if len(servers) > 1 {
return trace.NotFound("err-server-is-ambiguous")
}
return fmt.Sprintf("%s:%s", p.Host, port) | ||
} | ||
|
||
func (p *ProxyParams) Encode() string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The more I think about it, I don't think we should try to support connecting via UUID and host:port
to our current proxy subsystem.
The current system is designed around connecting via host:port
which is 99% of the use-cases and is going to really be the only one that works well with OpenSSH (where URL encoding probably won't be supported) and not something we can drop support for.
All the special casing to support UUID seems to indicate we're going down the wrong path.
What do you think about this: we leave the current proxy subsystem as-is and we add another subsystem proxy-uuid
. When tsh connects via UUID it just requests the proxy-uuid
subsystem. Parsing the old subsystem won't change at all and parsing the UUID subsystem should be much simpler.
What do you think? Would that help simplify the codebase?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm honestly not sure... there are pros and cons to both.
In the short term, keeping the old subsystem separate is probably the easier choice. Definitely reduces the chances of accidentally introducing a regression.
Long-term, separate subsystems feels like more maintenance and more places for tests to accidentally miss an edge case. In terms of openssh
, this new format seems just as compatible. Either way, the user is manually encoding the subsystem call (see original comment).
@klizhentas any thoughts on this?
84777c8
to
6cc2300
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some largely minor comments, overall looks good, let's discuss tomorrow.
uuid string | ||
} | ||
|
||
func (t targetNode) String() string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// String returns either the UUID or host:port for the target host. UUID
// is given priority if provided.
if err != nil { | ||
return nil, trace.Wrap(err) | ||
} | ||
// TODO(fspmarshall): Transition to using UUID here once |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a similar comment to what we have for the web proxy.
// DELETE IN: 5.0
//
// This fallback will be unnecessary once all proxies support lookup
// by UUID.
} | ||
} | ||
if len(nodes) == 0 { | ||
if tc.HostUUID != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Client is attempting to connect via UUID.
if err != nil { | ||
return trace.Wrap(err) | ||
} | ||
// TODO(fspmarshall): Upgrade this to use node uuid instead once |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a similar comment to what we have for the web proxy.
// DELETE IN: 5.0
//
// This fallback will be unnecessary once all proxies support lookup
// by UUID.
@@ -1411,13 +1457,24 @@ func (tc *TeleportClient) ListNodes(ctx context.Context) ([]services.Server, err | |||
return proxyClient.FindServersByLabels(ctx, tc.Namespace, tc.Labels) | |||
} | |||
|
|||
// ListAllNodes is the same as ListNodes except that it ignores labels. | |||
func (tc *TeleportClient) ListAllNodes(ctx context.Context) ([]services.Server, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this being used? Can't see it being called by anyone?
@@ -250,6 +250,197 @@ func SplitHostPort(hostname string) (string, string, error) { | |||
return host, port, nil | |||
} | |||
|
|||
type ProxyParams struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Struct and all members need comments.
return net.JoinHostPort(host, port) | ||
} | ||
|
||
func (p *ProxyParams) Encode() string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Encode takes the parameters given and creates the proxy subsystem string.
// For connections using host:port that means it returns something like
// "server:3022@default@example.com". For connections using UUID, it creates
// something like "conn(uuid=00000000-0000-0000-0000-000000000000&...)".
return EncodeConnParams(cp) | ||
} | ||
|
||
func (p *ProxyParams) legacyEncode() string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// legacyEncode transforms the given parameters into the proxy format of
// "host:port@namespace@clusterName".
paramNodeID = "uuid" | ||
) | ||
|
||
func ParseProxyParams(request string) (ProxyParams, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// ParseProxyParams parses the proxy subsystem request string. Supports
// standard as well as URL encoded proxy subsystem request schemes.
return params, nil | ||
} | ||
|
||
func parseLegacyProxyParams(request string) (ProxyParams, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// parseLegacyProxyParams will parse the classic proxy subsystem request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a comment about tctl vs tsh, and comment about how much work to expand this to SCP
} | ||
fmt.Fprintf(os.Stderr, "error: ambiguous host could match multiple nodes\n\n") | ||
showNodes(nodes, true) | ||
fmt.Fprintf(os.Stderr, "Hint: try addressing the node by unique id (ex: tctl ssh -U user@node-id)\n") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to change this from (ex: tctl ssh -U user@node-id)\n")
to (ex: tsh ssh -U user@node-id)\n")
( also note to self to add this to CLI docs after ) |
Feedback DiscussionI've gathered a few rounds of feedback via various channels. I think its time to summarize the various questions/concerns/comments I've come across, my current thinking on them, and a proposal for where to go from here. In no particular order...
Because users may (and do) have multiple nodes that share the same hostname, rejecting duplicate hostnames would break existing deployments. Furthermore, rejecting duplicate hostnames poses too much of an issue when dealing with things like replication, Blue/Green deploys, rotations, etc... There are perfectly legitimate reasons to have multiple nodes with the same hostname.
If we take the approach of simply checking if the
Agreed. If we go this route, a syntax which doesn't require escapes (at least in the trivial case) would be preferable. One simple change is to move from
I'm of the opinion that the Because clusters need to be specified by certificate when using Requiring certificate-level parameters to resolve ambiguities also increases the likelihood of human error. Say Alice is working on node The root issue here stems from the fact that
Seems reasonable. There are pros and cons to both approaches, but adding a new subsystem definitely cuts down on churn. This does open up the possibility of side-stepping the issue of encoding by simply giving up on the idea of supporting key-value pairs. A dedicated subsystem for
This is technically possible, but cumbersome to use with Revised ProposalLeave the existing Create a new subsystem named
The verbose encoding will be used by For all future subsystems moving forward, ensure that they accept a key-value pair encoding. |
@benarent @fspmarshall @russjones still seems like a solution that works, but at a very high cost of adding extra subsystem, deprecating the old subsystem and not supporting |
@fspmarshall this argument is also valid for any Continue to brainstorming, so still I can't understand why the following can't work:
|
Yes, and this has been on the “we should fix this” list for months. Several people requested that we improve the |
Closing this in favor of #3377. |
This PR consists of two parts. First, attempting to ssh into an ambiguous target results in an error. Second, nodes can now be unambiguously connected to via UUID. Ex:
In order to implement the above, a new format for the proxy subsystem was added:
Fixes #2396