Skip to content

tsh proxy app: Add support for multi-port TCP apps#50429

Merged
ravicious merged 10 commits intomasterfrom
r7s/target-port-tsh
Jan 2, 2025
Merged

tsh proxy app: Add support for multi-port TCP apps#50429
ravicious merged 10 commits intomasterfrom
r7s/target-port-tsh

Conversation

@ravicious
Copy link
Copy Markdown
Member

@ravicious ravicious commented Dec 19, 2024

This PR adds support for multi-port apps to tsh proxy app, so that users on platforms where VNet is not available can still access those apps. The target port can be specified by appending :<target port to the --port flag, e.g., tsh proxy app foo --port 3000:8080 See the relevant section from the RFD.

I also added support for the --target-port flag to tsh app login, mostly just so that if for whatever reason someone needs a cert for a specific port, they can get one. However, tsh proxy app is not going to use that cert for multi-port apps. Doing so would require implementing a lot of logic around this. We'd need to consider both the target port passed through argv and the target port included in the cert. As tsh proxy app is not the recommended way to access multi-port apps, I made it so that when pointed at a multi-port app, tsh proxy app ignores the app cert on disk and instead always fetches a new one.

If the target port is skipped, the connection is routed to the first TCP port in the app spec. This is similar to a connection made through a client that doesn't know about TargetPort at all.

$ tsh proxy app multi-port-example --port 9090:8765
Proxying connections to multi-port-example:8765 on 127.0.0.1:9090

$ tsh app login multi-port-example --target-port 8765
Logged into TCP app multi-port-example:8765. Start the local TCP proxy for it:

  tsh proxy app multi-port-example

Then connect to the application through this proxy.

Arguably, the output of tsh app login could include something like --port 0:8765 in the proxy instructions. But I'm not sure if it's worth the effort, given that tsh app login is an obscure escape hatch rather than a recommended way of interacting with multi-port apps.

Another not so great thing is that because of the format of the port flag, if you want to start a proxy on a random port to a specific target port, you need to pass something like --port 0:8765. We could support --target-port on top of that, but I feel like it'd be another case of adding too much complexity to a niche use case.


changelog: Added support for multi-port TCP apps to tsh proxy app

Comment thread tool/tsh/common/app_local_proxy.go Outdated
// newLocalProxyAppWithPortMapping creates a new generic app proxy. Unlike newLocalProxyApp, it
// accepts a specific port mapping as an argument.
func newLocalProxyAppWithPortMapping(tc *client.TeleportClient, appInfo *appInfo, portMapping client.PortMapping, insecure bool) *localProxyApp {
func newLocalProxyAppWithPortMapping(ctx context.Context, tc *client.TeleportClient, appInfo *appInfo, app types.Application, portMapping client.PortMapping, insecure bool) (*localProxyApp, error) {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite understand the idea behind appInfo. In theory, it has a GetApp method which lazily fetches the app or returns it if it was already fetched. However, to execute that method the callsite needs to have access to api/client.GetResourcesClient. Because of that, I opted for passing types.Application in addition to appInfo.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, appInfo isn't really useful here. We just need proto.RouteToApp and profile from it for some
apps. I think we should remove it localProxyApp and add those two fields.

Alternatively, just set appInfo.app = app instead of adding a duplicate field.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I replaced appInfo on localProxyApp with profile and routeToApp.

@ravicious ravicious marked this pull request as ready for review December 19, 2024 10:09
@github-actions github-actions Bot added size/md tsh tsh - Teleport's command line tool for logging into nodes running Teleport. labels Dec 19, 2024
Comment thread lib/client/api.go Outdated
return PortMapping{}, nil
}

parts := strings.SplitN(rawPorts, ":", 2)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strings.Cut is almost always better than strings.SplitN(2) IMO.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh TIL, I didn't know about it. With strings.SplitN, I always forget if SplitN(1) or SplitN(2) does the equivalent of strings.Cut.

Comment on lines +161 to +162
// As tsh proxy app is not the recommended way to access multi-port apps, let's bail out of
// using the existing cert and instead always generate a new one.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will the new port-specific certificate get stored? If not, and multi-port certs are never supposed to be stored, should we delete the stale on-disk certificate?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Local proxies always keep generated certs in memory, so the new port-specific cert will not be stored on disk. Multi-port certs can be stored. This might be useful in a situation where someone cannot use neither VNet nor local proxies. I think there's no harm in not touching the existing cert that's on disk. It's also not necessarily stale, the user might be using it for some other purposes than setting up a local proxy.

Copy link
Copy Markdown
Contributor

@Joerger Joerger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only minor comments, Approving since I'll be OOO

// when running "ssh" commands with a tsh "ProxyCommand".
func mustLoginSetEnv(t *testing.T, s *suite, args ...string) (tshHome, kubeConfig string) {
tshHome, kubeConfig = mustLogin(t, s, args...)
// deprecated: Create a new helper that depends on mustLogin instead which requires migrating from
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// deprecated: Create a new helper that depends on mustLogin instead which requires migrating from
// TODO(ravicious): Create a new helper that depends on mustLogin instead which requires migrating from

Did you intend to follow up on this TODO in this PR or just leave a note?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just leave a note. testenv.MakeTestServer has been there for quite some time, so I just wanted to make sure that the next person who's going to copy paste some test structure for another tsh test is aware that functions that depend on newTestSuite are deprecated too.

@ravicious ravicious enabled auto-merge December 31, 2024 12:00
@zmb3
Copy link
Copy Markdown
Collaborator

zmb3 commented Jan 2, 2025

/excludeflake *

@ravicious ravicious added this pull request to the merge queue Jan 2, 2025
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jan 2, 2025
@ravicious ravicious added this pull request to the merge queue Jan 2, 2025
Merged via the queue into master with commit 1e2b257 Jan 2, 2025
@ravicious ravicious deleted the r7s/target-port-tsh branch January 2, 2025 16:29
@public-teleport-github-review-bot
Copy link
Copy Markdown

@ravicious See the table below for backport results.

Branch Result
branch/v17 Failed

carloscastrojumo pushed a commit to carloscastrojumo/teleport that referenced this pull request Feb 19, 2025
…0429)

* Pass around PortMapping rather than port as string

* Pass target port to local proxy

* Validate target port

* Accept target-port flag in tsh app login

* Don't reuse certs for multi-port apps

* Add a test for multi-port tsh proxy app

* Use strings.Cut instead of strings.SplitN

* Remove appInfo from localProxyApp

* Remove unused method appInfo.appLocalCAPath
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport/branch/v17 size/md tsh tsh - Teleport's command line tool for logging into nodes running Teleport.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants