Using the same deploy key for multiple github projects
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:
- One showing a workaround
- One discussing the issue but not really going anywhere
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.
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".
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.
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.
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