161

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:

2
  • 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. Commented Apr 4, 2016 at 15:22
  • Great answer over here: stackoverflow.com/questions/11656134/… Commented Oct 26, 2016 at 17:43

6 Answers 6

47

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.

Sign up to request clarification or add additional context in comments.

4 Comments

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.
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 (stackoverflow.com/questions/40515569/…): "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."
Yup, this should be the correct answer.
"Connecting github accounts to key pairs effecticely establishes a 1:N mapping between github account based identities and key pair identities." should be "Connecting a github account to key pairs effecticely establishes a 1:N mapping between github account based identities and key pair identities."
25

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.

14 Comments

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.
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?
@DavidEbbo Following 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.
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.
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).
|
11

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 [email protected]$REPO_NAME:$REPO_OWNER_NAME/$REPO_NAME.git"
fi

1 Comment

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.
4

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.

6 Comments

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: 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.
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.
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.
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.
|
2

It is clear that (GitHub) repository deployment key is the wrong way to go by. I explain the steps that work.

Machine account

For this to work

  • Create a real email address for a machine account (the web server). For reset purposes and invites you will have to have access to the mail box!

  • Create a git (Github or otherwise) account per (web) server with this email address. This will be the machine account for this server

  • Create a public key (to be used on the webserver) with ssh-keygen and a accompanied private key for the git (Github) machine account

    ssh-keygen -t ed25519 -C "server-url" -f server-url

make sure not to enter a passphrase (twice). It will create the public key in a server-url.pub file and the private in a file with a similar name without the extension.

  • Copy the public key in the .ssh folder on the (web) server. Plesk also needs the private key for whatever reason.
  • Attach the private key to the git account (in Github under Settings / SSH and GPG keys

In your private repositories you

  • invite the machine account as collaborator with read rights
  • The machine account should accept the invitation so check its email or git account to accept the invitation!!!

The (web) server now has (read) access to your private repository and can for instance do automatic cloning with a webhook.

reference: Github and: GitHub (explaining Github allows machine accounts for ci/ci workflows only)

Comments

1

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?

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.