DEV Community

Bryan Primus Lumbantobing
Bryan Primus Lumbantobing

Posted on • Edited on • Originally published at bryanprim.us

Using SSH to Connect Local Git to Remote Repositories

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-agent and an SSH config file with AddKeysToAgent and UseKeychain so we only enter our passphrase once.
  • Test the connection with ssh -T git@github.com to 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

-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):
Enter fullscreen mode Exit fullscreen mode

Press enter to accept the default path or provide a custom path with the file name.

# output
Enter passphrase (empty for no passphrase):
Enter fullscreen mode Exit fullscreen mode

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]-----+
Enter fullscreen mode Exit fullscreen mode

Locate the Newly Generated SSH Key

Verify whether the key was generated successfully by running:

For public key:

# input
cat ~/.ssh/id_ed25519.pub
Enter fullscreen mode Exit fullscreen mode

Adjust the path if using a custom one.

# output
# dummy data
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB8Dkha0+XZ9sjd34fGFkeGZkHZck9TZx7Hnm0Dd9e2j bry@yourhost
Enter fullscreen mode Exit fullscreen mode

For private key:

# input
cat ~/.ssh/id_ed25519
Enter fullscreen mode Exit fullscreen mode

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-----
Enter fullscreen mode Exit fullscreen mode

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:

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

WSL using bash:

exec ssh-agent bash
Enter fullscreen mode Exit fullscreen mode

Windows (I haven't tested this):

CMD:

start-ssh-agent
Enter fullscreen mode Exit fullscreen mode

PowerShell:

Start-Service ssh-agent
Enter fullscreen mode Exit fullscreen mode

Then add the private key to the agent:

ssh-add C:\path\to\your\key
Enter fullscreen mode Exit fullscreen mode

Set Up the SSH Config File

Check if we have an existing ssh config file.

cat ~/.ssh/config
Enter fullscreen mode Exit fullscreen mode

If we don't have one, create a new config file.

touch ~/.ssh/config
open ~/.ssh/config
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

If everything goes well, we should see the following output:

# output
Hi username! You've successfully authenticated, but GitHub does not provide shell access.
Enter fullscreen mode Exit fullscreen mode

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.

  1. 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?
  2. What is ssh-agent and how does it work? We used ssh-agent to avoid entering our passphrase repeatedly, but how does it actually store and manage our keys in memory?

Links

Top comments (0)