[英]How to restrict git SSH access based on ssh key given
I have a private git server with one user git
and ssh key authentication.我有一个私人 git 服务器,一个用户git
和 ssh 密钥认证。 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.目前,我是唯一一个使用它的人,但我想添加更多人,我希望每个人都使用用户git
连接到服务器并执行git clone
, git push
等。如果所有存储库都是“公共”,那么我知道如何解决这个问题。 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.但是例如我想要一个私有存储库,我仍然会使用我的 SSH 密钥通过git clone git@server:repo
克隆它,但是我不希望其他用户能够使用他们的 SSH 密钥克隆它。
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.我检查了关于设置服务器的 git-scm 文档(这对公共存储库很有帮助)和这篇文章,但是这篇文章似乎只解决了私有存储库的问题。
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. TLDR:当你在 GitHub 上克隆一个存储库时,你说git clone git@github.com:user/repo
和你的 git 用户名和 ssh 密钥被发送过来。 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.所以基本上,表面上每个人都在使用git
用户,但在幕后进行了一些授权。 How does GitHub handle this for example?例如,GitHub 是如何处理的?
To implement a similar feature, you could rely on a dedicated tool such as gitolite .要实现类似的功能,您可以依赖专用工具,例如gitolite 。
Otherwise, you could install a full-blown GitLab server which would also provide fine-grained access control to the repositories, with "Git URLs" such as git@your-gitlab-domain.com:user/repo.git
否则,您可以安装一个成熟的GitLab 服务器,该服务器还可以提供对存储库的细粒度访问控制,使用“Git URL”,例如git@your-gitlab-domain.com:user/repo.git
(BTW it seems GitLab formerly relied on gitolite
, before version 5.0.0 ) (顺便说一句,似乎 GitLab 以前依赖gitolite
, 版本 5.0.0 之前)
gitolite
seems to be exactly what you are looking for managing access to repositories/branches based on the ssh key. gitolite
似乎正是您正在寻找的基于 ssh 密钥管理对存储库/分支的访问。
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
.但是如果你想从头开始构建这样的东西,你需要查看authorized_keys
文件中的选项,尤其是command
和environment
。 With these you can force a specific command/script or add/overwrite environment variables based on which ssh key was used.使用这些,您可以强制执行特定的命令/脚本或添加/覆盖环境变量,基于使用的 ssh 密钥。
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:例如,您可以编写一个脚本,将用户允许的存储库读取为其 arguments 并强制它为选定的 ssh 密钥运行:
# 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.该脚本现在可以从$SSH_ORIGINAL_COMMAND
读取请求的 repo 并检查它是否包含在传递的列表中。 A complete - but crude - example implementation of this script git-only-shell
could be like this:这个脚本git-only-shell
的完整但粗略的示例实现可能是这样的:
#!/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"
Tools like gitolite
were too complicated and unpleasant to use or did not offer the desired features. gitolite
之类的工具过于复杂且使用起来不愉快,或者无法提供所需的功能。 I ended up with the following solution:我最终得到了以下解决方案:
user
and repo
, and many-to-many table permissions
.具有表user
和repo
以及多对多表permissions
的 PostgreSQL 数据库。$SSH_ORIGINAL_COMMAND
.用 C 编写的程序,用于检查用户是否具有访问从$SSH_ORIGINAL_COMMAND
中提取的请求存储库的读/写权限。.ssh/authorized_keys
each key has its own command
which calls a C program with username associated with that key and a repository extracted from SSH command when that key is used for git
operations.在.ssh/authorized_keys
中,每个密钥都有自己的command
,该命令调用 C 程序,用户名与该密钥关联,并且当该密钥用于git
操作时,从 SSH 命令中提取的存储库。 So, when a user adds an SSH key to his account, a line in .ssh/authorized_keys
is added, eg因此,当用户将 SSH 密钥添加到他的帐户时,会在.ssh/authorized_keys
中添加一行,例如
command="/home/git/check_perms username \"${SSH_ORIGINAL_COMMAND}\"" no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa ...
Then every time a git operation is performed using this key, the command
is run and if user does not have sufficient permissions, git operation is terminated.然后每次使用此密钥执行 git 操作时,都会运行该command
,如果用户没有足够的权限,则终止 git 操作。
I solved this by using git-shell and a little bit of bash. The general idea is to check whether the key'd user is supposed to access a given repository, and if so, running their command with git-shell
.我通过使用git-shell和一点点 bash 解决了这个问题。一般的想法是检查密钥用户是否应该访问给定的存储库,如果是,则使用git-shell
运行他们的命令。
The shell script is as follows; shell脚本如下;
#!/bin/bash
SSH_ORIGINAL_COMMAND=${SSH_ORIGINAL_COMMAND#git-}
a=( ${SSH_ORIGINAL_COMMAND//\'/} ) # strip single qoutes [^1]
cmd=${a[0]} # git * command
path=${a[-1]} # targeted path
unset a[0] a[-1]
# optional arguments
if (( ${#a[@]} )); then
args="#{a[*]} "
fi
# check if the target path is whitelisted
for a_path in $@; do
f_path="${a_path}${path#@(a_path/|../)}/" # f(ull)_path
if [ -d "$f_path" ]; then
/path/to/git-shell -c "git $cmd $args'$f_path'"
fi
done
We then modify the git user's .ssh/authorized_keys
;然后我们修改 git 用户的.ssh/authorized_keys
;
command="/path/to/script /foo/ /bar/ /biz/" ssh-...
Arbitrary command execution is prevented by exlusively using git-shell
, so we only have to worry about access to unauthorised repositories -- this is addressed in two ways;通过独家使用git-shell
可以防止任意命令执行,所以我们只需要担心访问未经授权的存储库——这有两种方式解决;
../
when composing f_path
.在编写f_path
时删除../
的实例。eg;例如;
- foo [Authorised]
|_ biz
- bar
|_ boo
====
- `git clone git@server.dom:foo/../bar/boo` fails
- `git clone git@server.dom:bar` fails
- `git clone git@server.dom:foo/biz` passes
[1]: The way that a
is formed here may not play nice with globing; [1]:这里形成a
的方式可能不适合 globing; see this SO answer for more context.有关更多上下文,请参阅此 SO 答案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.