146

Github does not allow the same ssh deploy key to be used for more than one project, which would be very useful in some cases (e.g. CI server dealing with project with private sub-modules). I've seen various threads that seem to say that this limitation is there for 'security reasons', but I'm yet to see a convincing explanation about exactly what risk that would raise.

Note that the fact that Github doesn't allow Account Level keys to be reused makes sense (two users shouldn't share keys). It is only the restriction on Deploy Keys that I'm questioning.

And to be clear, I'm not looking for workarounds (create a dummy user, use multiple keys, ...), but only for a plausible explanation for this limitation on Deploy Keys.

Related threads:

David Ebbo
  • 42,443
  • 8
  • 103
  • 117
  • 3
    Since there is no better way we have create a dedicated deployment user whom we are granting the read-only access to repositories. The end result is the same. – Datageek Apr 04 '16 at 15:22
  • Great answer over here: http://stackoverflow.com/questions/11656134/github-deploy-keys-how-do-i-authorize-more-than-one-repository-for-a-single-mac – apple16 Oct 26 '16 at 17:43

6 Answers6

32

Unfortunately, this is a scenario where github just misinterprets the distinction between a key pair and an account or project.

Since a key pair is used for authentication and authorization, it is effectively an identity. Github accounts are another identity. Connecting github accounts to key pairs effecticely establishes a 1:N mapping between github account based identities and key pair identities.

Conversely, github enforces a 1:N mapping of projects to key pair based identities. The real world analogue is that there is a door granting access to the project that can be unlocked by many different people. But once any of them gets a key to the door, they cannot get any other keys for any other doors, ever again.

It makes sense not to re-use keys often from the perspective of containing breaches if a key gets compromised. But that's just a good administration policy. It doesn't make much sense to prevent a key from being used more than once on principle. That there are some keys for some doors that are never re-used, well, again that's down to policy.


A slightly more complex view is to illustrate key pairs as roles. You can possess many key pairs, and therefore inhabit many roles. The private key authenticates you for the role.

Github's deploy key mapping to projects states that a role can never encompass more than one task. That's rarely realistic.

None of which changes what github allows, of course.

  • 4
    Heh. It's kind of funny how this gets downvoted, when it's more correct than the accepted answer. There is literally **nothing** in this that prevents sharing a key with multiple users. – Jens Finkhaeuser May 24 '18 at 15:06
  • 2
    Yep, it's flawed reasoning turned into an unnecessary limitation. A warning would have at most been the only measure needed. As another commenter in this question stated (https://stackoverflow.com/questions/40515569/why-cant-i-use-one-ssh-key-on-more-than-one-github-repo): "Taking the least path of resistance would involve just copying the key to multiple machines, resulting in a greater security risk. GitHub should rather let the user choose and assume the risk on a per repository basis." – Jon May 14 '21 at 15:02
23

The only reason, illustrated by the workaround you reference (creating a single "build" user, or sharing the same id_rsa.REPONAME.pub per repo) is:

avoid sharing public/private key for different user

Even though that wouldn't be the case in your situation (build multiple project), allowing to reuse the same ssh key would open the possibility for two different users to share the same ssh key, which would defeat the authentication purpose.

Authentication means:
"using a certain ssh key should imply that you are supposed to know who is using it".

However, as noted in jtlindsey's answer, this is less related to authentication/identity/good policy, and more related to a "role" you attach to those keys. (role to deploy a certain repository).

As noted in "Why can't I use one ssh key on more than one github repo?" by tkeeler:

This workflow becomes a problem when using automated systems and git submodules.
You can't use the same deploy key in more than one repo, so the workaround becomes adding that key to their user account (or a dedicated machine account).

Taking the least path of resistance, most users will add it to their own account resulting in a greater security risk.

GitHub should rather let the user choose and assume the risk on a per repository basi


The GitHub page "Managing deploy keys" details the various accounts using ssh:

  • SSH agent forwarding: Agent forwarding uses the SSH keys already set up on your local development machine when you SSH in to your server and run git commands.
    You can selectively let remote servers access your local ssh-agent as if it was running on the server.
    So no need to replicate your private key on the server.

  • Machine users: (this is the "dummy account" strategy) Attach the key to a user account. Since this account won't be used by a human, it's called a machine user.
    You would treat this user the same way you would a human though, attach the key to the machine user account as if it were a normal account.
    Grant the account collaborator or team access to the repos it needs access to.
    So one private key associated to one "machine user", one per server.

(DHa points out in the comments to a deploy key limit number, and the fact you can have only one machine user account)

  • Deploy key (one per GitHub repo) SSH key that is stored on the server and grants access to a single repo on GitHub.
    This key is attached directly to the repo instead of to a user account.
    Instead of going to your account settings, go to the target repo's admin page.
    Go to "Deploy Keys" and click "Add deploy key". Paste the public key in and submit.

This time, the ssh key isn't attached to a user (for which you could grant access to several repo), but to one repo.
Granting the ssh access for several repo would be the equivalent of a "machine user".

In term of authentication:

  • using the same key for several repos is okay when it is done by a user (which has said key associated to his/her account)
  • using the same key for several repo is NOT okay when the key is attached by a repo, because you don't know at all who accessed what.
    That differs from the "machine user" where a "user" is declared as a collaborator for many repo.
    Here (Deploy key), there is no "collaborator", just a direct ssh access granted to the repo.

Yusuf Bhabhrawala further illustrates this model limitation in the comments:

Consider these very plausible use cases - a python project using a private pip module or a node project using a private npm package - both from another repo in same organization.

Currently there is no way to deploy this very simple use case either using a deploy key or an account key (which exposes too many other repos).

And Chris Stenkamp points out to "How to discover where 'pip install git+ssh://...' is searching for ssh keys?", which involves setting the GIT_SSH_COMMAND environment variable.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 82
    GitHub supports both *Account Level* public keys, and *Project Level* keys (aka Deploy Keys). Not allowing reuse of *Account Level* keys makes sense, but I claim that not allowing it for *Deploy Keys* doesn't. My one Account Level key allows access to all my projects, so why couldn't I have a Deploy Key that allows access to *some* of my projects? It's only more restrictive and does not create any concern that I can see. Your concern about opening the *possibility for two different users to share the same ssh key* does not come in the picture in that scenario. – David Ebbo Nov 05 '12 at 15:21
  • @DavidEbbo It might not come in the picture, but that concern (two different users to share the same ssh key) is at the core of the reason why an ssh key is not shared. – VonC Nov 05 '12 at 15:26
  • 28
    I'm afraid I don't follow your reasoning here. I'm asking about a very specific scenario (use a Deploy Key in multiple projects), and your argument for it not being possible is bring up an unrelated scenario (two users sharing ssh keys). Sticking exclusively with the Deploy Key scenario, what would be the negative of github allowing it? – David Ebbo Nov 05 '12 at 17:16
  • 7
    @DavidEbbo Following https://help.github.com/articles/managing-deploy-keys, none of the three methods (Account, Deploy or Machine accounts) involves sharing a private SSH key for accessing said repos. Sticking exclusively with the Deploy Key scenario, since it is a key *on the server*, for it to be valid on several repos would mean share (or replicate) a private key on multiple repos. That diminishes the authentication aspect, and if the key is compromised, augments the number of repos exposed. – VonC Nov 05 '12 at 19:27
  • 11
    thanks, that page has interesting info. I'll mark your reply as the answer in a day or two if I see nothing else, though to be honest I'm still not convinced by the argument. Having a deploy key used on two repos is no weaker than using a machine key that has access to the same set of repos. – David Ebbo Nov 05 '12 at 21:28
  • All I really see here is now, i cannot `npm install` a private repo that depends on another private repo. This really sucks. – Danosaure Mar 13 '17 at 16:24
  • This answer seems counter-intuitive, the deploy key limit encourages sharing of keys. If you have 10 servers needing access to 3 repos, you get the choice of sharing 3 keys across 10 servers or managing 30 keys. If the deploy key limit was not in place 10 keys would be sufficient which is more manageable. Machine users is not an alternative since you are only allowed one, thus forcing sharing of ssh keys across all 10 servers. – DHa Aug 13 '20 at 06:28
  • @DHa What about sharing key from a service account (the "machine user" I mention above in this 2012 answer)? – VonC Aug 13 '20 at 06:34
  • @VonC I noticed a machine user is allowed multiple keys, so you can set it up with 10 keys of access. But github only allows the creation of one machine user, so if you have another set of servers needing access to another set of repos, the arbitrary 'max 1'-limits of github screws you up again. Our solution for security/simplicity is to create multiple machine users, but technically it is not allowed. – DHa Aug 13 '20 at 06:54
  • @DHa I understand, and I have included your comment in the answer to make that limitation more visible. – VonC Aug 13 '20 at 07:10
  • 2
    Consider these very plausible use cases - a python project using a private pip module or a node project using a private npm package - both from another repo in same organization. Currently there is no way to deploy this very simple use case either using a deploy key or an account key (which exposes too many other repos). – Yusuf Bhabhrawala Oct 06 '21 at 23:13
  • @YusufBhabhrawala Good point, thank you. I have included your comment in the answer for more visibility. – VonC Oct 07 '21 at 05:38
  • @YusufBhabhrawala see https://stackoverflow.com/a/51979783/5122790 for a workaround, as annoying as it is that you need to use workarounds like this – Chris Stenkamp Nov 29 '21 at 16:15
  • 1
    @ChrisStenkamp Interesting. I have included your reference in the answer for more visibility. – VonC Nov 29 '21 at 16:18
6

I realize you are not looking for a workaround but I imagine a lot of users land on this page and would also like a simple solution. The work around you posted a link to is similar to what is in github docs. The process is error prone if done manually.

FYI, Bitbucket.org does not have this limitation. You can use the same read-only access key on multiple repos. This makes it super easy to use a single production server with multiple repos

If you're sticking with Github, here is a script i wrote that allows you to simply pass your repo owner name and the repository name by running ./generateDeployKey.sh repo_owner_name repo_name and it does everything for you and outputs anything you might need to copy.

Save the following to a new file named generateDeployKey.sh

#!/bin/sh
# This script generates a ssh key for a single repository
# and adds a custom configuration to the users (not global) ssh config file,
# and outputs the public key for you to copy and paste as the repo deploy key
# and outputs the url for you to clone the repo on the machine.
# Github docs ref:
# https://docs.github.com/en/developers/overview/managing-deploy-keys#using-multiple-repositories-on-one-server
#
# 1. Add the script to the user account of the machine. The home directory is fine.
# 2. Make the script executable by running the following command as the user:
# chmod u+x generateDeployKey.sh
# 3. Run script like `./generateDeployKey.sh REPO_OWNER_NAME REPO_NAME` Note the space between owner and repo name. Example:
# ./generateDeployKey.sh yourname hello_world
# If you make a mistake with what you pass in, you can remove change from your ~/.ssh/config file
# by deleting the most recent "New Key Generated on...." and deleting the related .pub and private keys


# Check if user passed in both parameters
if [ -z "$1" ] || [ -z "$2" ]
then
  echo "Make sure to pass in both parameters REPO_OWNER_NAME and REPO_NAME. Example:"
  echo "./generateDeployKey.sh yourname hello_world"
else
  REPO_OWNER_NAME=$1
  REPO_NAME=$2
  KEY_PATH=~/.ssh/id_rsa.$REPO_NAME
  echo "Generating ssh key At ${KEY_PATH}"
  ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa.${REPO_NAME}
  echo "Your ssh deploy key is:"
  PUB_KEY_PATH=$KEY_PATH".pub"
  cat $PUB_KEY_PATH
  echo ""
  # Will create config if it does not exist
  echo "Updating ~/.ssh/config"
  DATE_TIME=$(date +"%Y-%m-%d at %r")
  echo "
  # New Key Generated on $DATE_TIME
  Host github.com-$REPO_NAME
    HostName github.com
    User git
    IdentityFile $KEY_PATH" >> ~/.ssh/config
  echo ""
  echo "Here is your hostname's alias to interact with the repository using SSH:"
  echo "git clone git@github.com-$REPO_NAME:$REPO_OWNER_NAME/$REPO_NAME.git"
fi
jtlindsey
  • 4,346
  • 4
  • 45
  • 73
  • Thanks for this handy little script. While I understand it directly does NOT answer the question (which is arguably subjective and argumentative) but instead produces a extremely useful solution to the underlying problem. – TheSoftwareJedi Feb 10 '23 at 19:15
3

It took me a lot of thinking to rationalize the implications and came up with this scenario.

Imagine that you create a single deploy key for a user which you've assigned to multiple repositories. Now you want to revoke that key but it's used in multiple places. So instead of being able to revoke all access you may inadvertently only revoke partial access.

This may sound like a benefit but this many-to-one relationship is actually inherently insecure once you consider the human factor. This is because you can't know for sure if you've really revoked all access without inspecting every repository and compare each public key individually in the case that you've forgotten to where you've actually assigned it.

It's definitely frustrating to assign and manage so many unique keys but the security implications are clear with how GitHub has instituted their policy: when you revoke a key you're guaranteed to be revoking all access granted by that key because it's only used in one place.

Zhro
  • 2,546
  • 2
  • 29
  • 39
  • 1
    I'm not convinced by this explanation. How is that fundamentally different from allowing one user to access multiple repositories, which is obviously allowed? If you no longer trusted that user, you'd need to remove them from every repo. – David Ebbo Nov 09 '17 at 20:58
  • @David: `How is that fundamentally different from allowing one user to access multiple repositories, which is obviously allowed` Can you explain this further? I only have a Developer account and I see that you can add ssh keys for account-wide access (one key for all repositories) or add individual deploy keys (one key for each repository). This is still a one-to-many or one-to-one relationship where revoking the "one" key revokes "all" access in both cases. – Zhro Nov 10 '17 at 04:27
  • To further clarify, there is no opportunity (what what I can tell) to accidentally assign a key in a many-to-one relationship where access may exist elsewhere after being revoked. This appears to be GitHub's motivation for this restriction but I'm only guessing. – Zhro Nov 10 '17 at 04:34
  • The way I look at things, deploy keys are a little bit like 'anonymous users' that don't have a full account, but still represent some sort of identity. The difference is that in the account case, you give access to the account, which *indirectly* gives access to all the ssh keys in that account. While in the Deploy Key case, you skip the account abstraction and directly give access to the ssh key. But beyond that, I don't see the security needs being different. If the Account OR the owner of the deploy key becomes evil, you need to remove them from each repo. – David Ebbo Nov 10 '17 at 18:30
  • I think your "many-to-one" reasoning here is the best explanation of *why* they chose to do it like that. As you say, you can revoke one user (and thus their keys) in one step, but you can't revoke one deploy key from many repos in one step. That being said, I acknowledge the reasoning but that doesn't mean I have to like the result, haha... The obvious follow-up question to me is, "why is there no way to revoke one key from multiple repos?". It doesn't seem particularly complex to build that functionality - not trivial, but not super hard. Hopefully someday we'll get better key management. – rococo Apr 11 '21 at 03:41
  • @DavidEbbo the point is it's not any different from a security perspective, so there's not a valid reason in enforcing the restriction. On the other hand, there _are_ automation benefits in not allowing the restriction. – Jon May 14 '21 at 15:10
0

The "official" solution is https://docs.github.com/en/developers/overview/managing-deploy-keys#using-multiple-repositories-on-one-server.
Which is basically the workaround of the question, creating a key with a fake domain for each repository

Do they do this to force pay for one more user with read-only access for deployment?

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 31 '22 at 06:57
0

It is clear that (GitHub) repository deploymnet key is the wrong way to go by.

For this to work

  • You will need a git (Github or othewise) user per (web) server
  • You need an email address for this user (for reset purposes)
  • You create a public key (to be used on the webserver) with ssh-keygen
  • and a accompanied private key for the git (Github) user
  • the public key goes in your .ssh folder on the (web) server's .ssh
  • the private key goes in the git (Github) account for logon

in the repositories you

  • invite the user as collaborator with read rights to your private repository

on the server you now have (read) access to the repository and can do automatic cloning with a webhook.

theking2
  • 2,174
  • 1
  • 27
  • 36