[git] How to specify the private SSH-key to use when executing shell command on Git?

A rather unusual situation perhaps, but I want to specify a private SSH-key to use when executing a shell (git) command from the local computer.

Basically like this:

git clone [email protected]:TheUser/TheProject.git -key "/home/christoffer/ssh_keys/theuser"

Or even better (in Ruby):

with_key("/home/christoffer/ssh_keys/theuser") do
  sh("git clone [email protected]:TheUser/TheProject.git")
end

I have seen examples of connecting to a remote server with Net::SSH that uses a specified private key, but this is a local command. Is it possible?

This question is related to git bash shell ssh

The answer is


None of these solutions worked for me.

Instead, I elaborate on @Martin v. Löwis 's mention of setting a config file for SSH.

SSH will look for the user's ~/.ssh/config file. I have mine setup as:

Host gitserv
    Hostname remote.server.com
    IdentityFile ~/.ssh/id_rsa.github
    IdentitiesOnly yes # see NOTES below

And I add a remote git repository:

git remote add origin git@gitserv:myrepo.git

And then git commands work normally for me.

git push -v origin master

NOTES

  • The IdentitiesOnly yes is required to prevent the SSH default behavior of sending the identity file matching the default filename for each protocol. If you have a file named ~/.ssh/id_rsa that will get tried BEFORE your ~/.ssh/id_rsa.github without this option.

References


Other people's suggestions about ~/.ssh/config are extra complicated. It can be as simple as:

Host github.com
  IdentityFile ~/.ssh/github_rsa

With git 2.10+ (Q3 2016: released Sept. 2d, 2016), you have the possibility to set a config for GIT_SSH_COMMAND (and not just an environment variable as described in Rober Jack Will's answer)

See commit 3c8ede3 (26 Jun 2016) by Nguy?n Thái Ng?c Duy (pclouds).
(Merged by Junio C Hamano -- gitster -- in commit dc21164, 19 Jul 2016)

A new configuration variable core.sshCommand has been added to specify what value for GIT_SSH_COMMAND to use per repository.

core.sshCommand:

If this variable is set, git fetch and git push will use the specified command instead of ssh when they need to connect to a remote system.
The command is in the same form as the GIT_SSH_COMMAND environment variable and is overridden when the environment variable is set.

It means the git pull can be:

cd /path/to/my/repo/already/cloned
git config core.sshCommand 'ssh -i private_key_file' 
# later on
git pull

You can even set it for just one command like git clone:

git -c core.sshCommand="ssh -i private_key_file" clone host:repo.git

This is easier than setting a GIT_SSH_COMMAND environment variable, which, on Windows, as noted by Mátyás Kuti-Kreszács, would be

set "GIT_SSH_COMMAND=ssh -i private_key_file"

If none of the other solutions here work for you, and you have created multiple ssh-keys, but still cannot do simple things like

git pull

then assuming you have two ssh key files like

id_rsa
id_rsa_other_key

then inside of the git repo, try:

# Run these commands INSIDE your git directory
eval `ssh-agent -s`
ssh-add ~/.ssh/id_rsa
ssh-add ~/.ssh/id_rsa_other_key

and also make sure your github default username and userid are correct by:

# Run these commands INSIDE your git directory
git config user.name "Mona Lisa"
git config user.email "[email protected]"

See https://gist.github.com/jexchan/2351996 for more more information.


Way better idea to add that host or ip to the .ssh/config file like so:

Host (a space separated list of made up aliases you want to use for the host)
    User git
    Hostname (ip or hostname of git server)
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/id_rsa_(the key you want for this repo)

A lot of good answers, but some of them assume prior administration knowledge.

I think it is important to explicitly emphasize that if you started your project by cloning the web URL - https://github.com/<user-name>/<project-name>.git
then you need to make sure that the url value under [remote "origin"] in the .git/config was changed to the SSH URL (see code block below).

With addition to that make sure that you add the sshCommmand as mentioned below:

user@workstation:~/workspace/project-name/.git$ cat config
[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
    sshCommand = ssh -i ~/location-of/.ssh/private_key -F /dev/null <--Check that this command exist
[remote "origin"]
    url = [email protected]:<user-name>/<project-name>.git  <-- Make sure its the SSH URL and not the WEB URL
    fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
    remote = origin
    merge = refs/heads/master

Read more about it here.


for the gitlab RSAAuthentication yes

Host gitlab.com
  RSAAuthentication yes
  IdentityFile ~/.ssh/your_private_key_name
  IdentitiesOnly yes

doc is here


To sum up answers and comments, the best way to set up git to use different key files and then forget about it, which also supports different users for the same host (e.g. a personal GitHub account and a work one), which works on Windows as well, is to edit ~/.ssh/config (or c:\Users\<your user>\.ssh\config) and specify multiple identities:

Host github.com
HostName github.com
IdentityFile /path/to/your/personal/github/private/key
User dandv

Host github-work
HostName github.com
IdentityFile /path/to/your/work/github/private/key
User workuser

Then, to clone a project as your personal user, just run the regular git clone command.

To clone the repo as the workuser, run git clone git@github-work:company/project.git.


When you need to connect to github with a normal request (git pull origin master), setting the Host as * in ~/.ssh/config worked for me, any other Host (say, "github" or "gb") wasn't working.

Host *
    User git
    Hostname github.com
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/id_rsa_xxx

The problem with this method is, at least when running by bash.exe on Windows, that it will create a new process every time which will remain dormant.

ssh-agent bash -c 'ssh-add /somewhere/yourkey; git clone [email protected]:user/project.git'

If you want want to use that for syncig repo on schedule then you need to add "&& ssh-agent -k" at the end.

Something like:

ssh-agent bash -c 'ssh-add C:/Users/user/.ssh/your_key; git -C "C:\Path\to\your\repo" pull && ssh-agent -k' 

ssh-agent -k will kill the process when it's done.


Contents of my_git_ssh_wrapper:

#!/bin/bash

ssh -i /path/to/ssh/secret/key $1 $2

Then you can use the key by doing:

GIT_SSH=my_git_ssh_wrapper git clone [email protected]:TheUser/TheProject.git

GIT_SSH_COMMAND="ssh -i /path/to/git-private-access-key" git clone $git_repo

or

export GIT_SSH_COMMAND="ssh -i /path/to/git-private-access-key"
git clone REPO
git push

You need to create a ~/.ssh/config as below

Host <Your bitbucket server>
User <userid>
Hostname <Your bitbucket server as above>
IdentitiesOnly yes
IdentityFile ~/.ssh/id_rsa<file> This is your private key file

permission as below

-rw------- $HOME/.ssh/config

Add your public key into your git (cat ~/.ssh/id_rsa_pub [or simillar name])

and then git clone as below

git clone ssh://[email protected]/userid/test.git

You can try sshmulti npm package for maintaining multiple ssh key.


Many of these solutions looked enticing. However, I found the generic git-wrapping-script approach at the following link to be the most useful:

How to Specify an ssh Key File with the git command

The point being that there is no git command such as the following:

git -i ~/.ssh/thatuserkey.pem clone [email protected]:/git/repo.git

Alvin's solution is to use a well-defined bash-wrapper script that fills this gap:

git.sh -i ~/.ssh/thatuserkey.pem clone [email protected]:/git/repo.git

Where git.sh is:

#!/bin/bash

# The MIT License (MIT)
# Copyright (c) 2013 Alvin Abad
# https://alvinabad.wordpress.com/2013/03/23/how-to-specify-an-ssh-key-file-with-the-git-command

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 "$@"

I can verify that this solved a problem I was having with user/key recognition for a remote bitbucket repo with git remote update, git pull, and git clone; all of which now work fine in a cron job script that was otherwise having trouble navigating the limited-shell. I was also able to call this script from within R and still solve the exact same cron execute problem (e.g. system("bash git.sh -i ~/.ssh/thatuserkey.pem pull")).

Not that R is the same as Ruby, but if R can do it... O:-)


The problem is when you have different remote repositories on the same host (say github.com), and you want to interact with them using different ssh keys (i.e. different GitHub accounts).

In order to do that:

  1. First you should declare your different keys in ~/.ssh/config file.

    # Key for usual repositories on github.com
    Host github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_rsa
    
    # Key for a particular repository on github.com
    Host XXX
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_other_rsa
    

    By doing this you associate the second key with a new friendly name "XXX" for github.com.

  2. Then you must change the remote origin of your particular repository, so that it uses the friendly name you've just defined.

    Go to your local repository folder within a command prompt, and display the current remote origin:

    >git remote -v
    origin  [email protected]:myuser/myrepo.git (fetch)
    origin  [email protected]:myuser/myrepo.git (push)
    

    Then change origin with:

    >git remote set-url origin git@XXX:myuser/myrepo.git
    >git remote -v
    origin  git@XXX:myuser/myrepo.git (fetch)
    origin  git@XXX:myuser/myrepo.git (push)
    

    Now you can push, fetch... with the right key automatically.


In Windows with Git Bash you can use the following to add a repository

ssh-agent bash -c 'ssh-add "key-address"; git remote add origin "rep-address"'

for example:

ssh-agent bash -c 'ssh-add /d/test/PrivateKey.ppk; git remote add origin [email protected]:test/test.git'

Which private key is in drive D, folder test of computer. Also if you want to clone a repository, you can change git remote add origin with git clone.

After enter this to Git Bash, it will ask you for passphrase!

Be Aware that openssh private key and putty private key are different!

If you have created your keys with puttygen, you must convert your private key to openssh!


Here's the ssh key hack i found while finding solution to this problem:

For example you have 2 different set of keys:

key1, key1.pub, key2, key2.pub

Keep these keys in your .ssh directory

Now in your .bashrc or .bash_profile alias file, add these commands

alias key1='cp ~/.ssh/key1 id_rsa && cp ~/.ssh/key1.pub id_rsa.pub'

alias key2='cp ~/.ssh/key2 id_rsa && cp ~/.ssh/key2.pub id_rsa.pub'

Voila! You have a shortcut to switch keys whenever you want!

Hope this works for you.


I just needed to add the key then run the git clone again.

ssh-add ~/.ssh/id_rsa_mynewkey
git clone [email protected]:mycompany/myrepo.git

To have GIT_SSH_COMMAND environment variable work under Windows instead of:

set GIT_SSH_COMMAND="ssh -i private_key_file"

Use:

set "GIT_SSH_COMMAND=ssh -i private_key_file"

The quote has to be like

set "variable=value" 

Some backgorund: https://stackoverflow.com/a/34402887/10671021


If you're like me, you can:

  • Keep your ssh keys organized

  • Keep your git clone commands simple

  • Handle any number of keys for any number of repositories.

  • Reduce your ssh key maintenance.

I keep my keys in my ~/.ssh/keys directory.

I prefer convention over configuration.

I think code is law; the simpler it is, the better.

STEP 1 - Create Alias

Add this alias to your shell: alias git-clone='GIT_SSH=ssh_wrapper git clone'

STEP 2 - Create Script

Add this ssh_wrapper script to your PATH:

#!/bin/bash
# Filename: ssh_wrapper

if [ -z ${SSH_KEY} ]; then
    SSH_KEY='github.com/l3x'  # <= Default key
fi
SSH_KEY="~/.ssh/keys/${SSH_KEY}/id_rsa"
ssh -i "${SSH_KEY}" "$@"

EXAMPLES

Use github.com/l3x key:

KEY=github.com/l3x git-clone https://github.com/l3x/learn-fp-go

The following example also uses the github.com/l3x key (by default):

git-clone https://github.com/l3x/learn-fp-go

Use bitbucket.org/lsheehan key:

KEY=bitbucket.org/lsheehan git-clone [email protected]:dave_andersen/exchange.git

NOTES

Change the default SSH_KEY in the ssh_wrapper script to what you use most of the time. That way, you don't need to use the KEY variable most of the time.

You may think, "Hey! That's a lot going on with an alias, a script and some directory of keys," but for me it's convention. Nearly all my workstations (and servers for that matter) are configured similarly.

My goal here is to simplify the commands that I execute regularly.

My conventions, e.g., Bash scripts, aliases, etc., create a consistent environment and helps me keep things simple.

KISS and names matter.

For more design tips check out Chapter 4 SOLID Design in Go from my book: https://www.amazon.com/Learning-Functional-Programming-Lex-Sheehan-ebook/dp/B0725B8MYW

Hope that helps. - Lex


Starting from Git 2.3.0 we also have the simple command (no config file needed):

GIT_SSH_COMMAND='ssh -i private_key_file -o IdentitiesOnly=yes' git clone user@host:repo.git

Note the -o IdentitiesOnly=yes is required to prevent the SSH default behavior of sending the identity file matching the default filename for each protocol as noted in the answer above.


You could use GIT_SSH environment variable. But you will need to wrap ssh and options into a shell script.

See git manual: man git in your command shell.


As stated here: https://superuser.com/a/912281/607049

You can configure it per-repo:

git config core.sshCommand "ssh -i ~/.ssh/id_rsa_example -F /dev/null"
git pull
git push

If SSH port number is not 22(default), add Port xx in ~/.ssh/config

In my case (synology),

Host my_synology
    Hostname xxxx.synology.me
    IdentityFile ~/.ssh/id_rsa_xxxx
    User myname
    Port xx

Then clone using Host title in config. ("my_synology". to avoid @chopstik 's "*")

git clone my_synology:path/to/repo.git

I went with the GIT_SSH environment variable. Here's my wrapper, similar to that from Joe Block from above, but handles any amount of arguments.

File ~/gitwrap.sh

#!/bin/bash
ssh -i ~/.ssh/gitkey_rsa "$@"

Then, in my .bashrc, add the following:

export GIT_SSH=~/gitwrap.sh

if you have directory on your path where you want to sign with a given identifyfile you can specify to use a specific identify file via the .ssh/config file by setting the ControlPath e.g.:

host github.com
  ControlPath ~/Projects/work/**
  HostName github.com
  IdentityFile ~/.ssh/id_work
  User git

Then ssh will use the specified identity file when doing git commands under the given work path.


I use zsh and different keys are loaded to my zsh shell's ssh-agent automatically for other purposes (i.e. access to remote servers) on my laptop. I modified @Nick's answer and I'm using it for one of my repos that needs to be refreshed often. (In this case it's my dotfiles which I want same and latest version across my all machines, wherever I'm working.)

bash -c 'eval `ssh-agent`; ssh-add /home/myname/.dotfiles/gitread; ssh-add -L; cd /home/myname/.dotfiles && git pull; kill $SSH_AGENT_PID'
  • Spawn an ssh-agent
  • Add read-only key to agent
  • Change directory to my git repo
  • If cd to repo dir is successful, pull from remote repo
  • Kill spawned ssh-agent. (I wouldn't want many of agents lingering around.)

Examples related to git

Does the target directory for a git clone have to match the repo name? Git fatal: protocol 'https' is not supported Git is not working after macOS Update (xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools) git clone: Authentication failed for <URL> destination path already exists and is not an empty directory SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443 GitLab remote: HTTP Basic: Access denied and fatal Authentication How can I switch to another branch in git? VS 2017 Git Local Commit DB.lock error on every commit How to remove an unpushed outgoing commit in Visual Studio?

Examples related to bash

Comparing a variable with a string python not working when redirecting from bash script Zipping a file in bash fails How do I prevent Conda from activating the base environment by default? Get first line of a shell command's output Fixing a systemd service 203/EXEC failure (no such file or directory) /bin/sh: apt-get: not found VSCode Change Default Terminal Run bash command on jenkins pipeline How to check if the docker engine and a docker container are running? How to switch Python versions in Terminal?

Examples related to shell

Comparing a variable with a string python not working when redirecting from bash script Get first line of a shell command's output How to run shell script file using nodejs? Run bash command on jenkins pipeline Way to create multiline comments in Bash? How to do multiline shell script in Ansible How to check if a file exists in a shell script How to check if an environment variable exists and get its value? Curl to return http status code along with the response docker entrypoint running bash script gets "permission denied"

Examples related to ssh

Starting ssh-agent on Windows 10 fails: "unable to start ssh-agent service, error :1058" How to solve "sign_and_send_pubkey: signing failed: agent refused operation"? key_load_public: invalid format ssh connection refused on Raspberry Pi Getting permission denied (public key) on gitlab Verify host key with pysftp Can't connect to Postgresql on port 5432 Checkout Jenkins Pipeline Git SCM with credentials? How to open remote files in sublime text 3 how to setup ssh keys for jenkins to publish via ssh