Skip to content
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

cli: put a lock around login #362

Merged
merged 8 commits into from
Oct 1, 2021
Merged

cli: put a lock around login #362

merged 8 commits into from
Oct 1, 2021

Conversation

mxyng
Copy link
Collaborator

@mxyng mxyng commented Sep 29, 2021

  • prevent too many prompts from showing up

Fixes #351

@mxyng mxyng changed the title put a lock around login cli: put a lock around login Sep 29, 2021
internal/cmd/cmd.go Outdated Show resolved Hide resolved
}

loginPid := filepath.Join(homeDir, ".infra", "login.pid")
if _, err := os.Stat(loginPid); err == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this mean other kubectl commands will block, or will they error? If they error clients like Infra App this may cause a strange situation where the first command works but follow up ones just error

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Subsequent commands should error. I don't have Infra App installed so don't know exactly how it would interact. I feel like erroring out would be the best case scenario. Blocking kubectl might have additional side effects as well.

Copy link
Collaborator Author

@mxyng mxyng Sep 29, 2021

Choose a reason for hiding this comment

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

$ k get deploy 
Error: login already in progress
Unable to connect to the server: getting credentials: exec plugin is configured to use API version client.authentication.k8s.io/v1beta1, plugin returned version client.authentication.k8s.io/__internal

Copy link
Contributor

Choose a reason for hiding this comment

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

To add: not sure how easy it would be for all these invocations of login() to wait on each other (especially since they may be separate processes) – but there may be a useful file lock library we can leverage for this (saw a few like https://github.com/zbiljic/go-filelock - but they seem very old)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I found a more recent library that implements file locking using sync.RWMutex. It provides a nice interface to build the behaviour we want.

Now, infra login will create a lock such that other infra login attempts will block until the lock is freed or it expires (5m). Once it's unlocked, it will not attempt a login. Instead it will continue assuming the other attempt successfully logged in.

ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5)
defer cancel()

_, err = lock.TryLockContext(ctx, time.Second*1)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

1 second interval between retries

internal/cmd/cmd.go Outdated Show resolved Hide resolved
@jmorganca
Copy link
Contributor

Very elegant! Nice work!!

@jmorganca
Copy link
Contributor

jmorganca commented Sep 30, 2021

Just tested this out a bunch with a super short token duration and it feels very good for both kubectl and other tools such as Infra App! No more lost data / errors or multiple tabs opening up. Just a super smooth re-login flow.

One important challenge to think about here: would there ever be a case (e.g. apps like Infra App or a long running scripts using kubectl) where a browser may suddenly pop open unexpectedly as tokens expire? If so this could really surprise users since an Okta login will randomly pop up – and they may not even know why or what it's for. The user may be on an important screen sharing zoom call – this would definitely alarm them

Do we have thoughts on how to handle this? Maybe via a refresh flow that uses their existing token vs requiring a full auth (this could be something we allow for later)? Or maybe we can have a more subtle step before the window when it's time to re-auth (perhaps a prompt on the CLI if interactive? or something else if non interactive? –– maybe a notification even?)

@BruceMacD
Copy link
Collaborator

Really good points around the pop-up. I hate that too personally, it's not good to expect users to enter their credentials into windows that randomly pop up. Gives you a bad reflex to enter your credentials on demand without questioning it.

I think adding a refresh flow (in a separate issue) is probably the correct solution. I like the more contextual identity provider re-authentication, but I don't know if it would be possible with every identity provider.

@jmorganca
Copy link
Contributor

jmorganca commented Sep 30, 2021

Another issue to capture in this area: there may also be the case where a computer is left idle and every 5 minutes a popup will come up since the lock will release. Another item to think about around the re-authentication experience since in say after 8 hours of inactivity that may still be 96 popups (if I understand correctly! just a hunch based on how this is implemented)

@ssoroka
Copy link
Contributor

ssoroka commented Sep 30, 2021

I could potentially see problems here where one login screen opens and the user doesn't notice or ignores it, then subsequent logins don't work because we're locked on the one login window. Seems like we may have caused the problem by having infra creds try to automatically login

@ssoroka
Copy link
Contributor

ssoroka commented Sep 30, 2021

infra creds and infra login should probably use github.com/mattn/go-isatty to determine if there's a user at the end of the request, or just a piece of software, and customize accordingly.

@mxyng
Copy link
Collaborator Author

mxyng commented Sep 30, 2021

term.IsTerminal seems to be doing what I expected. Still need to test with Infra App. Would appreciate some help in that area.

$ infra creds docker-desktop </dev/null
Error: 403 Forbidden
$ infra creds docker-desktop           
✓ Logging in with Okta...

It also looks to block login prompt in desktop apps like Lens.
image

@mxyng mxyng force-pushed the lock-login branch 4 times, most recently from 2d55b14 to 8892bbe Compare September 30, 2021 22:47
- prevent too many prompts from showing up
- block login while another instance has the lock
- assume the other instance is successful and exit with success if
  instance is able to get the lock and proceed without showing login
  prompt
@@ -50,7 +51,7 @@ type Config struct {
type ErrUnauthenticated struct{}

func (e *ErrUnauthenticated) Error() string {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not related to this PR where this is an existing error, but maybe we should move the errors package to the root level and put this there

@BruceMacD
Copy link
Collaborator

BruceMacD commented Oct 1, 2021

Tested again locally by switching on infra.app and this was handled gracefully
Screen Shot 2021-10-01 at 11 59 25 AM

No login prompt

@mxyng mxyng merged commit d881286 into main Oct 1, 2021
@mxyng mxyng deleted the lock-login branch October 1, 2021 17:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Repeated Calls to the Infra Creds command result in many Identity Provider Authn Windows Opening
4 participants