While a proper ~/.ssh/config
can be great, this should be a minimal way to set up new keys for using multiple accounts on Mac, Windows, and WSL, and covers some of the traps in simultaneous use of Windows AND WSL.
To connect from "this machine" to a new GH account. While ~
will be different from your windows and WSL's perspective, WSL's ~/.ssh
should be a soft link to your window's ~/.ssh
, so it's ok to use either
# Make the soft link, if it doesn't yet exist.
# Might need to mkdir if this is your first key.
YOUR_WINDOWS_USERNAME=example
mkdir -p /mnt/c/Users/$YOUR_WINDOWS_USERNAME/.ssh
ln -s /mnt/c/Users/$YOUR_WINDOWS_USERNAME/.ssh ~/.ssh
# Make the key
EMAIL=your_email@example.com
KEYNAME=GH_Username
ssh-keygen -t ed25519 -C "$EMAIL" -f ~/.ssh/$KEYNAME
Upload the public key ~/.ssh/$KEYNAME.pub
to https://github.com/settings/ssh/new
.. to add the following to /etc/wsl.conf
from WSL
[automount]
options = "metadata"
and then close all sessions and wsl --shutdown
from cmd/pwsh, and apply the user read only permissions to the key, which is required by *nix's openssh but appears to have no affect on windows' ssh-agent.
chmod 400 ~/.ssh/$KEYNAME
Get-Service -Name ssh-agent | Start-Service
# Or to make it automatic, from an admin pwsh
Get-Service -Name ssh-agent | Set-Service -StartupType Automatic
Add the following to ~/.bashrc
to allow ssh-agent to start automatically.
if [ -z "$SSH_AUTH_SOCK" ] ; then
eval `ssh-agent -s` > /dev/null
# ssh-add
fi
Without this, it can be started manually each time with eval "$(ssh-agent -s)"
ssh-add ~/.ssh/$KEYNAME
This should show you which keys the agent is aware of, and which one is being used by ssh
.
ssh-add -l
ssh -vT git@github.com
By using the ssh -v
setting in the environment variable GIT_SSH_COMMAND
(which supersedes GIT_SSH
), and trying to git pull / clone for an account that has its key in the agent, we can see an issue. It appears git uses the ssh-agent on *nix by default but not on windows? To add ssh-agent to the keys tried by git on windows, open powershell and;
[Environment]::SetEnvironmentVariable("GIT_SSH", "$((Get-Command ssh).Source)", [System.EnvironmentVariableTarget]::User)
Which should be a persistent fix!
Shell | Command |
---|---|
sh | export GIT_SSH_COMMAND="ssh -v" |
cmd | set GIT_SSH_COMMAND=ssh -v |
pwsh | $env:GIT_SSH_COMMAND='ssh -v' |
However, the ssh
reference in pwsh is slightly more cumbersome. For some reason, although backslashes cause no issue in GIT_SSH
, as set by the above, Write-Host $env:GIT_SSH_COMMAND
yielding the same output as Write-Host $env:GIT_SSH
results in git trying to use the path for Get-Command ssh
without backslashes. So to add verbose temporarily to GIT_SSH_COMMAND
in pwsh, we need the following.
[Environment]::SetEnvironmentVariable("GIT_SSH_COMMAND", "$(((Get-Command ssh).Source).replace('\','/')) -v", [System.EnvironmentVariableTarget]::Process)
Git ssh doesn't retry when a key is accepted but not valid. That is to say, when multiple keys are added to the agent, it appears the first key that could be accepted by the site, will be. In other words, if multiple keys will be accepted by a server, because they are all valid for something, the first key to be accepted, might not be valid for the account you're trying to access!! For example, ssh-add A
followed by ssh-add B
will make git attempt to use key B
first. If the account for the repo you're working with has key A
attached to it, and B
is attached to some other account, then key B
will be "accepted", and then subsequently denied as not being for that account.
The agent, besides however it interacts with keychain on mac, is totally ephemeral on wsl. So it would not be unreasonable to assume if you want to work with the ssh-agent to handle keys, that you'd be familiar with ssh-add -l
to list the keys in the agent, and ssh-add -D
to remove all active one. While The agent is ephemeral on WSL, and will need specific keys added on each start up, without a default one being loaded in an rc
, Windows appears to store the keys in the agent permanently, so would need to have the previous entry deleted before adding another (it will use whatever the most recent unique addition was that was not already present).
If you've been using some https method, you'll need to update, or add a remote that uses ssh instead.
# HTTPS format is https://github.com/USERNAME/REPOSITORY.git
git remote get-url --all origin
git remote set-url origin git@github.com:USERNAME/REPOSITORY.git