Skip to content
This repository was archived by the owner on Feb 8, 2024. It is now read-only.

Connect: Implement tshd event handlers for db cert renewal#1383

Merged
ravicious merged 15 commits intomasterfrom
ravicious/expired-db-certs
Dec 6, 2022
Merged

Connect: Implement tshd event handlers for db cert renewal#1383
ravicious merged 15 commits intomasterfrom
ravicious/expired-db-certs

Conversation

@ravicious
Copy link
Copy Markdown
Member

teleport counterpart: gravitational/teleport#17950

Why

If the user attempts to make a connection through a local db proxy but the db & user certs have expired, we want Connect to bring focus to the Connect app, let the user relogin and then proxy the connection.

This is how the end result looks like:

connect-cert-renewal.mov

How

The backend flow is described in gravitational/teleport#17950. When it comes to the Electron app, tshd needs to be able to ask the Electron app for two things:

  • To ask the user to log in again.
  • To forward errors from goroutines running gateways to the Electron app in form of a notification. Otherwise those error would be visible only in the logs.

This is done through tshd events sent from tshd.

Important & regular modals

It's possible that while a db client attempts to make a connection, the user has a modal open in the app, for example to log in to a new cluster or whatever modal we might add in the future.

To avoid discarding the state from such modal, this PR introduces the concept of important modals. The basic idea is that we can have one regular and one important modal being rendered at the same time. The important modal is rendered over the regular modal, so if the user finishes the relogin process they can continue interacting with the old modal.

Opening an important modal should be reserved for situations where the interaction with the app happens outside of its UI and requires us to interrupt the user and show them a modal.

Structured object vs strings

Both tshd event handlers need to show a specific message to the user. Instead of passing a string from tshd to the Electron app, we pass a structured RPC message. This way the Electron app can decide how exactly the message should look like.

When tshd is sending the relogin event, it needs to be able to know when
the Electron app has finished relogging the user. I wanted to implement
this by simply waiting for the response from the RPC.

I'm so glad we did not use gRPC streams for tshd events as this would be
much harder to implement with streams.
With the introduction of important and regular modals, this will help us
close the specific dialog if need arises.
This will be useful in the upcoming commits. Basically, tshd is going to
ask the Electron app to relogin the user, with a 1 minute timeout.
The Electron app will show a login modal but if the user doesn't submit
it within 1 minute, tshd is going to cancel the request.

In that situation, we need to be able to close the modal.
This will let us show the relogin modal on expired cert, even if the user
was using some other modal at that moment.
The user can read more by expanding the notification. The title attribute
persisted even after expanding the notification, making reading it harder.
The next commit is going to add a private method to AppContext.
IAppContext is an interface which enables us to pass a mocked version of
AppContext in tests. That mock is not going have that private method,
so any place accepting AppContext wouldn't be able to accept the mocked
AppContext.

Instead, classes & functions should accept IAppContext rather than AppContext.
tshd needs to be able to do two things:

* Ask the user to log in again.
* Forward errors from goroutines running gateways to the Electron app
  in form of a notification. Otherwise those error would be visible
  only in the logs.
Restarting the gateways on login was a workaround from times where gateways
didn't manage their own certs.

In the new flow, a gateway takes care of refreshing the certs itself
through the middleware passed to alpnproxy.LocalProxy.

---

syncRootClusterAndRestartClusterGatewaysAndCatchErrors used to call two
functions:

* syncRootClusterAndCatchErrors
* restartClusterGatewaysAndCatchErrors

The second function is no longer necessary, so we can make any place that
was calling syncRootClusterAndRestartClusterGatewaysAndCatchErrors
call just syncRootClusterAndCatchErrors instead.
Comment thread packages/teleterm/src/types.ts Outdated
* @param {Object} eventData.request - The request payload converted to a simple JS object.
* @param {subscribeToTshdEventListenerOnCancelled} eventData.onCancelled - A function which lets
* you register a callback to be called when the call gets cancelled by the client.
*/
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.

idk, apparently this is the way to use JSDoc in cases like this?

https://jsdoc.app/tags-callback.html

It looks very messy and doesn't actually show the additional docs on symbol lookup.

Maybe I'm doing something wrong, but I think I might move all docs into a single comment… somehow.

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.

TBH I would only document params in SubscribeToTshdEvent (in src/types), however I still don't see any additional docs 🤔.

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.

TBH I would only document params in SubscribeToTshdEvent (in src/types), however I still don't see any additional docs 🤔.

You mean that even if you move all docs to a single comment, you still don't see docs if you look up an individual argument such as onCancelled?

I wonder if this is something that would work correctly if we actually generated HTML docs and the reason it's not shown here is simply a limitation of the language server.

Anyway, if the docs for individual arguments don't work, then having all docs in one comment would be much easier to read.

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.

Ah, the docs were visible when all of this was still a proof of concept and almost everything was inline within AppContext. The docs were shown because I added them to the subscribeToTshdEvent property of ElectronGlobals. Now that it's a property of AppContext, the docs are lost. I'll see what I can do about this.

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 suppose this is related to this issue: microsoft/TypeScript#51121

There doesn't even seem to be a way to reuse a single typedef across multiple sites, e.g. having the same jsdoc for appContext.subscribeToTshdEvent and ElectronGlobals.subscribeToTshdEvent. I'm going to move the comment to appContext.subscribeToTshdEvent since this is where the function will be actually used so the comment will be most helpful there.

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 mean that even if you move all docs to a single comment, you still don't see docs if you look up an individual argument such as onCancelled?

Hmm, I think so.

I'm unsure about documenting eventName and listener in AppContext. I would do it in the place where they are defined (src/types). I bet that after changing them, no one would think about updating the comment in AppContext.

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'm unsure about documenting eventName and listener in AppContext. I would do it in the place where they are defined (src/types). I bet that after changing them, no one would think about updating the comment in AppContext.

Good point, I'm going to move them to src/types.

@ravicious ravicious force-pushed the ravicious/expired-db-certs branch from 388842d to 5fc77df Compare November 25, 2022 18:14
@ravicious ravicious marked this pull request as ready for review November 25, 2022 18:14
@ravicious ravicious requested review from avatus and gzdunek November 25, 2022 18:15
Copy link
Copy Markdown
Contributor

@gzdunek gzdunek left a comment

Choose a reason for hiding this comment

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

First pass, looks really good :)

Comment thread packages/teleterm/src/ui/services/modals/modalsService.ts Outdated
Comment thread packages/teleterm/src/types.ts Outdated
* @param {Object} eventData.request - The request payload converted to a simple JS object.
* @param {subscribeToTshdEventListenerOnCancelled} eventData.onCancelled - A function which lets
* you register a callback to be called when the call gets cancelled by the client.
*/
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.

TBH I would only document params in SubscribeToTshdEvent (in src/types), however I still don't see any additional docs 🤔.

Comment thread packages/teleterm/src/ui/services/modals/modalsService.ts Outdated
Comment thread packages/teleterm/src/ui/appContext.ts
Comment thread packages/teleterm/src/ui/types.ts Outdated
Copy link
Copy Markdown
Contributor

@gzdunek gzdunek left a comment

Choose a reason for hiding this comment

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

Great work, it is really cool feature!

I'm only wondering if one minute timeout is not too strict? I would consider increasing it to maybe 3-5 minutes.

@ravicious
Copy link
Copy Markdown
Member Author

I'm only wondering if one minute timeout is not too strict? I would consider increasing it to maybe 3-5 minutes.

I think increasing it wouldn't help much here. pgAdmin's default connection timeout is 10 seconds. SSMS has a timeout of 15 seconds.

Of course the user is able to change the timeout (at least pgAdmin lets you do that). But in general I'd say the user is supposed to see the modal soon after the db client tries to make the connection and then they should relogin. If they don't manage to do so, then that's too bad but we'll show display a notification.

Also, unfortunately right now the modal will not disappear if the db client drops the connection, see gravitational/teleport#17950 (comment).

Copy link
Copy Markdown
Contributor

@avatus avatus left a comment

Choose a reason for hiding this comment

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

I spent a good two hours testing against teleport-11-ent.asteroid.earth and I wasn't getting any popup in Connect. Only errors in pgAdmin saying the connection had been terminated. I had a detailed list of reproduction steps, a video and everything only to then realize I hadn't built the tsh counterpart to this PR 🤦

This works great and is REALLY dope!!

@ravicious ravicious merged commit 99b218d into master Dec 6, 2022
@ravicious ravicious deleted the ravicious/expired-db-certs branch December 6, 2022 11:32
ravicious added a commit that referenced this pull request Dec 6, 2022
* tshd events: Wait for listeners before responding

When tshd is sending the relogin event, it needs to be able to know when
the Electron app has finished relogging the user. I wanted to implement
this by simply waiting for the response from the RPC.

I'm so glad we did not use gRPC streams for tshd events as this would be
much harder to implement with streams.

* Return a function from ModalsService.openDialog which closes dialog

With the introduction of important and regular modals, this will help us
close the specific dialog if need arises.

* Make tshd events listeners aware of request cancellation

This will be useful in the upcoming commits. Basically, tshd is going to
ask the Electron app to relogin the user, with a 1 minute timeout.
The Electron app will show a login modal but if the user doesn't submit
it within 1 minute, tshd is going to cancel the request.

In that situation, we need to be able to close the modal.

* Add support for passing reason in DialogClusterConnect

* Add support for important modals

This will let us show the relogin modal on expired cert, even if the user
was using some other modal at that moment.

* Remove title attr from notification text

The user can read more by expanding the notification. The title attribute
persisted even after expanding the notification, making reading it harder.

* Add WindowsManager.forceFocusWindow

* Use IAppContext instead of AppContext

The next commit is going to add a private method to AppContext.
IAppContext is an interface which enables us to pass a mocked version of
AppContext in tests. That mock is not going have that private method,
so any place accepting AppContext wouldn't be able to accept the mocked
AppContext.

Instead, classes & functions should accept IAppContext rather than AppContext.

* Implement handlers for new tshd events

tshd needs to be able to do two things:

- Ask the user to log in again.
- Forward errors from goroutines running gateways to the Electron app
  in form of a notification. Otherwise those error would be visible
  only in the logs.

* Don't restart gateways after logging in

Restarting the gateways on login was a workaround from times where gateways
didn't manage their own certs.

In the new flow, a gateway takes care of refreshing the certs itself
through the middleware passed to alpnproxy.LocalProxy.

syncRootClusterAndRestartClusterGatewaysAndCatchErrors used to call two
functions:

- syncRootClusterAndCatchErrors
- restartClusterGatewaysAndCatchErrors

The second function is no longer necessary, so we can make any place that
was calling syncRootClusterAndRestartClusterGatewaysAndCatchErrors
call just syncRootClusterAndCatchErrors instead.
ravicious added a commit that referenced this pull request Dec 12, 2022
…1416)

* tshd events: Wait for listeners before responding

When tshd is sending the relogin event, it needs to be able to know when
the Electron app has finished relogging the user. I wanted to implement
this by simply waiting for the response from the RPC.

I'm so glad we did not use gRPC streams for tshd events as this would be
much harder to implement with streams.

* Return a function from ModalsService.openDialog which closes dialog

With the introduction of important and regular modals, this will help us
close the specific dialog if need arises.

* Make tshd events listeners aware of request cancellation

This will be useful in the upcoming commits. Basically, tshd is going to
ask the Electron app to relogin the user, with a 1 minute timeout.
The Electron app will show a login modal but if the user doesn't submit
it within 1 minute, tshd is going to cancel the request.

In that situation, we need to be able to close the modal.

* Add support for passing reason in DialogClusterConnect

* Add support for important modals

This will let us show the relogin modal on expired cert, even if the user
was using some other modal at that moment.

* Remove title attr from notification text

The user can read more by expanding the notification. The title attribute
persisted even after expanding the notification, making reading it harder.

* Add WindowsManager.forceFocusWindow

* Use IAppContext instead of AppContext

The next commit is going to add a private method to AppContext.
IAppContext is an interface which enables us to pass a mocked version of
AppContext in tests. That mock is not going have that private method,
so any place accepting AppContext wouldn't be able to accept the mocked
AppContext.

Instead, classes & functions should accept IAppContext rather than AppContext.

* Implement handlers for new tshd events

tshd needs to be able to do two things:

- Ask the user to log in again.
- Forward errors from goroutines running gateways to the Electron app
  in form of a notification. Otherwise those error would be visible
  only in the logs.

* Don't restart gateways after logging in

Restarting the gateways on login was a workaround from times where gateways
didn't manage their own certs.

In the new flow, a gateway takes care of refreshing the certs itself
through the middleware passed to alpnproxy.LocalProxy.

syncRootClusterAndRestartClusterGatewaysAndCatchErrors used to call two
functions:

- syncRootClusterAndCatchErrors
- restartClusterGatewaysAndCatchErrors

The second function is no longer necessary, so we can make any place that
was calling syncRootClusterAndRestartClusterGatewaysAndCatchErrors
call just syncRootClusterAndCatchErrors instead.
ravicious added a commit to gravitational/teleport that referenced this pull request Feb 8, 2023
The Electron app no longer asks tshd to restart gateways. This was changed
in gravitational/webapps#1383.

This PR merely removes the tshd implementation of that RPC.
ravicious added a commit to gravitational/teleport that referenced this pull request Feb 9, 2023
The Electron app no longer asks tshd to restart gateways. This was changed
in gravitational/webapps#1383.

This PR merely removes the tshd implementation of that RPC.
github-actions Bot pushed a commit to gravitational/teleport that referenced this pull request Feb 9, 2023
The Electron app no longer asks tshd to restart gateways. This was changed
in gravitational/webapps#1383.

This PR merely removes the tshd implementation of that RPC.
ravicious added a commit to gravitational/teleport that referenced this pull request Feb 14, 2023
The Electron app no longer asks tshd to restart gateways. This was changed
in gravitational/webapps#1383.

This PR merely removes the tshd implementation of that RPC.
ravicious added a commit to gravitational/teleport that referenced this pull request Feb 14, 2023
The Electron app no longer asks tshd to restart gateways. This was changed
in gravitational/webapps#1383.

This PR merely removes the tshd implementation of that RPC.
ravicious added a commit to gravitational/teleport that referenced this pull request Feb 14, 2023
)

The Electron app no longer asks tshd to restart gateways. This was changed
in gravitational/webapps#1383.

This PR merely removes the tshd implementation of that RPC.
avatus pushed a commit to gravitational/teleport that referenced this pull request Mar 3, 2023
The Electron app no longer asks tshd to restart gateways. This was changed
in gravitational/webapps#1383.

This PR merely removes the tshd implementation of that RPC.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants