繁体   English   中英

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

[英]How to restrict git SSH access based on ssh key given

我有一个私人 git 服务器,一个用户git和 ssh 密钥认证。 目前,我是唯一一个使用它的人,但我想添加更多人,我希望每个人都使用用户git连接到服务器并执行git clonegit push等。如果所有存储库都是“公共”,那么我知道如何解决这个问题。 但是例如我想要一个私有存储库,我仍然会使用我的 SSH 密钥通过git clone git@server:repo克隆它,但是我不希望其他用户能够使用他们的 SSH 密钥克隆它。

我检查了关于设置服务器的 git-scm 文档(这对公共存储库很有帮助)和这篇文章,但是这篇文章似乎只解决了私有存储库的问题。

TLDR:当你在 GitHub 上克隆一个存储库时,你说git clone git@github.com:user/repo和你的 git 用户名和 ssh 密钥被发送过来。 现在,根据您是否拥有此存储库的权限,您可以克隆它,否则不能。 所以基本上,表面上每个人都在使用git用户,但在幕后进行了一些授权。 例如,GitHub 是如何处理的?

要实现类似的功能,您可以依赖专用工具,例如gitolite

否则,您可以安装一个成熟的GitLab 服务器,该服务器还可以提供对存储库的细粒度访问控制,使用“Git URL”,例如git@your-gitlab-domain.com:user/repo.git

(顺便说一句,似乎 GitLab 以前依赖gitolite版本 5.0.0 之前

gitolite似乎正是您正在寻找的基于 ssh 密钥管理对存储库/分支的访问。

但是如果你想从头开始构建这样的东西,你需要查看authorized_keys文件中的选项,尤其是commandenvironment 使用这些,您可以强制执行特定的命令/脚本或添加/覆盖环境变量,基于使用的 ssh 密钥。

例如,您可以编写一个脚本,将用户允许的存储库读取为其 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...

该脚本现在可以从$SSH_ORIGINAL_COMMAND读取请求的 repo 并检查它是否包含在传递的列表中。 这个脚本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"

gitolite之类的工具过于复杂且使用起来不愉快,或者无法提供所需的功能。 我最终得到了以下解决方案:

  • 具有表userrepo以及多对多表permissions的 PostgreSQL 数据库。
  • 用 C 编写的程序,用于检查用户是否具有访问从$SSH_ORIGINAL_COMMAND中提取的请求存储库的读/写权限。
  • .ssh/authorized_keys中,每个密钥都有自己的command ,该命令调用 C 程序,用户名与该密钥关联,并且当该密钥用于git操作时,从 SSH 命令中提取的存储库。

因此,当用户将 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 ...

然后每次使用此密钥执行 git 操作时,都会运行该command ,如果用户没有足够的权限,则终止 git 操作。

我通过使用git-shell和一点点 bash 解决了这个问题。一般的想法是检查密钥用户是否应该访问给定的存储库,如果是,则使用git-shell运行他们的命令。

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

然后我们修改 git 用户的.ssh/authorized_keys

command="/path/to/script /foo/ /bar/ /biz/" ssh-...

攻击

通过独家使用git-shell可以防止任意命令执行,所以我们只需要担心访问未经授权的存储库——这有两种方式解决;

  • 在编写f_path时删除../的实例。
  • 检查目录是否存在时确定目标路径的范围。

例如;

 - 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]:这里形成a的方式可能不适合 globing; 有关更多上下文,请参阅此 SO 答案

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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