# Github, ssh keys and 2FA

URL: https://joaocosta.dev/blog/6
Published: 2020-09-03 · 4 min read

I recently found myself setting up 2FA on my GitHub account and for that I started using the **ssh protocol** instead of the default **https protocol**. This is a quick guide explaining how and why you'd want to do that too.

## Introduction

I have a little project going on where I use a
[raspberry pi zero](https://www.raspberrypi.org/blog/raspberry-pi-zero/) as a
dedicated password manager. It could more accurately be described as a secrets
keeper/manager, but I use it mostly for passwords.  
 Anyway, I wanted to set up 2FA for as many services as I could (I'm currently using
[FreeOTP+](https://f-droid.org/en/packages/org.liberty.android.freeotpplus/) on android
and it has everything I need/want), so I started setting them up. Both my raspberry
pi and my android have the OTPs for redundancy.

At some point, I set up
[2FA on my Github account](https://help.github.com/en/github/authenticating-to-github/securing-your-account-with-two-factor-authentication-2fa)
and all seemed well, until I tried pushing to one of my GitHub repositories from
the command line.

## 2FA and the command line

It seems like 2FA and git from the command line don't play well together. For
example, if you try to clone a private repository, your credentials will not be
accepted. You'll get an "invalid username or password" error message. This
happens because GitHub uses the **https protocol** by default and recommends it
for its users, because it usually works _out of the box_.

If you want to use 2FA (you should) with git on the command line, you'll have to
use one of these solutions.

### Solution 1: Create a personal access token

You should do this if you intend to keep using the **https protocol** for your
GitHub operations. GitHub has a nice step-by-step
[guide](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line)
on their help page explaining how to do this, but the tl;dr is:

- Go to your account settings;
- Go to the **Developer settings** tab;
- Go to the **Personal access tokens** tab;
- Press the **Generate new token** button;
- Give it a name and the permissions you want/need (e.g.: repo);
- Copy the token and keep it safe like you would for your password (the token
  isn't recoverable).

Now to use the token, you just need to provide it during the authentication
process instead of your password.

I don't like this solution because you have to keep track of 2 different
passwords (more vulnerable) and this new _"token password"_ defeats the purpose
of 2FA (since it bypasses the 2FA temporary code). You can mitigate these
problems by limiting how much _power_ the access token has (give it fewer
permissions), but it still isn't optimal.

### Solution 2: Use the **ssh protocol**

To use the **ssh protocol**, you'll need an **ssh key pair**. While browsing the
web, I came across this amazing
[tutorial](https://happygitwithr.com/ssh-keys.html), which helped me understand
the subject better and quickly set everything up correctly.

I advise you to read it, but if you're in a hurry and on Linux, you can just
copy the commands below (which were taken from the tutorial linked above) and
follow the on-screen instructions:

```bash
# this will ask for a filename and an optional passphrase for the key (you should
# provide it one)
ssh-keygen -t rsa -b 4096 -C "USEFUL-COMMENT"

# this makes sure the ssh-agent is running
eval "$(ssh-agent -s)"
# add your key to the ssh-agent (change the path to the key if you didn't use
# the default)
ssh-add ~/.ssh/id_rsa
```

After generating your key, you just need to associate the generated public key
with your GitHub account. To do this, you need to go to your GitHub account
settings, access the **SSH and GPG keys** tab, and copy the contents of your
public key to a new **ssh key**, e.g. using:
`xclip -sel clip < ~/.ssh/id_rsa.pub` to insert the key contents into your
clipboard.

Now that you have associated your **ssh key** with your account, you just need
to start using the **ssh protocol** versions of your remote links.

Imagine you have the following repository:
`https://github.com/leereilly/games.git`. It is using the **https protocol**.
The equivalent **ssh protocol** link is: `git@github.com:leereilly/games.git`.
As you can see, it is really easy to convert from one **protocol** to another. I
even found a [small bash script](https://gist.github.com/m14t/3056747) to make
the conversion of your repository's remotes to the **ssh protocol**.

```bash
#/bin/bash
#-- Script to automate https://help.github.com/articles/why-is-git-always-asking-for-my-password

REPO_URL="$(git remote -v | grep -m1 '^origin' | sed -Ene's#.*(https://[^[:space:]]*).*#\1#p')"
if [ -z "$REPO_URL" ]; then
  echo "-- ERROR:  Could not identify Repo url."
  echo "   It is possible this repo is already using SSH instead of HTTPS."
  exit
fi

USER="$(echo $REPO_URL | sed -Ene's#https://github.com/([^/]*)/(.*).git#\1#p')"
if [ -z "$USER" ]; then
  echo "-- ERROR:  Could not identify User."
  exit
fi

REPO="$(echo $REPO_URL | sed -Ene's#https://github.com/([^/]*)/(.*).git#\2#p')"
if [ -z "$REPO" ]; then
  echo "-- ERROR:  Could not identify Repo."
  exit
fi

NEW_URL="git@github.com:$USER/$REPO.git"
echo "Changing repo url from "
echo "  '$REPO_URL'"
echo "      to "
echo "  '$NEW_URL'"
echo ""

git remote set-url origin "$NEW_URL"

echo "Success"
```

This is the solution that I went with. I believe the only problem you can have
while using the **ssh protocol** is having the connection to port 22 (ssh port)
blocked is in your network. I've never had that problem, but GitHub suggests
that you may still be able to
[use **ssh** over the **https** port](https://help.github.com/en/github/authenticating-to-github/using-ssh-over-the-https-port)
if you ever find yourself in such a situation.

## Conclusions

This was mostly a quick PSA to get more people to use the **ssh protocol** and
2FA on github. It's easy and it'll keep your accounts more secure.  
Stay safe :P
