46

I have a host, call it rob. I used ssh-keygen on rob to get a public key, which I gave to github in the add a new deploy key screen for repository cheech. Now I want to deploy chong on rob as well. But if I go to the add new deploy key screen for repository chong on github, and paste in the public key I generated on rob it says key already in use. I thought, if the key was in use, I could clone chong on rob but that says permission denied.

So clearly this is more complicated than I thought and it involves having multiple keys or something. What should I do to clone chong on rob?

Spikatrix
  • 20,225
  • 7
  • 37
  • 83
user1552512
  • 869
  • 1
  • 9
  • 14

10 Answers10

74

Once a key has been attached to one repo as a deploy key, it cannot be used on another repo. If you're running into this error while setting up deploy keys, then you'll need to modify your remote and set up your ~/.ssh/config file to use a non-existent github.com hostname that ssh will be able to use to pick the correct ssh deploy key for your repository.

# first we remove the origin
$ git remote -v
origin  git@github.com:username/foo.git (fetch)
origin  git@github.com:username/foo.git (push)
$ git remote rm origin

# here we add a new origin using a host nickname called
# foo.github.com that we will reference with a Host stanza in our
# ~/.ssh/config to specify which key to use with which fake hostname.
$ git remote add origin git@fake-hostname-foo.github.com:username/foo.git
$ git remote -v
origin  git@fake-hostname-foo.github.com:username/foo.git (fetch)
origin  git@fake-hostname-foo.github.com:username/foo.git (push)

Generate the deploy key for your repository and name it something reasonable like:

$ ssh-keygen -t rsa -f ~/.ssh/id_rsa-foo -C https://github.com/username/foo
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/username/.ssh/id_rsa-foo.
Your public key has been saved in /home/username/.ssh/id_rsa-foo.pub.
The key fingerprint is:
c0:ff:ee:34:24:11:5e:6d:7c:4c:b1:a0:de:ad:be:ef https://github.com/username/foo
The key's randomart image is:
+--[ RSA 2048]----+
|  E   o..o.oo.   |
| M o o o .+CoW   |
|  + o = o. ..    |
| .   . +         |
|        S        |
|       o .       |
|        +        |
|       . o       |
|        ..o.     |
+-----------------+

Once you've added the deploy key you will then need to add the following stanza to your ~/.ssh/config file:

Host fake-hostname-foo.github.com
    Hostname github.com
    IdentityFile ~/.ssh/id_rsa-foo

Now you can test it with:

$ ssh -T git@fake-hostname-foo.github.com
Hi username! You've successfully authenticated, but GitHub
does not provide shell access.
aculich
  • 14,545
  • 9
  • 64
  • 71
  • 1
    I do not understand what is fake here? You generate another key. Is every second key called "a fake"? – Little Alien Jul 12 '16 at 20:45
  • @LittleAlien the `foo` hostname in `foo.github.com` is the fake (aka non-existent) part. You can make up anything you want for the hostname. I've clarified the entry with `fake-hostname-foo.github.com` instead of just `foo.github.com`. – aculich Jul 14 '16 at 01:02
  • 10
    What's wrong with using the same key for several repos? Why do we have to do come up with hacks like this? – kontextify Sep 09 '16 at 10:56
  • even i have the same question as @kontextify ? Can you pls expain. – indianwebdevil Jul 20 '17 at 14:48
  • 2
    @kontextify Github limits deploy keys per repo. You can not share them either. As a secondary note make sure you don't keep a id_rsa or github will continue to default to it. – Shadoath Oct 17 '17 at 15:57
  • 1
    @Shadoath what you said was correct. I needed to rename the id_rsa file for this to work out correctly <3 – Abdalla Arbab May 22 '20 at 11:21
  • 1
    Well I'll be darned. You're are one clever person. – Grant Jul 18 '22 at 21:14
13

The simplest solution I found was outlined here.

1) Enter this command(You'll do this for however many keys you need):

ssh-keygen -t rsa -C "your_email@example.com"

2) When prompted with the the statement below type in a unique name(i.e.,foo1_rsa).The file will be created in your current directory and you may need to move it to .ssh if you want to be tidy:

Enter file in which to save the key (/Users/you/.ssh/id_rsa): [Press enter]

3) Update your SSH config file:

vi ~/.ssh/config

Which may be empty:

Host cheech github.com
Hostname github.com
IdentityFile ~/.ssh/foo1_rsa

Host chong github.com
Hostname github.com
IdentityFile ~/.ssh/foo2_rsa
Naruto Sempai
  • 6,233
  • 8
  • 35
  • 51
  • 2
    This doesn't work. Git will use the first matched hostname (ie. `github.com`), therefore only work properly for the first listed Host in the config file. – knutole Nov 28 '18 at 14:20
  • A fix for what @knutole said, is to have them in subdomains like so: `Host cheech.github.com` and `Host chong.github.com` – Abdalla Arbab May 22 '20 at 11:03
3

I know this is 5+ years old but there's no accepted answer to this popular question so here's what I consider the best way considering cleanliness and future readability:


ADD A DEPLOY USER TO YOUR TEAM

Step 1: Create a new email address in your organisation's domain for a new deploy user. Something like deploy@organisation.example.com.

Step 2: Use that mailbox to create a new GitHub account (GitHub calls these "machine users") give it a username like deploy-ExampleOrganisation so it's role is clear.

Step 3: Create a user on your server called "deploy" with a command like this:

useradd -d /home/deploy -m deploy

Generate an SSH key for deploy@servername, specifying no passphrase and /home/deploy/.ssh/id_rsa as the file location:

ssh-keygen -t rsa -b 4096 -C "deploy@servername"

Add the contents of /home/deploy/.ssh/id_rsa.pub as a SSH key on your new deploy-ExampleOrganisation GitHub account: Go to Settings > SSH and GPG keys > New SSH Key.

Step 4: Create a team in your organisation called something like "Read-only deploy users", add your new user to the team and give the team Read access to any repos that will be deployed. (If you don't have an organisation account you can still give this user access to multiple private repos)

Step 5: Add your own personal machine's SSH key to deploy user's authorized keys file (/home/deploy/.ssh/authorized_keys) so that you (or your deploy script) can login as deploy when deploying code.

Boom! That's it... You now have a clean and self-documenting flow.


P.S. I tried aculich's highly up-voted answer but it felt dirty messing around with fake host names and I thought, if I come back to this in a years time am I going to easily figure out what I did to create all the keys and understand how that SSH config file makes those funny non-existent remote addresses work? Probably not!

Advantages of a deploy user over fake host names method:

  • No hacks! It's standard user accounts with clear names, accessing repos through real host names.
  • Less keys floating around.
  • If/when you do move to additional servers, it's easy to give your Deploy user an account on all of them and just by adding 1 new key to her GitHub account, her account on the new server is ready to deploy code.
  • Deploy user only has low-privilege Read-only access to only the repos listed in the team and your personal SSH keys are kept off the server so if some nasty person does gain access to your server they can't wreak havock on all your repos as well.
  • Deploy tool config files (eg Capistrano) do not get dirtied up containing those confusing fake host names. (It was when they started spreading beyond the server that I really became uncomfortable with that method.)
  • If you forget how the hell you did this in a years time the file ownership will lead you to the deploy user ls -la, the SSH key will lead you to GitHub account name ssh -T git@github.com and hopefully then you're fully up to speed again.
  • And finally... it's the method recommended by GitHub.
Martin Joiner
  • 3,529
  • 2
  • 23
  • 49
3

I've not seen any good or easy answer here so I thought I'd throw in my solution. I don't like answers where you need to change the domain name, use wrapper scripts, screw around with keyring or mess around with .ssh config files. SSH is just the transport for git, so I don't want to fill up my important ssh folders and configs with hacks and settings to use multiple deploy keys which once set I don't care about and will only have one per repo. This should be a git config - and it is.

In addition why not keep the credentials in the repo's .git folder so if you delete the repos folder you delete the deploy key?

Assume /tmp/deploy_key is the initial (temporary) location of your private deploy key.

# Initial clone of repo    
GIT_SSH_COMMAND="ssh -i /tmp/deploy_key" git clone git@github.com:folder/reponame.git

# cd to the folder to assure we only change config for the repo instead of user/global.
cd reponame
# Could probably just mv instead of cp/chmod/rm the key but lets be precise
cp /tmp/deploy_key ./.git/deploy_key
chmod 600 ./.git/deploy_key
rm /tmp/deploy_key

# This is the magic - note the backslash escaping the $ as PWD is always the repo's
# folder when pulling or pushing etc... and at git (run) command time $PWD is the
# repo's root folder.
git config core.sshCommand "ssh -i \$PWD/.git/deploy_key"

Also you can of course store key somewhere else and use a absolute path without the $PWD for the deploy key:-

# Initial clone of repo    
GIT_SSH_COMMAND="ssh -i /path/to/a/deploy_key" git clone git@github.com:folder/reponame.git
# cd to repo to configure only for this repo
cd reponame
git config core.sshCommand "ssh -i /path/to/a/deploy_key"

But I prefer to keep it in the repo's internal .git folders so if I delete the repo's folder I know there's no credentials hanging around, and after all you can only have one repo per deploy key.

Jimmy
  • 553
  • 2
  • 9
3

As others pointed it out, deploy keys are unique. The best approach so far is ssh .config aliasing, but I found a cleaner way:

This can be done by customizing the GIT_SSH_COMMAND. As ssh .config only gets the host, you have to create aliases to handle different paths. Alternatively, as the git CLI sends the path of the repo to the GIT_SSH_COMMAND, you can intercept the request in a custom script, added in between git and ssh.

You can create a solution where you extract the path and add in the related identity file, if available on the server.

One approach to do this can be found here.

Usage:

cp deploy_key_file ~/.ssh/git-keys/github-practice
GIT_SSH_COMMAND=custom_keys_git_ssh git clone git@github.com:github/practice.git

You can define GIT_SSH_COMMAND=custom_keys_git_ssh globally and you only have to add the key files to ~/.ssh/git-keys/ folder.

Vajk Hermecz
  • 5,413
  • 2
  • 34
  • 25
2

A deploy key for github is unique ... You have to generate a new key for the other repository. Just run ssh-keygen again

See the github documentation for this: https://help.github.com/articles/managing-deploy-keys

klaustopher
  • 6,702
  • 1
  • 22
  • 25
2

If you're comfortable giving rob access to all of the private repositories in your GitHub account, you could remove the key as a deploy key from cheech and then add it as an SSH key to your GitHub account as a whole. This would give rob access to both cheech and chong.

This won't work if you have other repositories in your account that you do not wish rob to access.

If you need finer-grained control, you will need to generate additional keys on rob and assign them as deploy keys to specific repositories.

Nathan
  • 7,816
  • 8
  • 33
  • 44
2

Managing multiple GitHub deploy keys may be made easy with my tiny npm module github-add-key. It uses the approach described in this great answer, and all you'll need is

$ github-add-key rob/chong

in your locally cloned rob/chong and follow the simple automated process.

Community
  • 1
  • 1
2

While Pimkin's idea was great, I didn't want to install node just for this, so I created something similar in bash:

https://gist.github.com/blvz/8eeebacae11011c25fc79eff12f49ae9

Install and use:

curl https://gist.githubusercontent.com/blvz/8eeebacae11011c25fc79eff12f49ae9/raw/6f2f7f3709a0fe852d8a3a5bb125325e3ffbc7d8/gh-deploy-clone.sh > /usr/local/bin/gh-deploy-clone
chmod +x /usr/local/bin/gh-deploy-clone

gh-deploy-clone user/repo

# You can also give it a name, in case
# you have multiple deploy targets:

gh-deploy-clone user/repo staging
blvz
  • 1,335
  • 13
  • 15
0

You can also create an ssh wrapper and pass it as GIT_SSH. This option has the advantage that you don't have to change the git remote. https://stackoverflow.com/a/14221028/3461

Community
  • 1
  • 1
Benjamin Atkin
  • 14,071
  • 7
  • 61
  • 60