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

cmd/go: document how to fetch private repos with Yubikey SSH authentication #49515

Open
ckcr4lyf opened this issue Nov 11, 2021 · 7 comments
Open

Comments

@ckcr4lyf
Copy link

@ckcr4lyf ckcr4lyf commented Nov 11, 2021

From the changelog of Go 1.17, I see:

Password prompts

The go command by default now suppresses SSH password prompts and Git Credential Manager prompts when fetching Git repositories using SSH, as it already did previously for other Git password prompts. Users authenticating to private Git repos with password-protected SSH may configure an ssh-agent to enable the go command to use password-protected SSH keys. 

I use private repos with SSH and have correctly set up my .netrc and all. I use a yubikey with PIN for SSH authentication.

Go 1.16 behavior

I try and get a lib / run go mod vendor, the git client will prompt me for my yubikey PIN in order to authenticate with the remote server, once I enter it correctly, it all works well. (EDIT: Probably the SSH client which git calls, but either way, it worked fine)

go mod vendor
Enter PIN for 'PIV Card Holder pin (PIV_II)': 
Enter PIN for 'PIV Card Holder pin (PIV_II)': 
Enter PIN for 'PIV Card Holder pin (PIV_II)': 
go: downloading gitlab.com/[redacted]
go: downloading gitlab.com/[redacted]
go: downloading gitlab.com/[redacted]

Note: There are multiple prompts, I enter my pin for each one.

Go 1.17 behavior

I try and run go mod vendor or something, and it will fail with this:

go: gitlab.com/[redacted]: invalid version: git fetch -f origin refs/heads/*:refs/heads/* refs/tags/*:refs/tags/* in /home/raghu/go/pkg/mod/cache/vcs/[redacted]: exit status 128:
	git@gitlab.com: Permission denied (publickey,keyboard-interactive).
	fatal: Could not read from remote repository.
	
	Please make sure you have the correct access rights
	and the repository exists.

I am 99% sure this is due to the change in Go 1.17 to disable "password prompts", since if I revert to Go 1.16 all works as expected.

I am able to use my Yubikey w/ PIN prompt for Git and other SSH stuff just fine. The changelog suggests using an ssh-agent for password-protected keys, but in my case, the key resides on my Yubikey, and I need to enter the pin in order to perform cryptographic operations with it. Since the key is not accessible, I cannot cache it within SSH agent. (Additionally, I do not want to cache my yubikey pin).

This change introduces in Go 1.17 seems like a nerf to security, since for SSH repos, it seems to force you to have an SSH key which is cached by ssh-agent.

If there is no way to disable this with a flag on my end or something, I think it should be reverted for Go 1.18, as @bcmills suggested it is being "given a try" in Go 1.17 - #44904 (comment)

@ckcr4lyf
Copy link
Author

@ckcr4lyf ckcr4lyf commented Nov 12, 2021

FYI: I currently have bypassed this by setting by env var

GIT_SSH_COMMAND="ssh -o ControlMaster=no"

as hinted by the source here -> https://github.com/golang/go/blob/master/src/cmd/go/internal/get/get.go#L156

I think it should at least be added to the docs (or just changelog) to make it easier

Loading

@bcmills bcmills changed the title Go 1.17 - Unable to fetch private repo with Yubikey SSH authentication cmd/go: unable to fetch private repo with Yubikey SSH authentication Nov 12, 2021
@bcmills
Copy link
Member

@bcmills bcmills commented Nov 12, 2021

This change introduces in Go 1.17 seems like a nerf to security, since for SSH repos, it seems to force you to have an SSH key which is cached by ssh-agent.

The go command intentionally disables password prompts because it fetches repositories in parallel — there isn't really a controlled way for you to see which repo is asking for your credential. (That is admittedly more of a concern when PasswordAuthentication is enabled in your ssh client configuration, but we still don't want to train users to get in the habit of entering passwords without a clear destination.)

The long-term solution for Yubikey authentication in particular might be to use the ssh ControlMaster setting. You could establish the ssh session prior to running the go command, and then use that connection to fetch any needed repos, and finally tear it down again. Unfortunately, we also currently disable ControlMaster for #13453 — but that may be feasible to fix a different way.

Loading

@ckcr4lyf
Copy link
Author

@ckcr4lyf ckcr4lyf commented Nov 12, 2021

there isn't really a controlled way for you to see which repo is asking for your credential.

That's a great point. Since all the private repos I'm using are based on the same auth, the thought had never crossed my mind, but I can see where this change came from.

I think using the environment variable is a decent workaround for now, since it allows for using Yubikey SSH auth w/ Go 1.17

Loading

@bcmills
Copy link
Member

@bcmills bcmills commented Nov 15, 2021

I did a bit more reading, and here's my suggestion (based on https://ldpreload.com/blog/ssh-control):

  1. Explicitly set GOPRIVATE for your private module path(s) (as I assume you have already done).
  2. You may also need to set a Git insteadOf rule in ~/.gitconfig as described in #26134 (comment).
    • (Since gitlab.com is not a hard-coded hosting provider, that might not be necessary.)
  3. Add ControlPath ~/.ssh/control-%C to your .ssh/config file.
  4. Run ssh -M -N -f git@gitlab.com. That should prompt you for Yubikey authentication to GitLab, then return you the shell prompt (leaving the SSH connection open in the background).
  5. Run whatever go command(s) you need to download the needed module contents.
  6. Finally, run ssh -O exit git@gitlab.com to terminate the open SSH connection.

If you don't want to adjust your global .ssh/config, you could instead do:

  1. In the shell in which you will run the go command:
    export GIT_SSH_COMMAND='ssh -o ControlPath=~/.ssh/control-%C -o BatchMode=yes`
  2. Run ssh -o ControlPath=~/.ssh/control-%C -M -N -f git@gitlab.com to prompt for authentication.
  3. Run whatever go command(s) you need to download the needed module contents.
  4. Finally, run ssh -o ControlPath=~/.ssh/control-%C -O exit git@gitlab.com to terminate the open SSH connection.

If you don't want to leave a connection open in the background indefinitely if you forget step (5), you could omit the -f flag to the ssh command and instead set the ControlPersist option to a reasonably short timeout.

Or:

  1. In a different terminal, run ssh -M -N git@gitlab.com.
  2. Run whatever go command(s) you need to download the needed module contents.
  3. In the terminal running the ssh command from step (4), press ⌃C to terminate the open session.

Loading

@bcmills
Copy link
Member

@bcmills bcmills commented Nov 15, 2021

@ckcr4lyf, please give the above a try and let me know if it works. (If so, perhaps I can add some more formal documentation on this configuration.)

Loading

@bcmills bcmills added this to the Backlog milestone Nov 15, 2021
@ckcr4lyf
Copy link
Author

@ckcr4lyf ckcr4lyf commented Nov 16, 2021

@bcmills , thanks so much for the response. I can confirm it works perfectly!

I tested it with the latter option of a "temporary setup", and was able to fetch multiple private repos (for the same SSH auth) with no issues (including prompts). I tried the "permanent" solution w/ SSH config, and that also works great!

I believe this is a viable solution, since it allows for secure SSH-auth (e.g. via Yubikey + pin) explicitly, which can further be terminated once the go operations are complete.

Cheers, and thanks for the help (not sure if I should close the issue or not)

Loading

@bcmills bcmills changed the title cmd/go: unable to fetch private repo with Yubikey SSH authentication cmd/go: document how to fetch private repos with Yubikey SSH authentication Nov 18, 2021
@bcmills
Copy link
Member

@bcmills bcmills commented Nov 18, 2021

Thanks for confirming! I'll leave this issue open for documentation — we certainly do want to support private repos that require two-factor authentication, and this setup and workflow is not trivial to figure out.

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants