134

I have 2 Git servers that require 2 different SSH keys.

git clone user1@server1:blahblahblah uses ~/.ssh/id_rsa, but I need to specify which key to use depending on the server I am connecting to.

What Git command-line parameter does this job? (I am running Linux.)

Lii
  • 11,553
  • 8
  • 64
  • 88
user349302
  • 3,491
  • 7
  • 27
  • 31
  • 2
    This question is answered at http://superuser.com/questions/232373/tell-git-which-private-key-to-use . – Daira Hopwood Jun 20 '13 at 21:40
  • possible duplicate of [Specify an SSH key for git push without using ~/.ssh/config](http://stackoverflow.com/questions/7927750/specify-an-ssh-key-for-git-push-without-using-ssh-config) – Ciro Santilli OurBigBook.com Nov 16 '14 at 13:03
  • 1
    Possible duplicate of [Specify private SSH-key to use when executing shell command with or without Ruby?](http://stackoverflow.com/questions/4565700/specify-private-ssh-key-to-use-when-executing-shell-command-with-or-without-ruby) – Zaz Feb 16 '16 at 15:15

9 Answers9

211

There is another possibility. That's to set core.sshCommand, e.g.

git config --local core.sshCommand "/usr/bin/ssh -i /home/me/.ssh/id_rsa_foo"

There's one particular scenario when this strategy is particularly useful: that's when you have multiple accounts on Github, as all accounts ssh to Github as git@github.com and it uses the ssh key to determine which Github user you are. In this case neither .ssh/config nor ssh-agent will do what you want.

Update — You cannot run the above until you have a local repository, so if you're trying to clone a remote repository, you'll need to specify the key manually as per drewbie18's answer:

git clone -c core.sshCommand="/usr/bin/ssh -i /home/me/.ssh/id_rsa_foo" git@github.com:me/repo.git

Once you've cloned the repository you can use the git config command to set this permanently.

Richard Smith
  • 2,953
  • 2
  • 15
  • 15
  • 21
    This should be the selected answer. The required configuration is nicely localized in the repository which needs to use different ssh key to access different git servers. No need to modify ~/.ssh/config (and even /etc/hosts, in case of multiple accounts on the GitHub). – mr.b Aug 15 '17 at 08:21
  • 1
    There's one problem, though: you can't clone a repo before you configure it, and in order to configure it, you need to have it cloned first. OR, you could `git init` a repo, configure it locally to use correct ssh command, and then add a remote (3 steps, compared to only 1 with "git clone"). – mr.b Aug 15 '17 at 08:37
  • 3
    To clone the repo, I'd use @Guss's solution, below, by setting the GIT_SSH_COMMAND environment variable. But in normal use I prefer my solution as it avoids having to re-export that variable every time I start a new shell. – Richard Smith Aug 16 '17 at 09:23
  • lifesaver! there are tons of people showing how to use ./ssh/config to set this up, and using ssh-agent / ssh-add -K for mac. Had been using it for a year, but it suddenly stopped working for in High Sierra with latest patches. core.sshCommand worked when everything else failed. thanks! – Steve Dec 04 '17 at 03:12
  • 3
    Thank you for this elegant solution. However I just want to precise that core.sshCommand is only available for [git >= 2.10.0](https://github.com/git/git/blob/master/Documentation/RelNotes/2.10.0.txt#L83) – slonepi Jul 11 '19 at 17:24
  • You can add `-o StrictHostKeyChecking=no` to avoid asking for unknown ssh keys. – akostadinov Jul 24 '20 at 16:41
  • 1
    @akostadinov – Why would you want to disable strict host key checking? It will only ask you to add the host key the first time you ssh to that server, which will normally be the first time you clone a repo from that server. – Richard Smith Jan 08 '21 at 16:10
  • 1
    @RichardSmith, if you are building container images or deal wit various CI systems, you will often need this. – akostadinov Jan 08 '21 at 21:55
  • This config could be set per folder like ~/pro ~/personnal with include statement https://stackoverflow.com/a/48088291/7227940 – Et7f3XIV Feb 14 '21 at 22:00
  • Why not just `"ssh -i ~/.ssh/id_rsa_foo"` as suggested in the [Git documentation for `GIT_SSH_COMMAND`](https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables#_miscellaneous)? – Wolf Aug 24 '23 at 12:07
  • @Wolf – If the question is why provide full paths to ssh and the key file, then what you've written relies on both `$PATH` and `$HOME` being set correctly. If a git command is executed when they're incorrectly set, it'll fail. (Or worse, if they're set maliciously, could compromise your repository.) I doubt that's a big issue here, but I think best practice is to get into the habit of writing code that leaves the door open to such issues. – Richard Smith Aug 25 '23 at 10:08
89

If you are connecting via SSH then the key will be controlled by an SSH parameter, not a git parameter.

SSH looks in the ~/.ssh/config file for configuration parameters. Modify that file and add IdentityFile entries for the two Git servers like this:

Host server1.whatever.com
  IdentityFile /path/to/key_1
Host server2.whatever.com
  IdentityFile /path/to/key_2

This article has some more details.

Cameron Skinner
  • 51,692
  • 2
  • 65
  • 86
  • 20
    That did not answer the question. The question is, how do I give git this parameter, when ssh handles authorization? – keks Feb 04 '13 at 21:54
  • 1
    @Keks: You *cannot* pass ssh arguments via the git command line. This answer gives a workaround. There are other workarounds possible, too, for example https://git.wiki.kernel.org/index.php/GitTips#How_to_pass_ssh_options_in_git.3F. Note that that approach also does not use command line arguments to modify the ssh behavior. – Cameron Skinner Feb 04 '13 at 22:58
  • 13
    That is true, however your original answer did not answer the question. You can also use `ssh-agent` and use `ssh-add` and then just use git. When git connects via ssh, the key is already enabled. You see, there are several ways, and your original answer just dosn't really help. – keks Feb 05 '13 at 22:31
  • 2
    This is a link-only answer pretty much. Could you summarise the relevant bits of the article in your answer? – Flimm Aug 04 '16 at 10:38
  • @Flimm: Summary added. – Cameron Skinner Oct 13 '17 at 05:56
  • 2
    This answer is outdated. – nurettin Oct 20 '20 at 10:16
  • this answer should be removed really. you can use `-c` now, as per drewbie18's answer. – Hugh Perkins Feb 25 '21 at 04:54
45

Generally, you want to use ~/.ssh/config for this. Simply pair server addresses with the keys you want to use for them as follows:

Host github.com
  IdentityFile ~/.ssh/id_rsa.github
Host heroku.com
  IdentityFile ~/.ssh/id_rsa.heroku
Host *
  IdentityFile ~/.ssh/id_rsa

Host * denotes any server, so I use it to set ~/.ssh/id_rsa as the default key to use.

Zaz
  • 46,476
  • 14
  • 84
  • 101
  • 16
    This idea doesn't work if you have multiple accounts on one server like GitHub, and you want a different private key for each one. – Flimm Aug 04 '16 at 10:39
  • 2
    For multiple accounts on one server see this answer http://stackoverflow.com/questions/3225862/multiple-github-accounts-ssh-config – lexicalscope Jan 31 '17 at 12:30
25

[Update]: see my other answer for a configuration-based method with a ready-made script for you to use

In my scenario, similar to @Richard Smith scenario (whose solution, BTW, didn't work for me), I need to use different keys for the same server under different repositories.

The workaround for me was to set up the session correctly with the environment variable GIT_SSH_COMMAND, like so:

export GIT_SSH_COMMAND="ssh -o IdentitiesOnly=yes -i ~/.ssh/my-secret-identitiy"

Update:

Another thing to note here is that setting the environment variable correctly can be a hustle, so I'm using the command prompt modification facilities provided by things like Liquid Prompt or Fish Shell to hook into the shell and keep updating the environment variables according to the current directory and some rules. For example, all my personal projects that need to my personal SSH key with Gitlab are under ~/Documents/Projects/personal so when the shell hook runs pwd and finds that the current directory is under that path, it automatically sets the GIT_SSH_COMMAND variables as needed.

Guss
  • 30,470
  • 17
  • 104
  • 128
18

You can use --global or --local configuration, global will update ~/.gitconfig file, local will update configuration in the repository .git/config and override the global (~/.gitconfig) configuration

git config --local --add core.sshCommand 'ssh -i ~/.ssh/my_key'
Wolf
  • 9,679
  • 7
  • 62
  • 108
Maoz Zadok
  • 4,871
  • 3
  • 33
  • 43
17

Use ssh-add path-to-private-key it works out of the box.

khelll
  • 23,590
  • 15
  • 91
  • 109
  • 11
    Not if you have two deploy keys and they only work with a certain repositories. In that case, it may use the wrong key and say you don't have access. – cdmckay May 14 '19 at 13:43
  • 1
    I got the problem cdmckay said – Suge May 28 '19 at 05:05
14

Windows user here, I just ran into this issue and have a slightly different solution then I have read here so far. The problem I faced is that I simply wanted to clone a repo using a specific private ssh key and not have to globally configure my git config or add specific git bash settings, as I do my work in PowerShell. Essentially I just want to have some private keys sitting in my .ssh folder and I reference them in specific repos as required.

The following command works for this:

git clone -c core.sshCommand="ssh -i ~/.ssh/<PRIVATE KEY NAME>" <CLONE URL>

Essentially what this does is upon the initialization of the git repo it sets the core.sshCommand option before running the clone. So the specific ssh key you wish to use for this repo is set ONLY for this repo. This may not be an ideal solution for all cases but for what I want it is.

drewbie18
  • 321
  • 2
  • 6
  • Thank you. I wasn't aware of the `-c` option to `git clone`. This is better than setting the environment as I suggest when cloning. – Richard Smith Sep 12 '19 at 08:04
  • this should really be the accepted answer. It answers the actual question posed (and the one I wanted the answer to too....). PS it works just fine on linux too. – Hugh Perkins Feb 25 '21 at 04:52
  • (note that for other commands, you can put the `-c` before the command, eg for `git push`, do `git push -c ... etc ... push` – Hugh Perkins Feb 25 '21 at 04:52
4

Another option is to write a small script to make core.sshCommand a bit smarter - check if the current working directory has a specific SSH key configured and if so use it, otherwise - rely on the standard SSH key resolution.

Here is my first version:

#!/bin/bash
key="$(git config ssh.key)"
if [ -n "$key" ]; then
        ssh -o IdentitiesOnly=yes -i "$key" "$@"
else
        ssh "$@"
fi

Then set it up as the global git SSH command:

chmod 755 ~/.local/bin/git-ssh-command
git config --global core.sshCommand ~/.local/bin/git-ssh-command

(~/.local/bin is the current standard for "place user scripts here" on SystemD operating systems)

After you set this up, you can configure any repository to use a specific SSH key by setting the configuration option ssh.key:

git config --local ssh.key ~/.ssh/my-non-default-private-key

Additional Optional Tricks

  • Set the global ssh.key to have a "default fallback to non-default SSH key" or something.
  • As git executes core.sshCommand in the root directory of the repository, your custom git-ssh-command can look at that and have some heuristics about directory names. This can be done in the else section so heuristics only kick in if there is no specific key in ssh.key.
  • You can add git remote -v check to add heuristics based on the the remotes, like in Eike's script
  • If you want to look at the repository remotes but have multiple remotes that need different keys, you can add remote="$1:$(sed "s,.* ,,;s,',,g"<<<"$2")" at the beginning of the script to resolve the remote being operated on - and check against that ($remote would look like the middle column in git remote -v output).

Update:

Here's a version of the custom SSH command that looks at the remote URL and does some heuristics - in my case, I have several different identities with the same public git hosting service (one for work, one for personal, etc) and I can select the correct identity by looking at the remote URL (examining the Gitlab group or the Github organization, etc):

#!/bin/bash
while ! [[ "$1" =~ "git@" ]]; do shift; done # git may send ssh options
if [[ "$2" =~ ^git-lfs-authenticate.* ]]; then # reconstruct url for git-lfs
        remote="$1:$(awk '{print$2}'<<<"$2")"
else # reconstruct url for standard git commands
        remote="$1:$(sed "s,.* ,,;s,',,g"<<<"$2")"
fi
echo "Detected $remote from $@" >&2
key="$(git config ssh.key)"
if [ -n "$key" ]; then # use specified SSH key, if set
        ssh -o IdentitiesOnly=yes -i "$key" "$@"
elif [[ "$remote" == git@gitlab.com:my-company* ]]; then # use company id
        ssh -o IdentitiesOnly=yes -i ~/.ssh/company-id "$@"
elif [[ "$remote" =~ git@bitbucket.org:.*other-org.* ]]; then # bitbucket has weird urls
        ssh -o IdentitiesOnly=yes -i ~/.ssh/custom-org-key "$@"
else # otherwise use whatever the agent has (my personal key)
        ssh "$@"
fi
Guss
  • 30,470
  • 17
  • 104
  • 128
1

The other answers inspired me to write a little script that chooses the ssh key depending on command line options or (if present) the values of git remote -v. Hope it helps!

To actually answer the question: Use gat.

See also https://bitbucket.org/eikerobert/gat/src/master/

#!/bin/bash

usegat=false

for VAR in "$@"
do
    if [ "$VAR" != "${VAR/git@bitbucket.org:myaccount/}" ]; then
        usegat=true
    fi
done

if [ $usegat=false ]; then
   /usr/bin/git rev-parse --is-inside-work-tree >/dev/null 2>&1
   isinsidegitrepo=$?
    #echo $isinsidegitrepo
   if [ $isinsidegitrepo = 0 ]; then
       remote=`/usr/bin/git remote -v`
       if [ "$remote" != "${remote/git@bitbucket.org:myaccount/}" ]; then
           usegat=true
       fi
   fi
fi


if [ $usegat = true ]; then
    # echo "TRUE"
    /usr/bin/git -c core.sshCommand="/usr/bin/ssh -i /home/myaccount/.ssh/mykey" "$@"
else
     #echo "FALSE"
    /usr/bin/git "$@"
fi
Eike
  • 359
  • 3
  • 8