Skip to content

Check for already configured ssh keys before using automatic key#5958

Merged
cmbrose merged 21 commits intotrunkfrom
cmbrose/no-auto-key-if-uploaded
Aug 23, 2022
Merged

Check for already configured ssh keys before using automatic key#5958
cmbrose merged 21 commits intotrunkfrom
cmbrose/no-auto-key-if-uploaded

Conversation

@cmbrose
Copy link
Member

@cmbrose cmbrose commented Jul 17, 2022

Fixes: https://github.com/github/codespaces/issues/8154 (again)

An additional follow up to #5752 where users had issues with the password-less codespaces.auto key being generated for gh cs ssh, even though they already had valid ssh keys configured.

This PR will now check the ssh configuration to see which private keys ssh (or scp) will use and check if the user has a matching public key uploaded to GitHub. If so, it will skip creating the automatic key.

The configuration check is done using ssh -G localhost which gives the effective configuration for the real localhost connection the command will attempt to make later. It parses this configuration for identityfile lines and uses these as the target private keys to search for in the public key set.

Additionally, the PR improves the handling of --profile and -- -F parameters, where before we cautiously bailed out and did not generate keys, we now can use those parameters in the ssh -G call to check if we should still generate auto keys with those parameters. This is useful for users of gh ssh cs --config to ensure that they still get proper automatic keys. If the -- -i flag is provided, the command will continue to not generate automatic keys, this provides an escape hatch from the behavior if needed.

Note: the -G flag is added in OpenSSH 6.8 which released in 2015, so hopefully that is long enough ago to rely on 😄

Verified cases:

  • Results in using codespaces.auto:
    • No uploaded ssh keys
    • No matching uploaded ssh keys
    • codespaces.auto already exists
    • codespaces.auto already exists and has a passphrase
    • Using the --config flow as documented in --help
    • Has a matching custom key, but codespaces.auto exists
  • Results in a custom key:
    • Using -- -i <key> for key with/without passphrase
    • Using --profile which specifies a custom key
    • Has a matching uploaded public key with/without a passphrase

@cmbrose
Copy link
Member Author

cmbrose commented Jul 17, 2022

I haven't updated the tests yet because I wanted to get a general 👍 on this approach before diving into them. Will update them accordingly once things are decided.

Copy link
Contributor

@mislav mislav left a comment

Choose a reason for hiding this comment

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

I think this is a worthwhile exploration! I would have some suggestions how to simplify the approach before this ships, but it's a bit premature at this stage to start reorganizing code around. Thanks for the code spike 🙇

@cmbrose
Copy link
Member Author

cmbrose commented Jul 22, 2022

@mislav I've updated the logic to just check for existing .pub files instead of trying to generate them on the fly and also worked in logic to support --config from #5877 (there were some things needed in this PR to support that so it made sense to roll together).

Does the approach look good? If so, I'll start putting actual polish on it.

@cmbrose cmbrose marked this pull request as ready for review August 2, 2022 22:29
@cmbrose cmbrose requested a review from a team as a code owner August 2, 2022 22:29
@cliAutomation cliAutomation added the external pull request originating outside of the CLI core team label Aug 2, 2022
Copy link
Contributor

@josebalius josebalius left a comment

Choose a reason for hiding this comment

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

I'm a bit worried about the performance implications of this change. We tried to keep the SSH command as fast as possible, and this PR adds more cycles to this process. I wonder if we have alternatives?

@cmbrose
Copy link
Member Author

cmbrose commented Aug 3, 2022

I'm a bit worried about the performance implications of this change

@josebalius there are a couple ways that the api call to fetch known keys can be short circuited:

  • if the user specifies -i we already skip the api check
  • it would be easy to add a check that if the codespaces.auto key exists then we can assume we already did the api check and it failed (otherwise we wouldn't have generated the auto key)

The issue is the case where there IS a matching public key locally and in dotcom. In that case there's not much we can do to cache that result (at least not that I'm aware of) and so the cli would always make the api check every time. It may be enough to just encourage users to stay out of this group - use the auto keys as the preferred option if they can and use -i if they can't (and don't like the latency hit).

One slight alternative is to create a new RPC call to the codespace to fetch authorized_keys directly instead of via the dotcom api. That may be slightly faster since codespaces are geo-located with the user (generally). However, it's still an api call and I'm not too convinced just looking at the theory though that the latency savings would warrant the engineering effort.

@josebalius what do you think?

@josebalius
Copy link
Contributor

if the user specifies -i we already skip the api check
it would be easy to add a check that if the codespaces.auto key exists then we can assume we already did the api check and it failed (otherwise we wouldn't have generated the auto key)

My concern with one, is that in order to get the fast experience the user has to do something extra that they have not been doing until now, so we are changing the default experience. Granted perhaps this should have been the default experience and avoid some of the friction users often run into.

If we find a codespaces.auto key, is it guaranteed that the connection will work?

@cmbrose
Copy link
Member Author

cmbrose commented Aug 4, 2022

My concern with one, is that in order to get the fast experience the user has to do something extra that they have not been doing until now

@josebalius thats not really correct. With the proposal to skip the api check if the codespaces.auto key exists, then only the very first run of the ssh command would be slower. All subsequent runs would be fast as the key would have be generated in the first run.

This change is explicitly to better support users who have policies on their machines that flag ssh keys without passphrases. Currently they are required to always use the -i flag in order to work around the automatic key generation. This change will remove the pain in the command line at the cost of the api call latency. That said, we could even recommend that those users manually create the codespaces.auto key themselves, just one time and they would also then skip the api latency.

@cmbrose
Copy link
Member Author

cmbrose commented Aug 4, 2022

If we find a codespaces.auto key, is it guaranteed that the connection will work?

It is guaranteed that the connection authentication will work. It is however still possible that something else could be broken in the ssh connection, e.g. the code spec doesn't have ssh installed.

@josebalius
Copy link
Contributor

With the proposal to skip the api check if the codespaces.auto key exists, then only the very first run of the ssh command would be slower. All subsequent runs would be fast as the key would have be generated in the first run.

Fair

In that case, short circuiting if it exists:

it would be easy to add a check that if the codespaces.auto key exists then we can assume we already did the api check and it failed (otherwise we wouldn't have generated the auto key)

sounds like a good idea. Only thing is what happens if for some reason user needs to re-gen, should we provide some way of "resetting" or regenerating the key?

@cmbrose
Copy link
Member Author

cmbrose commented Aug 5, 2022

Only thing is what happens if for some reason user needs to re-gen, should we provide some way of "resetting" or regenerating the key?

Is there a scenario where the user would need to do that? The codespaces.auto key isn't really intended for users to take and use for other things.

That said, if they need to reset it, they can just delete it manually and either just run gh cs ssh again to get a default, or manually re-create with a passphrase if needed. Each time gh cs ssh connects to the codespace it sends over the latest version of codespaces.auto.pub so there's no issues around stale caching or anything.

@cmbrose
Copy link
Member Author

cmbrose commented Aug 9, 2022

@josebalius thanks for all the feedback/discussion here! I've added the short circuit existence check, does this look OK now?

Copy link
Contributor

@josebalius josebalius left a comment

Choose a reason for hiding this comment

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

This is looking good, there's still a lot of code 🙈 that I'd probably want to take more time with and test some different things, but I trust you've got this working, and it works well ❤️

@cmbrose
Copy link
Member Author

cmbrose commented Aug 9, 2022

@josebalius I've outlined the validations I've run for this at the top, if there's anything you think I'm missing let me know!

I did run into one issue I was hoping you could help with though, regarding --profile and --config.

The issue is that --config includes a line like:

ProxyCommand /opt/homebrew/bin/gh cs ssh -c <name> --stdio

If you then use --profile to target that config it makes the ssh command effectively call back into itself, but with different parameters (no --profile).

Losing the --profile then changes the inputs to the API key check which can cause it to fail. I tested locally that if you add --profile to the --config output then it will work again, but only if you remove the check that you can't specify --stdio and --profile at the same time...

I'm not really sure how to move forward with this because I don't have enough context on how --profile is generally used. Do you have any ideas?

@cmbrose
Copy link
Member Author

cmbrose commented Aug 10, 2022

The resolution to the issue about ProxyCommand was to add -- -i <auto-key-path> to the generated config so the proxied command works too. This does mean that if users don't want the auto key they will need to one time regenerate configs. Without a regenerated config things will work still, but the proxy gh cs ssh call will generate the key.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external pull request originating outside of the CLI core team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants