写在前面
参考资料
- Apache2 Docs:Access Control Authentication and Authorization Expressions in Apache HTTP Server
-
git-http-backend docs:Git - git-http-backend Documentation
Git远程服务器
基于SSH协议的Git远程服务器
Git支持多种远程协议。而笔者首个自己搭建的Git远程服务器是基于ssh协议的。搭建一个基于ssh协议的Git服务器无需学习更多知识,只需要知道如何配置openssh服务器、修改用户的默认登录shell为git-shell
即可。若想让服务器变得更加安全,可以进一步配置openssh的chroot环境,使Git用户通过openssh登录时,访问的范围仅限自己的家目录。(当然,配置chroot后,git-shell
以及其依赖的各种动态库都需要处理)
但是,SSH有一个非常大的缺点:无论需要对存储库做什么操作(例如clone、push等),SSH都需要进行用户权限认证。这想要做访问控制,只能通过Linux的文件权限进行控制。此外,如果Git仓库要在网上公开发布,SSH的认证过程就成为了阻碍用户获取仓库的一道门槛。
基于HTTP协议的Git远程服务器
因此,我们可以使用http协议来搭建Git远程服务器。我们可以通过配置http服务器上的访问控制策略来更加灵活控制用户对于Git仓库的访问。
需要注意的是,Git并不自己提供http服务器程序,而是提供了一个CGI程序git-http-backend
来与外部http服务器进行对接。在这篇文章里,我们要使用的http服务器是Apache 2.4。
Apache2.4的配置
请参考官方文档Git - git-http-backend Documentation。
配置目标
- 将本地目录
/var/www/git/
下的所有Git仓库映射到Web目录/git/
下 -
所有用户均可以克隆(clone)Git仓库
-
只有指定的用户能推送修改(push)到Git仓库
前置条件
需要在Apache2服务器上启用mod_cgi
、mod_alias
、mod_env
这几个模块。模块的启用方法,可以将/etc/apache2/mods_available
下的对应文件软链接到/etc/apache2/mods_enabled
目录下,再重启服务器即可。
配置环境变量与ScriptAlias
SetEnv GIT_PROJECT_ROOT /var/www/git
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAlias /git/ /usr/libexec/git-core/git-http-backen
SetEnvIf Git-Protocol ".*"GIT_PROTOCOL=$0
其中,SetEnv
与SetEnvIf
用于设置Apache内部的环境变量。这里设置的环境变量将会被传递给git-http-backend
,告知Git仓库存储的位置,并做一些必要的工作。
配置ScriptAlias
的目的是将Web目录/git/
下的所有请求交由git-remote-https
处理。
配置访问权限
Git官方文档是如此配置访问权限的:
RewriteCond %{QUERY_STRING} service=git-receive-pack [OR]
RewriteCond %{REQUEST_URI} /git-receive-pack$
RewriteRule ^/git/ - [E=AUTHREQUIRED:yes]
<LocationMatch "^/git/">
Order Deny,Allow
Deny from env=AUTHREQUIRED
AuthType Basic
AuthName "Git Access"
Require group committers
Satisfy Any
</LocationMatch>
首先,利用mod_rewrite
模块提供的RewriteCond
指令和RewriteRule
指令,在收到push请求(git-receive-pack)时,设置环境变量AUTHREQUIRED
。
随后在LocationMatch
标签下进行权限控制。当环境变量中存在AUTHREQUIRED
时,表明本次收到的请求为pull请求。此时,默认策略就会拒绝访问,转而进入Auth
打头的认证流程。
但是,Apache官方文档Access Control已经说明,Order
、Deny
、Allow
指令已经被弃用(Deprecated),我们应当转而使用Require
指令和<RequireAny>
和<RequireAll>
标签进行权限控制。
事实上,笔者在按照Git官方文档进行配置时,也发生了一些问题。要么是整个目录完全无法访问,即便是clone操作也会导致403;要么是整个目录门户大开,即便是push请求也不要求认证。因此才会转而寻求Apache文档的帮助。
经过笔者的修改,现在文件的访问控制部分如下:
SetEnvIfExpr "%{QUERY_STRING} =~ /.*service=git-receive-pack.*/" REQUIRE_AUTH='yes'
SetEnvIf Request_URI ".*git-receive-pack.*" REQUIRE_AUTH='yes'
<LocationMatch "^/git/.*">
<RequireAny>
AuthType Basic
AuthName "Restricted Area"
AuthUserFile ...
AuthGroupFile ...
Require group git-committers
<RequireAll>
Require all granted
Require not env REQUIRE_AUTH
</RequireAll>
</RequireAny>
</LocationMatch>
开头的两句SetEnvIfExpr
与SetEnvIf
指令做了与Git官方文档中Rewrite
指令一样的事情,那就是当检测到push操作时,设置一个环境变量,供后续认证阶段判断使用。此处设置的环境变量名称为REQUIRE_AUTH
。
其中,最外层的<RequireAny>
标签是逻辑“或”标签,只要其中的任意一个<Require>
指令被满足即可。因此,我们的权限管理外层逻辑就很明了了:满足Require group
或满足<RequireAll>
二者其一即可。
Require group
与前面的Auth
指令配合,要求用户进行登录,并检查用户是否在指定的组git-committer
中。如果在,则这条Require
被满足,值为真;否则为假。-
<RequireAll>
标签主要检查是否有REQUIRE_AUTH
环境变量。如果没有这个环境变量,则表明当前操作不是push操作,此时Require all granted
和Require not
语句都为真,<RequireAll>
整体为真;如果有这个环境变量,则Require not
语句为假,<RequireAll>
整体为假。
我们可以用一个更为简单的布尔表达式表达放行逻辑:
access = (!REQUIRE_AUTH || Require_Group());
至于GitWeb的配置,留待后面的文章进行分解。
文章评论