Post Details

Setting up SSH and GPG keys for GitHub

Tutorials

Setting up SSH and GPG keys for GitHub

I find myself needing to set up SSH and GPG keys for use with GitHub, Bitbucket, GitLab and other similar services rather frequently. Here are the steps I use to set this up.

SSH Keys

Create SSH Keys

Use the below command to create SSH keys. It might be wise to "silo" your keys such that you dedicate a separate key for each computer you use and per repository, service or some other logical way. For instance, have one key for GitHub and another for Bitbucket.

$ ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_$(whoami)_$(date -f %Y-%m-%d) -C "Macbook Pro SSH Key for GitHub"

Make sure to set a passphrase! If you don't, and your key is compromised, it's a free pass for the attacker.

Get Public Key

Copy the public key from the .pub file created in the last command to your clipboard. From our previous example, it would be something like this:

$ clip < ~/.ssh/id_ed25519_jdoe_2020-12-29.pub

You can also open the file in your favorite editor (*cough vim) and simply copy it that way (assuming you can quit out of vim :p ).

Add to GitHub (or whatever service you are using)

In GitHub, or whichever service you happen to be using, go to your settings and then to SSH Keys. Many services combine SSH key and GPG key management into the same page, so don't leave the page once you find it.

Select "Add new key", and paste the key in. If you copied the whole key, it should automagically populate the key name. I personally only copy the type and the base64 encoded key leaving out the username, hostname and comment, if any, e.g. ssh-ed25519 aAbBcCdD... (see https://coolaj86.com/articles/the-ssh-public-key-format/ for a breakdown of the key format).

You will receive an email stating that a new key has been added. Likewise, if you delete a key you will also receive an email alerting you to the activity.

Set git Config

This step isn't entirely necessary, however, it does help if you plan to use multiple keys.

Open ~/.ssh/config in your favorite vim, I mean editor. Add the following, making changes where you see text surrounded by '<>'.

Host <slug>
    Hostname <github.com or your services FQDN>
    Username git
    IdentityFile <path to private key>
    IdentitiesOnly yes

Here's a breakdown:

  • Host: If you are only planning to have one key (to rule them all), you can put the FQDN of the service here, like github.com, but if you are going to silo, you might choose something more descriptive, like mycoolwidget. Then when you set up your git remote in your local repository, you should use that slug instead of github.com. This configuration will essentially alias the FQDN for the slug. The main benefit here is that you can use multiple keys with the same service. Just make different slugs that point to each key, add the keys to your service and then make sure to use the slug when you set up the git remote locally. See the example below for context.
  • Hostname: This is the FQDN that will be aliased using the "Host" slug in the line above. For instance, if your github.com repository SSH URI is git@github.com:jdoe/mycoolwidget.git you would simply use github.com here.
  • Username: This is the SSH username that will be used. Virtually all git implementations will use "git" here, but if you have some weird setup, you can change this.
  • IdentityFile: Defines the key pair to use. The private key is the file generated by ssh-keygen in step 1 above that has no extension. Keep it secret, keep it safe. But refer to it here. In our above example, it would be ~/.ssh/id_ed25519_jdoe_2020-12-29. You can use the ~ here, thats fine.
  • IdentitiesOnly: This line simply means that the host configuration should only be used with SSH keys.

You need to make sure that the config file has chmod 600, meaning that the owner of the file has read and write permissions (6), and then group and everyone have no permissions (0). You can always hit it with a quick chmod 600 ~/.ssh/config to make sure.

As an example of "siloing" your keys, assuming your project mycoolwidget is hosted on github:

# ~/.ssh/config
Host mycoolwidget
    Hostname github.com
    Username git
    IdentityFile ~/.ssh/id_ed25519_jdoe_2020-12-29_mycoolwidget
    IdentitiesOnly yes
Host mycoolapp
    Hostname github.com
    Username git
    IdentityFile ~/.ssh/id_ed25519_jdoe_2020-12-29_mycoolapp
    IdentitiesOnly yes
# set up local git repositories
# the alias would be the equivalent to 'git@github.com:jdoe/mycoolwidget.git',
# but since we are using the slug we defined in the SSH config, 
# our identity file will automatically be used
$ git remote add origin mycoolwidget:jdoe/mycoolwidget.git
$ cd ../mycoolapp
$ git remote add origin mycoolapp:jdoe/mycoolapp.git

Lastly, verify that your config works.

ssh -T <slug>

You should get a message back saying something along the lines of you've connected successfully, but shells aren't allowed.

Thats it for the SSH key peice. Now for the hard part…GPG keys.

GPG Keys

You will need the GPG command line tool for this. Make sure you install the correct version for your OS. Most linux and Mac (including Windows Subsystem for Linux) will already have gpg ready for you.

Create a new key

  1. If you don't already have keys set up for your e-mail, or if like me, you've failed to store your passphrase in your password vault and have now forgotten it, then you need to generate a new key.

    gpg --full-generate-key
    

    You will be prompted for several things:

    • Key algorithm: You can accept the default of RSA and RSA unless you are feeling frisky
    • Key Size: Anything greater than 4096 should be adequate (multiples of 1024, and not a https://xkcd.com/394)
    • Expiration: If you enter nothing, it will never expire. You should probably think twice before doing that. Values can be in days, weeks, years…e.g. 30d, 52w, 3y, etc.
    • Verify: If your selections so far are valid, type y, else bail out and start over.
    • Your email address: Enter your email address. This needs to match a verified email address on your GitHub (or whatever) account AND the email you use in your local git configuration. You can also use the privatized email address provided by GitHub, like 12345678+jdoe@users.no-reply.github.com which can be found in your email settings, but just make sure that it matches your local git config as well.
    • Enter a passphrase: As annoying as it may seem, this is crucial. If you don't set a passphrase, ANYONE can use the gpg key to sign things as though it was from you. This is all about chain of trust and if you can't establish non-repudiation, then there is no trust AND EVERYTHING WILL FALL INTO CHAOS! Seriously though, it's not THAT bad…just set a passphrase.
  2. Congratulations, you have a gpg key. Now we need to get the keyid and the ASCII armored key so we can tell our provider du jour about it. You need to copy the key ID from the output of the following, which in this example would be 3XE290DWFIXQ8L1J.

    $ gpg --list-secret-keys --keyid-format LONG
    /Users/jdoe/.gnupg/secring.gpg
    ----------------------------------------------
    sec             4096R/3XE290DWFIXQ8L1J  2020-12-29      [expires: 2021-12-29]
    uid                                                             jdoe
    ssb             4096R/CTRNZTHH95IG5CSB  2020-12-29
    
  3. Using the key ID that we just got, we need to export the key in ASCII armor format:

    $ gpg --armor --export 3XE290DWFIXQ8L1J
    -----BEGIN PGP PUBLIC KEY BLOCK-----
    gibberish
    -----END PGP PUBLIC KEY BLOCK-----
    

Copy the key output, including the -----BEGIN PGP PUBLIC KEY BLOCK----- and -----END PGP PUBLIC KEY BLOCK-----

  1. Paste the key output into your providers Add GPG Key interface

Hold your horses, we aren't done yet. Now, let's tell our local git repository to use the key

Configure git to use the key

You can do this globally for all repositories if you will use only one GPG key (i.e. only one email/git account/persona), or by repository. If you are doing this globally, you can skip step 1.

  1. In command line, cd into your git repository
  2. Get your Key ID again, just like before. Again the Key ID in the example below would be 3XE290DWFIXQ8L1J

    $ gpg --list-secret-keys --keyid-format LONG
    /Users/jdoe/.gnupg/secring.gpg
    ----------------------------------------------
    sec             4096R/3XE290DWFIXQ8L1J  2020-12-29      [expires: 2021-12-29]
    uid                                                             jdoe
    ssb             4096R/CTRNZTHH95IG5CSB  2020-12-29
    
  3. Set your GPG signing key.

    # For only the repository in the current directory
    $ git config user.signingkey 3XE290DWFIXQ8L1J
    # OR globally for all repositories. Can be overriden by repository specific config
    $ git config --global user.signingkey 3XE290DWFIXQ8L1J
    
  4. (OPTIONAL) If you want to sign all commits by default, you can configure it by repository or globally as well:

    #  Repository specific
    $ git config commit.gpgsign true
    # Global
    $ git config --global commit.gpgsign true
    
  5. Sometimes on windows, you may get a "gpg failed to sign data" message. Add the following to the end of your ~/.bashrc file:

    $ export GPG_TTY=$(tty)
    

Now run your commit again, and it should prompt you for your GPG passphrase.

Conclusion

This should be it. Your workflow should look something like this:

  1. Make changes in local branch
  2. Stage changes using git add
  3. Commit changes using git commit -m 'message'
  4. Enter your GPG passphrase to sign the commits
  5. Push changes using git push origin <branch>
  6. Enter your SSH key passphrase to push the commit

Once pushed, you should see that your commit is now reflected in the remote via the git web interface. You should also note that your commit features a verified committer a la pretty green checkmark or similar icon.

Hope you found this useful. Please let me know in the comments if you have any questions, or if this doesn't yield promised results. I wrote it from memory, not doing an actual walkthrough so cut me a bit of slack.