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

#13680 git support private key password #94407

Closed

Conversation

alarr46
Copy link

@alarr46 alarr46 commented Apr 3, 2020

This PR fixes #13680.

The git extension starts an ssh-agent right after the Git object instantiation. Code looks for this command in the directory given by the git.sshAgentDirectory setting or in C:\Program Files\Git\usr\bin on Windows or /usr/bin on other platforms, before tyring to call the raw command.
It then stores the associated SSH_AUTH_SOCK and SSH_AGENT_PID environment variables and uses them for every subsequent git call.
When opening a git repository, Code checks for a git.sshPrivateKeyPath. If it is set, it adds it to the running ssh-agent.

@alarr46
Copy link
Author

alarr46 commented Apr 3, 2020

Here is one way to test this without too much trouble :

  • create a new password protected SSH key pair
  • add it to your GitHub account
  • add the following lines to your~/.ssh/config file :
    Host githubtest
        HostName github.com
        User git
        IdentityFile path_to_your_new_private_key
    
  • clone one of your private repos with git clone githubtest:your_github_username/your_private_repo.git
  • now in this repo, any git command interacting with the remote will use your new key, and only this one

@joaomoreno joaomoreno added the git GIT issues label Apr 6, 2020
@WSLUser
Copy link

WSLUser commented Apr 6, 2020

The git extension starts an ssh-agent right after the Git object instantiation.

Can it check if an agent is already running? (Which it should be since the Win32-OpenSSH sets the service to automatically start upon reboot and systemd starts all services including ssh on Linux).

Basically the only time the ssh-agent might possibly NOT be running is if you are using the ssh binary provided by GitforWindows.

@WSLUser
Copy link

WSLUser commented Apr 6, 2020

Code looks for this command in the directory given by the git.sshAgentDirectory setting or in C:\Program Files\Git\usr\bin on Windows.

Please add C:\Program Files\OpenSSH-Win64 as well.

@alarr46
Copy link
Author

alarr46 commented Apr 6, 2020

The git extension starts an ssh-agent right after the Git object instantiation.

Can it check if an agent is already running? (Which it should be since the Win32-OpenSSH sets the service to automatically start upon reboot and systemd starts all services including ssh on Linux).

I did not implement this feature in this first version, but I should be able to add it.

@WSLUser
Copy link

WSLUser commented Apr 6, 2020

I would add a comment or note somewhere (perhaps a warning message) that the ssh-agent for the official Win32 OpenSSH requires PowerShell/openssh-portable#362 for PKCS11 Support.

@alarr46 alarr46 force-pushed the #13680-git-support-private-key-password branch from b56d0df to 127bb59 Compare April 6, 2020 20:39
@alarr46
Copy link
Author

alarr46 commented Apr 8, 2020

Unfortunately it seems that interacting with OpenSSH's ssh-add is more complicated than with the one from Git for Windows. I can start both ssh-agents fine, but I cannot read data from nor send data to OpenSSH's ssh-add.

With C:\Program Files\Git\usr\bin\ssh-add, I can use child_process.spawn, then read data (e.g. the password prompt) from the result's stdout and write data (e.g. the password) to its stdin.
However I could not manage to make this work with C:\Program Files\OpenSSH-Win64, at least on Windows 7.

I tried to locate the issue by building OpenSSH from source: it seems that the issue is that data written by _cputws, which is used to display the password prompt is not sent to stdout, and that data sent to stdin is not sent to _getwch, which is used to read the password. This seems logical as the doc states that

The console I/O routines are not compatible with stream I/O or low-level I/O library routines.

As I have no experience with all of this, any help would be appreciated.

@WSLUser
Copy link

WSLUser commented Apr 8, 2020

We might have to leave off for this PR and add a TODO, file an issue on their repo and link it in the TODO

@alarr46
Copy link
Author

alarr46 commented Apr 9, 2020

We might have to leave off for this PR and add a TODO, file an issue on their repo and link it in the TODO

I might have a solution : I am currently looking into node-pty, which seems to do just what we need.

@alarr46
Copy link
Author

alarr46 commented Apr 12, 2020

It now works with Windows OpenSSH.

The current behaviour is:

  • right after the Git object instantiation, Code calls the ssh-agent command.
  • when opening a git repository, if the git.sshKeyPrivatePath setting is set, Code checks if it is already registered in the ssh-agent. If not, it calls ssh-add with it. If a password is required, the user is prompted for it.

To find the ssh-agent command, Code tries, in this order:

  • ${git.sshAgentDirectory}/ssh-agent
  • ssh-agent
  • (Windows only) C:\Program Files\OpenSSH-Win64\ssh-agent
  • (Windows only) C:\Program Files\OpenSSH\ssh-agent
  • (Windows only) C:\Program Files\Git\usr\bin\ssh-agent

To communicate with the ssh-agent:

  • the ssh-add command is looked for in ssh-agent's directory, and the GIT_SSH environment variable is set to it. It is needed on Windows for the OpenSSH version.
  • if ssh-agent outputs special values for the SSH_AUTH_SOCK and SSH_AGENT_PID environment variables, they are used for the subsequent ssh-add and git calls. This is needed for the other ssh-agent versions.

With the OpenSSH version on Windows, if an agent is already running, it will be used (it is the default setting for this command). For the other versions, a new agent is started.

@WSLUser
Copy link

WSLUser commented Apr 12, 2020

Awesome job. Linux should default to an already running ssh agent as well. I'm not familiar enough with Mac to know if an agent is already started but I think it's safer to try re-using there too. A new agent should only be started if its not detected as running. For Linux and Mac, you can quickly check by using ps -ef | grep ssh. Technically the ef is optional.

@alarr46
Copy link
Author

alarr46 commented Apr 12, 2020

Awesome job. Linux should default to an already running ssh agent as well. I'm not familiar enough with Mac to know if an agent is already started but I think it's safer to try re-using there too. A new agent should only be started if its not detected as running. For Linux and Mac, you can quickly check by using ps -ef | grep ssh. Technically the ef is optional.

Thanks! I agree, however detecting if an agent is already running is not enough: in order to communicate with it, one must find the socket it is bound to. The man states that it defaults to /tmp/ssh-XXXXXXXXXX/agent.<ppid> so I guess we could search for this specific file if a running agent is detected. However, it will not be enough for custom bind_address values.

@WSLUser
Copy link

WSLUser commented Apr 12, 2020

In the event of custom, I would feel comfortable starting a new agent instead unless someone chimes in differently. We're only trying to check standard ssh configurations. So I would just stick to the man and search that file and be sure there are good tests in place to ensure this feature works as intended. If you think it's trivial to address the custom value, then include it by all means and will certainly warrant its own test.

@alarr46
Copy link
Author

alarr46 commented Apr 12, 2020

Another issue with the search for a running agent would be to find which one to use if several are found. For example, a machine I recently installed running Ubuntu 18.04 has 2 agents that start on boot: one seems to come from gdm3 and the other from gnome-keyring, both of them being on a default install.

gdm3 uses a default /tmp/ssh-XXXXXXXXXX/agent.<ppid> socket, whereas gnome-keyring uses a custom /run/user/${UID}/keyring/ssh. In my env, the SSH_AUTH_SOCKET variable is set to this custom value.

Maybe the safest way would be to check if the SSH_AUTH_SOCKET is already set, and if so to use it, along with the associated agent. After all, it seems to be the proper way to tell a program to use an agent.

@alarr46 alarr46 force-pushed the #13680-git-support-private-key-password branch from 3a6507c to 67c3e18 Compare April 14, 2020 09:19
@alarr46
Copy link
Author

alarr46 commented Apr 14, 2020

I added this feature: if the SSH_AUTH_SOCK environment variable is set, the associated agent is used instead of starting a new one.

@WSLUser
Copy link

WSLUser commented Apr 14, 2020

I suggest rebasing off master again. It appears Linux CI is broken so I guess just run tests locally for Linux.

@alarr46 alarr46 force-pushed the #13680-git-support-private-key-password branch from 67c3e18 to cb1b353 Compare April 14, 2020 16:16
@monospacedmagic
Copy link

Can't wait for this to be merged! Is there anything keeping this PR from being merged?

@14alarro
Copy link

Can't wait for this to be merged! Is there anything keeping this PR from being merged?

It needs to be reviewed first, then I'll have to address the comments made in the review.

@@ -143,6 +143,8 @@
"config.untrackedChanges.mixed": "All changes, tracked and untracked, appear together and behave equally.",
"config.untrackedChanges.separate": "Untracked changes appear separately in the Source Control view. They are also excluded from several actions.",
"config.untrackedChanges.hidden": "Untracked changes are hidden and excluded from several actions.",
"config.sshPrivateKeyPath": "Path of the SSH private key associated with your remote.",
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this needed even if the ssh config file includes proper Host entries?

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, what happens if the ssh-agent already has some keys added?

Copy link
Contributor

Choose a reason for hiding this comment

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

Also see #13680 (comment). It might be relevant.

Copy link
Author

Choose a reason for hiding this comment

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

Is this needed even if the ssh config file includes proper Host entries?

It can be avoided, but it would cost more substantial changes to the extension code, so I am waiting for reviews before doing it. I think the best way to use the SSH config file would not be to parse it (that would add complexity to the extension) but to read the output of the git commands and to handle any password prompt there. However, that would mean changing the exec function since it uses child_process which is unable to read such prompts, but replacing it with node-pty as I did here would not be enough since we would lose the stdout / stderr distinction.

Also, what happens if the ssh-agent already has some keys added?

If the ssh-agent already has some keys added, nothing changes : you can add the same key twice without side effects.

Also see #13680 (comment). It might be relevant.

I agree that this could be an interesting feature. Git Bash's agents store their SSH_AUTH_SOCK in %TEMP%\ssh-* folders, so we could check them before starting the agent. I chose not to do it at first because I read that OpenSSH was now installed by default on Windows 10, and as you noted in your comment its agent is available to all processes. However I guess there is still a number of people who do not use it and would therefore be interested in this.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for clarifying. Let's wait for some maintainers to get around to reviewing this.

I think it's better to get something in first and then iterate on feedback rather than holding things up to achieve perfection.

@hashhar
Copy link
Contributor

hashhar commented May 3, 2020

Thanks for picking this up. I attempted this 4 years ago and got to where you were stuck - interaction with the ssh-agent.

Thanks a lot for persisting with this and bridging that gap. It's surprising really how difficult /impossible interaction with ssh is.

@alarr46 alarr46 force-pushed the #13680-git-support-private-key-password branch from cb1b353 to cc1925c Compare May 21, 2020 11:09
@alarr46 alarr46 force-pushed the #13680-git-support-private-key-password branch from cc1925c to 142f1c5 Compare July 26, 2020 16:36
@alarr46 alarr46 force-pushed the #13680-git-support-private-key-password branch from 1d22a32 to 722e4ed Compare August 1, 2020 20:19
@alarr46
Copy link
Author

alarr46 commented Aug 1, 2020

These last commits make this work on Windows 10 (until recently I could only test it on Windows 7).

One part of the fix involved a better handling of ANSI/VT sequences. I made a custom function in the git extension to remove them, but I think it would be better to put it elsewhere. I wanted to edit and use the removeAnsiEscapeCodes function from the src/vs/base/common/strings.ts file, but I did not find a way to reach it from the extension code.

@joaomoreno joaomoreno assigned eamodio and unassigned joaomoreno Nov 11, 2020
@joaomoreno joaomoreno changed the base branch from master to main February 15, 2021 08:51
@lszomoru lszomoru assigned lszomoru and unassigned eamodio Oct 4, 2021
@lszomoru
Copy link
Member

Closing this PR as this capability has been added through #159573 leveraging some of the existing ASKPASS infrastructure.

@lszomoru lszomoru closed this Sep 26, 2022
@github-actions github-actions bot locked and limited conversation to collaborators Nov 10, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
git GIT issues
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Git: Support git with private key password
8 participants