I have a private git server with one user git
and ssh key authentication. Currently, I am the only one using it, but I want to add more people and I want everyone to use user git
to connect to the server and perform git clone
, git push
, etc. If all the repositories are "public", then I know how to solve this. But for example I want to have a private repository, that I will still clone via git clone git@server:repo
with my SSH key, however I do not want other users to be able to clone it using their SSH keys.
I checked git-scm documentation on setting up the server (which was helpful for public repositories), and this post, however this post seems to only solve the problem for only private repositories.
TLDR: When you clone a repository on GitHub, you say git clone git@github.com:user/repo
and your git username and ssh key are being sent over. Now, depending on whether you have the permissions to this repository, you can clone it, otherwise not. So basically, everyone is using git
user on the surface, but under the hood some authorisation is taking place. How does GitHub handle this for example?
gitolite
seems to be exactly what you are looking for managing access to repositories/branches based on the ssh key.
But if you want to build something like this from scratch you need to look into the options in the authorized_keys
file, especially command
and environment
. With these you can force a specific command/script or add/overwrite environment variables based on which ssh key was used.
For example you could write a script that reads the allowed repositories for the user as its arguments and force it to be run for selected ssh keys:
# file ~/.ssh/authorized_keys
command="/home/git/bin/git-only-shell /home/git/repos/repo1" ssh-rsa AAAAB2...
command="/home/git/bin/git-only-shell /home/git/repos/repo1 /home/git/repos/repo2" ssh-rsa AAAAB3...
The script can now read the requested repo from $SSH_ORIGINAL_COMMAND
and check that it is contained in the passed list. A complete - but crude - example implementation of this script git-only-shell
could be like this:
#!/bin/bash
# verify that $SSH_ORIGINAL_COMMAND starts with git-upload-pack, git-upload-archive or
# git-receive-pack, followed by a space
if ! [[ "$SSH_ORIGINAL_COMMAND" == git-upload-pack\ * || "$SSH_ORIGINAL_COMMAND" == git-upload-archive\ * || "$SSH_ORIGINAL_COMMAND" == git-receive-pack\ * ]]; then
echo "unsupported command" >&2
exit 1
fi
# remove first word (git command)
ARGUMENTS="${SSH_ORIGINAL_COMMAND#git-* }"
# use eval to un-quote repo path (it is passed in single-quotes)
REPO_PATH="$(eval "echo $ARGUMENTS")"
# allowed repos are passed as arguments to this script
ALLOWED_REPOS=("$@")
# check if repo was whitelisted
IS_ALLOWED=false
for repo in "${ALLOWED_REPOS[@]}"; do
if [[ "$REPO_PATH" == "$repo" ]]; then
IS_ALLOWED=true
fi
done
if [[ $IS_ALLOWED == "false" ]]; then
echo "access to this repo not allowed" >&2
exit 1
fi
# execute the original command
eval "$SSH_ORIGINAL_COMMAND"