[单篇] 从 gitolite 实现原理拓展 GitLab 及其他 Git 服务的实现原理
我博客的原文地址 https://www.insp.top/article/gitlab-like-i... 转载请注明原文地址!
通过本文了解 git server 和 SSH 授权实现原理,如何使用数据库而不是 authorized_keys 来管理用户,并实现更精细的自定义访问权限管理。本文不会实现完整的 Git 服务,但可以通过本文了解实现原理。本文内容不算完整原创,有大量整合资料,可能是我写过的质量不算高的文章,但还是有一定意义。写本文的初衷是因为我想实现一个 PHP 版的 Gitlab(不过暂时弃坑,太麻烦,主要作用是我可能要实现一个基于 Git 的内容发布系统),从而搜集了这些资料,便于希望了解这些的朋友。
对于熟悉 SSHD 的肯定帮助就不大了。Ok,现在开始。
汇总的相关资料来源
- How does gitolite work?
- Git-内部原理-传输协议
- 翻译:Gitolite 工作原理
- SELINUX CONTEXTS
- 使用selinux contexts
- 在CentOS 6.X上折腾SELinux
ssh 协议下的 clone、push
clone 和 push 过程
git clone ---> ssh -> | SERVER | -> sshd ---> git-upload-pack ; git fetch ---> ssh -> | ORIGIN SERVER | -> sshd ---> git-upload-pack
git push ---> ssh -> | SERVER | -> sshd ---> git-receive-pack
而 gitlab、gitolite 是通过在 sshd 调用具体命令(git-upload-pack 或 git-receive-pack)之间加了一个中间命令,该命令会进行额外的校验操作(权限验证),最终决定是否执行下一步命令。
由于 git ssh 大多时候都是通过 git 用户(例如 git@github.com,可以看到用户名为 git),但实际情况是每个仓库都有自己的权限,仅通过 git 用户是无法区分的。
因此实际过程应该如下(以 git push 为例):
git push ---> ssh -> | SERVER | -> sshd ---> {custom-shell} ---> git-receive-pack
其实,custom shell 就是一个脚本或可执行文件,问题在于,如何让 sshd 去主动调用这个执行文件,并传递一些我们需要用于判定的参数呢?
sshd 通过 authorized_keys 来设置客户端公钥,客户端通过使用私钥连接时,sshd 会读取该文件遍历其中条目来获取对应设置信息,authorized_keys 设置如下:
command="[path]/custom-shell sitaram",[more options] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA18S2t...
command="[path]/custom-shell usertwo",[more options] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArXtCT...
和我们常见的设置不太一样的是,每一行的开头多了一项设置,即当指定私钥连接时,执行命令并传递指定参数,我们就可以利用其判断具体登录上来的用户到底是谁。另外,sshd 在调用指定 command 之前,会修改环境变量 SSH_ORIGINAL_COMMAND,该值是原本实际 ssh 连接上来希望执行的命令,我们就可以利用这个,在判定完权限后知道该调用什么命令了。
不过这个不适用于对分支进行权限限制,若要限制某用户操作指定分支的权限,则需要通过 git hook 实现,这个可参考相关文档(本文开头已给出)。
更高级的公钥认证
通过 authorized_keys 管理公钥很不灵活,我们更希望通过数据库或其他服务来进行,这便于拆分业务和未来横向扩展。实际上仅需要通过改动 sshd 的配置文件即可。
通过修改 /etc/ssh/sshd_config
中 AuthorizedKeysCommand 即可,且同时要求设定 AuthorizedKeysCommandUser。
在进行下一步前,需要保证系统上的 OpenSSH 版本高于 6.9,否则 AuthorizedKeysCommand 无法接收诸如指纹信息等参数。
设置参考如下:
AuthorizedKeysCommand /server/bin/command.sh %u %t %f
AuthorizedKeysCommandUser root
其中可以看到,附带的参数中有几个占位符:%u
、%t
、%f
,这几个占位符会替换成传递给 AuthorizedKeysCommand 的具体参数,通过 man sshd_config
可查阅到关于这部分的信息:
Arguments to some keywords can make use of tokens, which are expanded at runtime:
%% A literal `%'. %F The fingerprint of the CA key. %f The fingerprint of the key or certificate. %h The home directory of the user. %i The key ID in the certificate. %K The base64-encoded CA key. %k The base64-encoded key or certificate for authentication. %s The serial number of the certificate. %T The type of the CA key. %t The key or certificate type. %u The username.
AuthorizedKeysCommand accepts the tokens %%, %f, %h, %t, and %u.
如上所言,有这么多占位符,但是 AuthorizedKeysCommand 仅支持有限的参数,需要注意。
AuthorizedKeysCommand 命令执行返回结果格式和 authorized_keys 的单条数据结果格式一致即可生效,若返回错误、结束状态非 0,则 SSHD 仍然会去查询 authorized_keys,若 authorized_keys 不存在,则提示输入密码。
需要注意的权限问题
AuthorizedKeysCommand 这个地方如果设置了一个自定义的命令,这个命令不是在 /
目录下,则会出现无法执行,查看日志提示:
May 27 23:50:54 bogon sshd[4445]: error: Could not stat AuthorizedKeysCommand "<your command path>": Permission denied
往往疑惑在于,该命令 mode 为 0700,哪怕是 777 也仍然报错,用户、用户组都正确为什么提示权限问题呢?出现这个问题的原因是 SElinux 导致的,SElinux 权限限制更加精细,通过 stat 命令查看能够正确执行的文件是这样的:
[root@localhost ~]# stat /authkey
File: ‘/authkey’
Size: 889 Blocks: 8 IO Block: 4096 regular file
Device: fd00h/64768d Inode: 143414 Links: 1
Access: (0700/-rwx------) Uid: ( 0/ root) Gid: ( 0/ root)
Context: unconfined_u:object_r:etc_runtime_t:s0
Access: 2018-05-27 23:33:38.977963479 -0400
Modify: 2018-05-27 23:33:12.481929023 -0400
Change: 2018-05-27 23:33:12.485179139 -0400
Birth: -
而不正确的是这样的:
[root@localhost ~]# stat ~/gitserver-authorize
File: ‘/root/gitserver-authorize’
Size: 621 Blocks: 8 IO Block: 4096 regular file
Device: fd00h/64768d Inode: 8455139 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Context: unconfined_u:object_r:admin_home_t:s0
Access: 2018-05-28 00:03:55.153955969 -0400
Modify: 2018-05-28 00:03:55.156122714 -0400
Change: 2018-05-28 00:03:55.156122714 -0400
Birth: -
对比发现在于 Context 字段中第三栏有差异,可执行的值为:etc_runtime_t
,相反,不能执行的为:admin_home_t
,这里需要补充关于这个 Context 的意义,其实所有文件、目录都有该值(废话),可以通过 ls -lZ 查看,对单个文件则可用 stat 查看,可以看到通过 ls 列出的列表中,Context 被列出。
Context 字段结构为:
user:role:type:level
需要注意,user、role 等都是指的 SELinux 的 user 和 role。关于 SELinux 用户和 Linux 用户的关联,则需要通过一系列工具查看。相关资料文章首部已给出,可参考查阅。下面直接给出之前问题的解决办法。
问题原因清楚了,解决办法
- 关闭 SELinux
-
设置文件目录 Context,使用命令 chcon 修改:
chcon -t etc_runtime_t -R /gitserver
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: