Git SSH Keys for Windows and WSL

“A very little key will open a very heavy door.” - Charles Dickens

Thanks a lot to Joshua Mitchell for reviewing and correcting this article! And thanks to my wife Evie who edits most of the content of all of my articles!

Among the more common questions I get is about how authentication in GitHub, GitLab, or some other hosting service for Git repositories works. One way to do it is via Secure Shell (SSH) Keys (see What is an SSH Key?). This tutorial is meant to show you how to do this on Windows 10 or 11 AND how to do it for Windows Subsytem for Linux -- https://learn.microsoft.com/en-us/windows/wsl/about

Generate a new SSH key for Windows

Tip: Because some older distributions of FIPS (Federal_Information_Processing_Standards) don’t allow ed25519 keys yet -- I’m looking at you RHEL 8.(https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8) -- it may be best to generate an RSA key if you need to support such.

https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent

From the source:

You can generate a new SSH key on your local machine. After you generate the key, you can add the public key to your account on GitHub.com to enable authentication for Git operations over SSH.

1.    Open Git Bash.

2.    Paste the text below, replacing the email used in the example with your GitHub email address.

3.  ssh-keygen -t ed25519 -C "your_email@example.com"

Note: If you are using a legacy system that doesn't support the Ed25519 algorithm, use:

 ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

This creates a new SSH key, using the provided email as a label.

> Generating public/private ALGORITHM key pair.

When you're prompted to "Enter a file in which to save the key", you can press Enter to accept the default file location. Please note that if you created SSH keys previously, ssh-keygen may ask you to rewrite another key, in which case we recommend creating a custom-named SSH key. To do so, type the default file location and replace id_ALGORITHM with your custom key name.

> Enter a file in which to save the key (/c/Users/YOU/.ssh/id_ALGORITHM):[Press enter]

4.    At the prompt, type a secure passphrase. For more information, see "Working with SSH key passphrases."

5.  > Enter passphrase (empty for no passphrase): [Type a passphrase]
> Enter same passphrase again: [Type passphrase again]

On my machine it looked like this to generate a key called sample-key:

I recommend setting a passphrase on every key:

https://docs.github.com/en/authentication/connecting-to-github-with-ssh/working-with-ssh-key-passphrases

If you kept the default location it should have put your key(s) in this folder.

Location of the hidden .ssh folder on my machine.

Location for me was C:\Users\<username>\.ssh which is a hidden folder. If you can’t see hidden files then here’s how to get them to show: https://support.microsoft.com/en-us/windows/show-hidden-files-0320fe58-0117-fd59-6851-9b7f9840fdb2

SSH-agent

You are going to need to manage your keys with a program called ssh-agent https://en.wikipedia.org/wiki/Ssh-agent. The gory details are provided here:

https://unix.stackexchange.com/questions/72552/whats-the-purpose-of-ssh-agent

Essentially ssh-agent is responsible for caching your ssh authentication tokens locally. It also provides the capability to only enter your ssh passphrase one time when you first login and never again until you log in again.

Add the key to ssh-agent: Windows

Here’s a way to do it from stackoverflow: https://stackoverflow.com/questions/18683092/how-to-run-ssh-add-on-windows

In a PowerShell administrator prompt enter:

Get-Service ssh-agent

If ssh-agent is off then:

Set-Service ssh-agent -StartupType Automatic

Start-Service ssh-agent

Now ssh-agent should start up on boot and you shouldn’t need to worry about it again.

To add your private keys to ssh use the PowerShell ssh-add feature:

ssh-add <key1-path>

ssh-add <key2-path>

Add the key to ssh-agent: Ubuntu

Ensure both openssh-client and openssh-server are installed:

sudo apt install openssh-client openssh-server

To automatically add ssh keys to your ssh-agent, please see my custom script in the “Automating” section of this document.

For more on Ubuntu the openssh-server see here: https://ubuntu.com/server/docs/openssh-server

Link the key to your GitHub account:

Source: https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account

The process is relatively straightforward. Click on your logo in github -- mine is ironman’s head currently.

Click on your logo in github -- mine is ironman’s head currently.

This should bring up a menu. Click on “Settings”

Click on Settings in your GitHub profile

Click on “SSH and GPG keys” in the resulting screen.

Then click on “New SSH key”

Add your PUBLIC (not private) key in the resulting window. I do this with Vim, but you can use whatever editor or tool you like. For example, here’s how to do it with the type https://en.wikipedia.org/wiki/TYPE_(DOS_command) command line tool in Powershell:

cd
cd .ssh
type id_rsa.pub

 My public key is given in the below screenshot:

And that should be it! You now should be able to use ssh keys on github.

How to create an SSH key for WSL

Prerequisite: install the ssh keys on native windows first!

This step will cover how to copy your public and private keys to your specific instance of WSL. Make sure the permissions are tight on your private key after the copy!

https://devblogs.microsoft.com/commandline/sharing-ssh-keys-between-windows-and-wsl-2/

NOTE: I have deviated slightly from the instructions provided in the link. Notably I include a missing asterisk on the cp -r /mnt/c/Users/<username>/.ssh/* ~/.ssh and I use something other than keychain to support non-Debian distros.

Step 1 is to log into your WSL Linux instance (I’m using Ubuntu 20.04). If this is the first time you have set up .ssh keys in your WSL instance you shouldn’t see any keys.

Enter the following commands:

cd $HOME
mkdir -p .ssh
cp -r /mnt/c/Users/<username>/.ssh/* ~/.ssh

Then change the permission on each private key. If had private keys in the .ssh directory named id_rsa and id_ed25519 then I would change the permissions with these commands:

chmod 600 ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_ed25519

Do the same chmod command for additional keys (if any)

The command chmod 600 ~/.ssh/id_rsa is used to change the permissions of the file id_rsa located in the .ssh directory of the current user’s home directory.

Here’s what each part means:

·         chmod: This is a command in Unix and Unix-like operating systems that allows you to change the permissions of a file or a directory.

·         600: This is the permission level you’re setting. In this case, 600 means that the owner of the file has read and write permissions (represented by 6), and no one else has any permissions (represented by the two 0s).

·         ~/.ssh/id_rsa: This is the path to the file you’re changing permissions for. The ~ represents the home directory of the current user, .ssh is a hidden directory within the home directory (files and directories that start with a . are hidden in Unix-like systems), and id_rsa is the file within the .ssh directory.

So, in summary, this command is commonly used to secure the id_rsa file by ensuring that only the owner can read and write to it, which is important because this file typically contains a private SSH key. It’s good practice to secure your SSH keys to prevent unauthorized access.

If you have created a public key in step 1 and exported it to your GitHub repo you can use the same key for your WSL instance of Linux.

Automating

The following instructions assume the following are in your Ubuntu packages:

  • openssh-client

  • git-core

To automate to run on WSL startup you need to enter the following into your $HOME/.bashrc file (or if using zsh into the $HOME/.zshrc file). Other shells have not been tested by me, so your mileage may vary.

The full source the script is provided here:

# This script should work in bash or zsh.
# It is most often placed in the $HOME/.bashrc or $HOME/.zshrc file
# Other shells have not been tested.

#source: https://stackoverflow.com/a/18915067

SSH_ENV=$HOME/.ssh/environment

function start_agent {
    echo "Initializing new SSH agent..."

    /usr/bin/ssh-agent | sed 's/^echo/#echo/' > "${SSH_ENV}"
    chmod 600 "${SSH_ENV}"
    . "${SSH_ENV}" > /dev/null
    /usr/bin/ssh-add
}

if [ -f "${SSH_ENV}" ]; then
    . "${SSH_ENV}" > /dev/null
    ps -ef | grep ${SSH_AGENT_PID} | grep ssh-agent$ > /dev/null || {
    start_agent;
    }
else
    start_agent;
fi

Script Details

This line:

SSH_ENV=$HOME/.ssh/environment

indicates where your .ssh/environment folder lives – see Stack Exchange.

A start_agent function is then defined. A sed script is used to populate the SSH_ENV environment variable. Chmod is used to set the permissions needed on the ssh agent file (usually not necessary). The whole thing is then redirected to the Null device to avoid cluttering up the console. Finally a system call is made to /usr/sbin/ssh-add:

function start_agent {

    echo "Initializing new SSH agent..."

    /usr/bin/ssh-agent | sed 's/^echo/#echo/' > "${SSH_ENV}"

    chmod 600 "${SSH_ENV}"

    . "${SSH_ENV}" > /dev/null

    /usr/bin/ssh-add

}

Next a conditional call to ssh-add is made if it’s not already running. I’ll concede that the next operation is a bit hard to follow, but it employs a combination of psgrep, and a logical or operator as it determines whether it’s necessary to call the start_agent function. Then just in case something has stopped the process an else makes sure it still exists; the environment variable ensures we are never running more than one instance of ssh-agent:

if [ -f "${SSH_ENV}" ]; then

    . "${SSH_ENV}" > /dev/null

    ps -ef | grep ${SSH_AGENT_PID} | grep ssh-agent$ > /dev/null || {

        start_agent;

    }

else

    start_agent;

fi

Note you will either have to run

source $HOME/.bashrc

or restart your WSL session for this to be enabled.

Now you should be able to use your ssh key for whatever repo you have set up. Note that the first time you do this you will have to give permission to your local git to add files to your $HOME/known_hosts folder.

Alternatives:

Other methods of accessing Git repositories include:

Recommended:

Token-based Authentication: Instead of using a password, you can use a personal access token (PAT -- see: Managing your personal access tokens - GitHub Docs) or an OAuth  token for authentication. Many Git hosting services, such as GitHub, GitLab, and Bitbucket, support token-based authentication.

HTTPS: authentication to GitHub You can use HTTPS to clone and interact with Git repositories. However: be careful about entering a credentials via a URL because it might be included in intermediate router logs on your way over the network!

Also I have found the approach doesn’t work well with git submodules.

Sometimes recommended:

Git Protocol: The Git protocol (git://) is a lightweight protocol that doesn't require authentication. However, it's generally used for read-only access and may not be suitable for private repositories.

Anonymous Git URLs: For public repositories, you can often use anonymous URLs that do not require any authentication.

Not Recommended:

Username and Password in the URL: While not recommended for security reasons (because it exposes your credentials in the URL), you can include your username and password directly in the repository URL.

Feedback

As always, do make a comment or write me an email if you have something to say about this post!

Previous
Previous

Wireshark Introduction

Next
Next

Git Stashing