简体   繁体   English

如何根据给定的ssh key限制git SSH访问

[英]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 clonegit 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文件中的选项,尤其是commandenvironment 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:我最终得到了以下解决方案:

  • A PostgreSQL database with tables user and repo , and many-to-many table permissions .具有表userrepo以及多对多表permissions的 PostgreSQL 数据库。
  • A program written in C that checks whether a user has read/write permissions to access requested repository which is extracted from $SSH_ORIGINAL_COMMAND .用 C 编写的程序,用于检查用户是否具有访问从$SSH_ORIGINAL_COMMAND中提取的请求存储库的读/写权限。
  • In .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-...

Attacks攻击

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可以防止任意命令执行,所以我们只需要担心访问未经授权的存储库——这有两种方式解决;

  • Removing instances of ../ when composing f_path .在编写f_path时删除../的实例。
  • Scoping the targeted path when checking for directory existence.检查目录是否存在时确定目标路径的范围。

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

References参考

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM