SwiftPM SSH issues on Windows 11

Greetings SwiftFans!

I'm having an issue using SPM on Windows 11. I have two private Swift Package repositories on GitHub. It has been working fine for one Package to depend on the other, and I've only had some occational SSH issues that have been quickly resolved.

Now however, I've been trying to set up my development environment on Windows 11 and can't get 'swift build' to fetch private dependencies. I constantly get "Permission denied" errors, even though the SSH key works (which is evident since I can clone the repository just fine).

I have tried setting up multiple virtual machines with Windows 11 and yet always run into the same issue. I thought it might be a VM issue but today I tried on a physical Windows 11 PC and get the same issue.

Are there any known issues with SPM on Windows 11? I'm aware that Swift on Windows is still in development but I've managed to get it working on Windows 10 multiple times so I thought I'd best reach out for help.

I would suggest that you check what URL is being used. Most likely, it is using the https transport, which would require the authentication to have the bearer token which requires that you login with the prompt that shows up first. This is due to the repository being private and not a Windows specific thing I believe.

CC: @Max_Desiatov @NeoNacho

Thanks for the quick reply @compnerd, and let me also say I much appreciate your work to bring Swift to Windows!

Regarding the URL; do you mean I should check the URL in the Package.swift manifest? It is using git@github.com, not https://github.com so it should be using SSH, right?

Or do you mean I can check somewhere else what URL it's actually using? I don't imagine the swift command transforms the URL from Package manifest at all?

You're welcome, I'm glad that you find it useful!

Okay, good, so using git@github.com:... would imply it should use SSH, which now makes me wonder why is that failing. If git clone with the URL works from the command line, SPM should be able to do that (it does the same exact thing).

If git clone with the URL works from the command line, SPM should be able to do that (it does the same exact thing).

That's my thinking as well, however I've seen git clone work without swift build working. In those cases I have had to run ssh-agent -s followed by ssh-add path/to/key to get swift build to find the dependencies successfully. I believe that git in that case finds the .ssh/config file, but I'm not sure why the swift command wouldn't be able to.

On macOS the ssh-agent command has to run with eval and I have previously also had to run start-ssh-agent on Windows 10 to get it working (I think that's a script included by Git for Windows but I don't know what it does). Needless to say I've tried those on my Windows 11 setups and nothing worked there. I hope that adds some context.

This seems entirely reasonable. Setting up the SSH Agent to get the ssh key to be used is required. One possible thing could be happening is that we are blowing away the configuration for the ssh agent from the environment when invoking the child process. It might be possible to observe this using procmon while swift resolve executes.

I haven't used procmon in a long time, so that'll be fun. I'm not familiar with swift resolve though, is that a subcommand of the driver? Can you tell me how I filter for it? And can you tell me more about what I'm looking for in terms of the ssh-agent?

I guess the reference was to swift package resolve? If so, that's a SwiftPM subcommand of the swift-package executable binary called when you invoke swift package.

I see, so should I run swift-package resolve while monitoring using procmon? Or is that called by swift build?
I'm still not sure exactly what I'm looking for. Can you explain what would indicate that the ssh-agent configuration changes for a child process? Am I looking for a specific event?

I appreciate that you take the time to help me troubleshoot. In the meantime I'm using a workaround and cloning the dependencies manually so I'm not blocked by this. But I hope to be able to get this running correctly.

When you run swift package resolve, SPM should try to compute the version information for all the packages, which will require cloning them. By using procmon, you should be able to find the invocation of git that it runs, and hopefully identify the environment variables at the time of execution. Hopefully that will help recreate the command and identify what is going on.

I can see that when git.exe is launched by the swift-package.exe it's given the environment variable GIT_SSH_COMMAND=ssh -oBatchMode=yes.
If I add this to my command line (I'm using x64 Native Tools Command Prompt) I get the same permission denied (publickey) error on a git clone git@github.com:... command.
And removing it using set GIT_SSH_COMMAND= fixes it and the same git clone git@github.com:... command succeeds again.
I'm guessing this is the issue, though I'm not sure what to do about it.

It sounds like a configuration issue. The issue is that the way that ssh is currently configured, it does not have access to the credentials required. ssh -vvv might be helpful to see where the configuration is and allow you to configure this.

Figuring out how to configure this might require @NeoNacho or @Max_Desiatov to identify how to wire this up properly.

Alright, I managed to get it working, and I feel like I'm close to understanding what's going on. ssh -T git@github.com worked fine, and doesn't ask for a password, which shows the ssh agent seems to be accessible and working. And ssh-add -l showed my key as expected. However git clone git@github.com:... always asked me for a password to my ssh key, which seemed odd and I realized a bit late how significant this was.

So I went searching a bit and found this answer on SO. I tried a modified version of the proposed solution: git config --global core.sshCommand (get-command ssh).Source.Replace('\','/') and found that now git clone git@github.com:... no longer asked for a password but worked seamlessly. Trying swift build again I found it still didn't work, but I thought I must be close so I went back to the SO answer.

Reading it again I think I finally understand, the answer says:

When you install git, it comes with ssh. But if you have a newer version of Windows 10, Windows has an install of SSH that comes with it. Installed in C:\Windows\System32\OpenSSH. That gets put into the environment PATH

Assuming this is correct I think it makes sense that perhaps I have only set up the projects on older Windows 10 versions, hence why I have not encountered this issue before.

When I realized there were 2 separate ssh-agents running I found the one git is using instead of the one I have given my key. I had some trouble before I realized that the start-ssh-agent script I mentioned earlier is actually used to start the ssh-agent git uses instead of the Windows built-in one (if I understand correctly). So having run that script and also ssh-add /path/to/key with the ssh-add explicitly in C:\Program Files\Git\usr\bin I got it working. So now both git clone git@github.com:... and swift build run successfully, without asking for password!

Thanks a lot for helping me guys! I'm curious if there's some documentation I've missed about this. Is there some recommended way of setting up ssh on Windows when there's this double implementation?

Cheers!

2 Likes

I’m not certain, but I’d check the git book. This is part of why the recommendation is to never use swift from git bash - the environment is different and I’ve seen weird behavioral differences.

Will do!

I understand, like I said before I almost always use the x64 Native Command Prompt
but in recent Swift versions I've found that CMD also works fine.
Thanks again!

Yes, it should as there was effort put into trying to make that work properly as well :slight_smile: If there are corner cases with that still, please to report them.