This is cross-post from my blog: https://www.bryanprim.us/blogs/using-ssh-to-connect-local-git-to-remote-repositories
Visit original post for better reading experience where mermaid diagram properly rendered
TL;DR
- SSH uses a public and private key pair instead of tokens, making it more secure and convenient for Git authentication.
- Generate keys with
ssh-keygen -t ed25519, then add the public key to our remote repository platform (GitHub, GitLab, Bitbucket). - Use
ssh-agentand an SSH config file withAddKeysToAgentandUseKeychainso we only enter our passphrase once. - Test the connection with
ssh -T git@github.comto verify everything is working.
GitHub, GitLab, Bitbucket and many more are platforms to host remote repositories using Git.
The two most common ways to connect to a remote repository from local Git are HTTPS and SSH.
Each has its own advantages and disadvantages, but throughout this blog post we will focus on SSH since it is the most commonly used method and it offers many benefits compared to HTTPS.
Note: This post assumes git is installed on the local machine. It can also be used as a practical guide for setting up a new SSH connection to a remote repository. With existing SSH keys, jump straight to Generating SSH Keys.
SSH and Git
SSH is a protocol that allows us to securely communicate between a local machine and a remote host. Git is a distributed version control system designed to track changes in files and directories over time.
Git is a collaboration tool that allows us to work on projects collaboratively, and using SSH will help us communicate our changes to other people in a secure way.
Benefits of Using SSH over HTTPS
Security
When using HTTPS to connect to a remote repository, we typically use a Personal Access Token (PAT) for authentication. Platforms like GitHub deprecated password based HTTPS authentication back in 2021, so tokens are now the standard. The token and data are encrypted in transit, so they’re not easily readable by anyone on the network. However, we still need to enter or cache our token for each session, and if the token is leaked or stolen, anyone with it can access our repositories until we revoke it.
When using SSH, we use a private and public key pair for authentication, which works differently:
- Private Key is stored only on our local machine. Think of it as a digital signature unique to us. It is encrypted and often secured with a passphrase.
- Public Key is stored on the remote server. This is what our private key "unlocks" to prove it is really us.
sequenceDiagram
participant Local as Our Machine
participant Server as Remote Server
Local->>Server: Connection request
Server->>Local: Send challenge (newly generated)
Note over Local: Solve challenge using Private Key
Local->>Server: Send response
Note over Server: Verify response using Public Key
Server->>Local: Authenticated
Our local machine uses the private key to prove our identity without sending the private key itself over the network. Instead, the server sends a challenge to our local machine that only our private key can solve. This challenge is newly generated every time we try to connect to the remote server. After our machine solves the challenge, the server verifies the response using our public key. If it matches, we are authenticated. Because the challenge changes every time, intercepting the network traffic won't help an attacker gain access.
Convenience
Using SSH is more convenient than HTTPS for connecting to a remote server because it eliminates the need to enter our token every time. Instead, we can use an SSH agent, which securely stores our passphrase in memory and handles the authentication process for subsequent connections.
Generate SSH Keys
There are several algorithms that can be used to generate SSH keys. In this blog post, we will use Ed25519 (Edwards-curve Digital Signature Algorithm) because it is modern and faster than other algorithms.
Check Existing SSH Keys
Before generating a new SSH key, we should check if we already have one because we don't want to accidentally overwrite an existing key.
### input
ls -al ~/.ssh
Look for id_ed25519 and id_ed25519.pub as these are the default file names for SSH key generation.
### output
total 40
drwx------@ 7 username staff 224 Nov 5 15:45 .
drwxr-x---+ 42 username staff 1344 Nov 5 19:18 ..
-rw-r--r--@ 1 username staff 185 Sep 11 19:00 config
-rw-------@ 1 username staff 464 Sep 11 18:24 id_ed25519
-rw-r--r--@ 1 username staff 104 Sep 11 18:24 id_ed25519.pub
-rw-------@ 1 username staff 1842 Oct 25 16:47 known_hosts
-rw-------@ 1 username staff 1106 Oct 25 16:43 known_hosts.old
If we see id_ed25519 and id_ed25519.pub then we already have SSH keys. If we don't want to overwrite them, we will need to specify a different path for the next key generation. If we want to use our existing SSH keys, we can skip the creation step and continue to Connecting to a remote repository.
Create a New SSH Key
# input
ssh-keygen -t ed25519 -C "your_email@example.com"
-t is the algorithm type, -C (optional) is custom comment.
# output
Generating public/private ed25519 key pair.
Enter file in which to save the key (/Users/username/.ssh/id_ed25519):
Press enter to accept the default path or provide a custom path with the file name.
# output
Enter passphrase (empty for no passphrase):
Passphrase is optional, but I highly recommend entering one to protect the use of our private SSH key.
We will be using ssh-agent later so that we don't have to enter the passphrase every time we make a connection.
# output
# dummy data
Your identification has been saved in /Users/username/.ssh/id_ed25519
Your public key has been saved in /Users/username/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:nds92nn/dsNDsadbjndkansdjsknadkj your_email@example.com
The key's randomart image is:
+--[ED25519 256]--+
| o.**|
| o**|
| ++**|
| . +=*|
| S. . +o*o|
| o..=.o.=|
| --o+o+o|
| .. o=*=|
| o+E=|
+----[SHA256]-----+
Locate the Newly Generated SSH Key
Verify whether the key was generated successfully by running:
For public key:
# input
cat ~/.ssh/id_ed25519.pub
Adjust the path if using a custom one.
# output
# dummy data
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB8Dkha0+XZ9sjd34fGFkeGZkHZck9TZx7Hnm0Dd9e2j bry@yourhost
For private key:
# input
cat ~/.ssh/id_ed25519
Adjust the path if using a custom one.
# output
# dummy data
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktZDI1NTE5AAAAIB8Dkha0+XZ9sjd34fGFkeGZkHZck9TZx7Hnm
0Dd9e2j+AAAAAAAAAAAAAAAAAAEAAAAIB8Dkha0+XZ9sjd34fGFkeGZkHZck9TZx7
Hnm0Dd9e2j+AAAAAlkdiJHDU6FOSZDa7yxptjMNxzHxgtid7YKm2GFjdjXaAAAAAE
2jxKVmksl3Dbjoef1H5/tf1sgYnJe+JqfgvUhrBgkgmfhdAAAAAAEAAAXLS1mhdhd
bHJdkfHZ9GFFX3NNMgVdk+KfbkhTgyg==
-----END OPENSSH PRIVATE KEY-----
If everything is done correctly, we should see our public and private key in the output as shown above.
Connect to a Remote Repository
Add Public Key to Remote Repository
We need to add our public key to the remote repository platform we are using. Visit one of these links:
- GitHub: https://github.com/settings/keys
- GitLab: https://gitlab.com/-/profile/keys
- Bitbucket: https://bitbucket.org/account/settings/ssh-keys/
Note: Links provided may become invalid in the future as I don't have control over any changes the platforms might make.
Click New SSH Key or Add SSH Key.
Give the key a descriptive name.
Copy the public key from the local machine:
# input
pbcopy < ~/.ssh/id_ed25519.pub
Adjust the path if using a custom one.
Paste the public key in the Key field and save.
Add Our Private SSH Key to the ssh-agent
First, we need to start the ssh-agent running in the background.
Mac:
exec ssh-agent zsh
WSL using bash:
exec ssh-agent bash
Windows (I haven't tested this):
CMD:
start-ssh-agent
PowerShell:
Start-Service ssh-agent
Then add the private key to the agent:
ssh-add C:\path\to\your\key
Set Up the SSH Config File
Check if we have an existing ssh config file.
cat ~/.ssh/config
If we don't have one, create a new config file.
touch ~/.ssh/config
open ~/.ssh/config
Update our config file based on the remote host we are using.
For example:
# For GitHub
Host github.com
AddKeysToAgent yes
UseKeychain yes # macOS only
IdentityFile ~/.ssh/id_ed25519
UseKeychain will save our passphrase in the keychain, which will be used to unlock our private key when we make a connection.
This way, we don’t have to enter the passphrase again every time we start a new session or restart our computer.
Test the SSH Connection
# input
# github
ssh -T git@github.com
# gitlab
ssh -T git@gitlab.com
# bitbucket
ssh -T git@bitbucket.org
If everything goes well, we should see the following output:
# output
Hi username! You've successfully authenticated, but GitHub does not provide shell access.
Now we are ready to connect, clone repositories, and push changes to our remote repository!
Wrapping Up
To recap what we covered in this post: we started by understanding why SSH is a better choice than HTTPS for connecting to remote repositories. Then we generated an Ed25519 key pair, added the public key to a remote repository platform, configured the SSH agent and SSH config file for convenience, and finally tested the connection. We should now be able to push, pull, and clone repositories over SSH without entering credentials every time.
Questions Worth Exploring
These topics go deeper into some of the concepts we touched on in this post. I will cover them in future posts.
- Why is a passphrase necessary? We mentioned that a passphrase protects our private key, but what exactly happens if someone gets access to our private key without a passphrase?
-
What is ssh-agent and how does it work? We used
ssh-agentto avoid entering our passphrase repeatedly, but how does it actually store and manage our keys in memory?
Top comments (0)