How to specify an ssh-key file with the Git command

If you want to use an ssh-key file whenever you run the ssh command, one convenient way to do this is to use the -i option.


ssh -i ~/.ssh/thatuserkey.pem thatuser@myserver.com

This is pretty neat. It’s simple, elegant, and highly intuitive. I want to do the same thing with the Git command like this:


git -i ~/.ssh/thatuserkey.pem clone thatuser@myserver.com:/git/repo.git

Unfortunately, there is no such -i option in the git command. Bummer.

I’ve looked around but I can’t find a solution like this. There are two options I can think of: 1) use GIT_SSH and 2) use a wrapper script.

Option 1: Use the GIT_SSH environment variable

The GIT_SSH option will allow you to specify a key file with the Git command like this:


PKEY=~/.ssh/thatuserkey.pem git clone thatuser@myserver.com:/git/repo.git

where ~/.ssh/thatuserkey.pem is the keyfile you want to use.

To make this work, it needs some pre-configuration. The first step is to create a shell script that contains the following.

~/ssh-git.sh


#!/bin/sh
if [ -z "$PKEY" ]; then
# if PKEY is not specified, run ssh using default keyfile
ssh "$@"
else
ssh -i "$PKEY" "$@"
fi

The script needs to be executable so do a chmod +x on it.

Next step is to set the value of the GIT_SSH variable to the path of the script above. The variable will need to be exported to the shell environment.


export GIT_SSH=~/ssh-git.sh

Now every time you run the git command, the keyfile you set to the PKEY variable is passed to the shell script GIT_SSH is pointing to. This will then allow Git to connect using that key file.


PKEY=~/.ssh/thatuserkey.pem git clone thatuser@myserver.com:/git/repo.git

From hereon, every time you run the Git command, you can freely choose any key file you want to use by setting the PKEY variable.[1]

If you run the git command without the PKEY line, the GIT_SSH script will still run since this is exported to the shell environment. The script has a fail safe to avoid using the -i option if there was no keyfile set so that it can still run using the default keyfile.

Be careful when exporting PKEY to the shell environment because GIT_SSH will use whatever value it is set to even if you don’t specify it with the git command. This brings another problem with GIT_SSH exported to the environment since Git will always use this when it runs. So you need to be constantly conscious that you have this set. You can always chain the GIT_SSH setting with the git command to avoid exporting it to the environment, but at the expense making the entire command longer.

The PKEY-line usage works well except that the setting of PKEY together with the git command is somehow unconventional.[2]

If you find this unintuitive, there is another option.

Option 2: Use a wrapper script

The -i option with ssh is pretty neat and elegant. You supply the -i option to choose the key file you want to use. If you don’t use the option, ssh will fall back to use the default ssh-key file.

To use the -i option with the Git command, we need to write a wrapper script. The wrapper script will then allow us to set the usage we like and that is to mimic the -i option in ssh.

The usage will be something like this:


git.sh -i ~/.ssh/thatuserkey.pem clone thatuser@myserver.com:/git/repo.git

where git.sh is the wrapper script.

The only thing you need to do is create that script, put it in your PATH, and you’re all set.

To get the code, you can download it from here or cut-and-paste that below.

git.sh


#!/bin/bash

# The MIT License (MIT)
# Copyright (c) 2013 Alvin Abad

if [ $# -eq 0 ]; then
    echo "Git wrapper script that can specify an ssh-key file
Usage:
    git.sh -i ssh-key-file git-command
    "
    exit 1
fi

# remove temporary file on exit
trap 'rm -f /tmp/.git_ssh.$$' 0

if [ "$1" = "-i" ]; then
    SSH_KEY=$2; shift; shift
    echo "ssh -i $SSH_KEY \$@" > /tmp/.git_ssh.$$
    chmod +x /tmp/.git_ssh.$$
    export GIT_SSH=/tmp/.git_ssh.$$
fi

# in case the git command is repeated
[ "$1" = "git" ] && shift

# Run the git command
git "$@"

The wrapper script can fail gracefully. If you don’t specify the -i option, it will run git using your default key-file.

This wrapper script uses the same principle of the GIT_SSH environment variable. But instead of pre-setting this up manually, the wrapper script sets this up on the fly every time it runs the actual git command.

Other options

There are other ways to use different ssh-keys with the Git command. There is this $HOME/.ssh/config file where you can map different keys to hosts you want to connect to. But this method doesn’t allow you to choose any key file at will when you need to run the git command. The keys need to be pre-defined in the config file.

You can also use ssh-agent to programmatically add the key you want to use. I also wrote a wrapper script that uses ssh-agent to allow the -i option. But it turned out to be more complex than GIT_SSH way. I’ll probably post that to show how it can be done that way.

With all the different methods available, none is necessarily better than the other. It will all depend on the circumstances and of course your personal taste.

Alvin


[1] I prefer this kind of control in my workflow because I use different keys for different servers I use. I have a different key for my servers at work and different keys for my personal servers and public sites (like Github). It works the same with passwords. You don’t use the same password on your Facebook and bank accounts.


[2] Personally, I find this all right since I am used to this usage. I run a lot of scripts and make commands that require environment settings. But I don’t like the idea of exporting all of them to the shell environment and leaking them everywhere so I only specify them with the command.

28 thoughts on “How to specify an ssh-key file with the Git command

  1. Looking at your Option 2 Bash script:
    Regarding multi-process parallel trouble, would it make more sense to avoid using EXPORT, and instead use
    GIT_SSH=/tmp/.git_ssh.$$ git “$@”
    ?

    Cheers for the useful blog-post.

    • There is no “multi-process parallel trouble” because the GIT_SSH environment is private in that instance of shell. It won’t clash with other calls or instances of the shell script.

  2. You don’t have enough quotes. It should be the following:

    SSH_KEY=”$2″; shift; shift
    echo “ssh -i \”$SSH_KEY\” \$@” > /tmp/.git_ssh.$$

    Otherwise, your script will fail with directories or file names with spaces.

  3. Pingback: Day 7 – Useful Android terminal environment, with a working hub of a git | Adaptive Intelligence

  4. Pingback: Fixed: OS X Terminal "must have" utilities #answer #computers #dev | InfoBot

  5. You can use ssh config to specify the IdentityFile you’d like to use with a certain host.
    i.e. create/append ~/.ssh/config
    with the following block:
    Host myserver.com
    IdentityFile ~/.ssh/thatuserkey.pem
    User thatuser

    then use git clone like this
    git clone ssh://myserver.com:/git/repo.git

    Hope this helps.

    • i wasn’t able to use git clone to get a repo from my AWS and could not figure out why. when i opened /.ssh/config it turns out it had that info but had the wrong key specified. i changed it and bam, it worked. thanks for posting!

  6. Pingback: » Apple:OS X Terminal “must have” utilities [on hold]

  7. Pingback: git指定ssh密钥文件 | 哈喽哈咯

  8. It would be nice if the GIT_SSH environment variable can be set per repo so it wouldn’t be a hassle having to prevent it to the command everytime I want to push/pull to the remote repo.

  9. Pingback: 【Git】如何在git命令中指定ssh | 秀品折

  10. You really should be more careful about giving advice on the Internet. I followed your instructions and afterwards, I could not run the git commands the normal way, even after doing an export GIT_SSH=””.

    I even uninstalled git and reinstalled and I still got this error when trying to do a clone or pull:

    Cloning into ”…
    error: cannot run : No such file or directory
    fatal: unable to fork

    I had to reboot the server in order to fix this problem. Imagine if this had been a super important server that would have caused a lot of trouble if taken down, even for a couple of minutes.

    It is a cool trick, however I would suggest that in the future, always have a method of reverting the actions.

    You should have a disclaimer at the very beginning: WARNING, if you follow these instructions, you will have to reboot to revert to default behavior.

    • I mentioned this warning in my post above:

      “This brings another problem with GIT_SSH exported to the environment since Git will always use this when it runs. So you need to be constantly conscious that you have this set. You can always chain the GIT_SSH setting with the git command to avoid exporting it to the environment, but at the expense making the entire command longer.”

      You cannot set GIT_SSH to an empty string to unset it. It is not the same thing. You have to use the unset command (unset GIT_SSH).

      You should have simply logged out instead of rebooting your server or uninstalling git to clear your GIT_SSH environment. Rebooting servers or uninstalling applications at the first sign of trouble is usually not a good idea unless you don’t know anymore what else to do. Also, if it is an important server like in production, you don’t run something you haven’t tested first on a development machine.

  11. Please disregard the part about rebooting… What I think would have worked is if I had killed my shell and opened another one. I think that those environment variables were the trouble and they would have died when I opened a new terminal.

    I thought that something was persistent… being somehow cached by git.

    But I hope that you do put a warning at the beginning so that people will know what to do if this happens to them.

  12. I would like to add… this trick is awesome! There are many “tutorials” out there on this subject, but pretty much all of them are conditional… such as the author using things that are specific to their distro (like gnome key ring).

    Then there are all of the incomplete tips on sites like Stack Exchange. If you have a situation where you are using SSH keys AND you are using a non standard port, using Alvin’s guide and my tip below will get you out of the woods.

    Alvin’s method should work on any distro. And if you don’t want to run into the trouble that I encountered, use Max Barraclough’s idea. I did it like this:

    GIT_SSH=/home/*user*/.ssh/git_wrapper PKEY=/home/*user*/.ssh/git_key git pull

    As long as your /*repodir*/.git/config file has an entry something like this:

    [remote “origin”]
    url = ssh://username@repo.server:1234/repo/directory

    it will work just fine, even if you have outgoing port 22 blocked on the firewall, and you have a non standard port like in the example (1234). Notice that the command at the end is just ‘git pull’. It will get what it needs from the repo ‘config’ file (assuming that you have already cloned this repo initially, or you created the config file yourself).

    Thanks Alvin!

  13. Alvin, I did read your comments… sorry for jumping on your case, and yes… I should have unset the ENV variable, but sometimes I forget things like that. I certainly would not have done this on a production machine… I was thinking more like a dev server where developers would be yelling at you if you had to reboot.

    My suggestion above chains the ENV variable just like you suggested.

  14. Pingback: git clone ssh identity file not accessible – 1OO Club

  15. Pingback: How to specify which SSH key to use within git for git push in order to have gitorious as a mirror? - QuestionFocus

Leave a reply to Alvin Abad Cancel reply