Jul 30

Gitolite 是一款由 Perl 语言开发的 Git 服务管理工具,通过 SSH 公钥对用户进行认证,并能够通过配置文件对版本库的写操作进行基于分支、标签以及文件路径等的权限控制。 gitweb 为 Gitolite 提供了版本库的 Web 界面,方便用户浏览版本库的变更与版本打包;另一方面,由于 Gitolite 并没有提供匿名用户的读权限,因此,需要借助 git-daemon 来弥补这一缺陷。

1、Gitolite 的安装与配置

Gitolite 的安装非常简单,执行下述命令即可:
$ sudo yum install gitolite
查看 Gitolite 的用户信息
$ cat /etc/passwd | grep -i gitolite
gitolite:x:976:971:git repository hosting:/var/lib/gitolite:/bin/sh
这表明 Fedora 中 Gitolite 的家目录位于 /var/lib/gitolite。
 
下面开始初始化 Gitolite。由于 Gitolite 采用的是 SSH 协议,因此在初始化开始之前必须注入 Gitolite 管理者的 SSH 公匙。这里不去谈论 SSH 密匙的生成过程了,直接假定拥有 SSH 密匙的用户 James 将成为 Gotolite 的管理者:
$ cp ~/.ssh/id_rsa.pub /tmp/james.pub
请注意公匙的命名方法,这是 Gitolite 区别各个用户的默认办法。另外,可能出于安全的考虑,我们可能修改过 ~/.ssh 目录下文件的权限。由于下面的操作需要用户 James 的公匙具有写权限,所以需要执行下面的命令行
$ chmod a+r /tmp/james.pub
还有一件事情需要指出,既然 Gitolite 使用的是 SSH 协议,因此它工作的前提是运行 sshd 系统服务,请查看它是否运行
$ systemctl status sshd

如果没有运行的话,请执行下述命令行,至于 SSH 端口之类的问题就不做赘述了:

$ sudo systemctl start sshd
$ sudo systemctl enable sshd
现在可以初始化 Gitolite 了:
$ sudo -u gitolite -H gl-setup /tmp/james.pub
 
好了,下面该让我们稍微解释一下 Gitolite 对 Git 服务器的管理方式了。 Gitolite 通过导入用户的 SSH 公匙的方式来实现对 GIT 服务器的用户管理。Gitolite 利用配置文件 /var/lib/gitolite/repositoreis/gitolite-admin.git/config/gitolite.conf 实现了用户权限的控制。非常有意思的是,Gitolite 以 GIT 版本库的形式在管理 gitolite.conf 与所有用户的 SSH 公匙。具体来说,Gitolite 中预设了两个版本库,即 gitolite-admin.git 与 testing.git:
$ sudo ls /var/lib/gitolite/repositories
gitolite-admin.git  testing.git
可能我们已经注意到,版本库目录中的版本库名字均是以 .git 结尾的,这是 Gitolite 的约定。Gitolite 的管理者正是通过 gitolite-admin.git 来管理版本库的,他可以以版本控制的方式管理它。而 Gitolie 会自动根据该版本库中的修改来产生新的配置。好了,让我们的管理者把 gitolite-admin.git 克隆到他的家目录中去吧:
$ cd
$ git clone gitolite@serverIP:gitolite-admin.git
$ cd gitolite-admin
$ cat conf/gitolite.conf
repo    gitolite-admin
        RW+     =   james

repo    testing
        RW+     =   @all
我们很容易看到 gitolite.conf 中奇特的语法。每一个版本库的授权均以 repo 指令开头。repo 后面紧跟的是版本库列表。版本库列表中的版本库名之间用空格分开,版本库名中可以含有相对路径名。根据 Gitolite 的默认规则,repo 后面的版本库名不需要以 .git 为后缀,因为 Gitolite 会自动添加该后缀名。另外,版本库列表里面还可以包括版本库组。版本库组就是一些以空格间隔的版本库名的合集,版本库组名以@开头,例如
@testings testing james/testing
repo @testings
repo 指令也可以紧跟以正则表达式定义的通配符版本库。需要注意通配符版本库在匹配时,会自动在通配符版本库的正则表达式前、后分别加上前缀 ^ 和后缀 $。例如
repo james/.+
要当心的是版本库名中的正则表达式过于简单,就可能引起歧义,让 Gitolite 以为它就是普通版本库名称。为了不必要的麻烦,可在正则表达式的前或后主动加上 ^ 或 $ 符号,例如
repo james/.+$
 
repo 指令后续的几行就是版本库的授权命令列表。每条授权指令有相同的缩进,且以 = 为标记分为前后两段,等号前面的是权限表达式,等号后面的是用户列表。授权指令的语法类似于
perms [regex ...] = user1 [user2 ...]
正如我们所看到的,授权指令中包含一个可选的 regex 列表。regex 列表有正则表达式给出,它们与 repo 所指定的版本库里的分支、标签甚至是文件名或路径进行匹配。具体说来,正则表达式 regex 可能与分支名匹配,也可能与分支组匹配。分支组名以@开头,它是一些有空格间隔的分支名的合集,例如
@mainstream master developing testing
regex 也可能与 refs/tags/ 中的标签名匹配;regex 还可能与版本库中的文件名或文件路径匹配。如果 regex 中的表达式不以 $ 结尾的话,那么它将匹配以该正则表达式开头的任意字符,相当于在表达式后面添加 .*$。如果授权指令中不包含任何 regex 列表,那么该授权将针对由 repo 指定整个的版本库。
 
perms 部分只能出现如下授权之一:-、C、R、RW、RW+、RWC、RW+C、RWD、RW+D、RWCD、RW+CD。它们的具体含义如下:
  • -:不能写入、但是可以读取;
  • C:仅在通配符版本库中可以使用。用于指定谁可以创建和通配符匹配的版本库。
  • R, RW, 和 RW+:R 为只读。RW 为读写权限。RW+ 含义为除了具有读写外,还可以对 rewind 的提交强制 PUSH。
  • RWC, RW+C:只有当授权指令中定义了 regex 列表才可以使用该授权指令。其中 C 的含义是允许创建与 regex 匹配的分支、标签、文件名或者路径。
  • RWD, RW+D:只有当授权指令中定义了 regex 列表才可以使用该授权指令。其中 D 的含义是允许删除与 regex 匹配的分支、标签、文件名或者路径。
  • RWCD, RW+CD:只有当授权指令中定义了 regex 列表才可以使用该授权指令。
Gitolie 中的用户通常是以公匙文件名来指定的,这是 Gitolite 中的用户的表示方法之一。另一种 Gitolite 用户的表示方法就是用户组,用户组的命名以@开头,例如
@myteam james alice
定义了一个名为 @myteam 的用户组,james、alice 都是属于 myteam 这个用户组。Gitolite 中有个特殊的用户组,即@all,用来表示所有的用户。
 
下面给出一个权限控制的示例
@admin james
@myteam james alice
@test alice

@testings james/testing alice/testing

@mainstream master developing

repo    james/.+$
        C                           =   @admin
        R       @mainstream         =   @test
        -                           =   bob
        RW                          =   @myteam bob

repo    testing @testings
        RW+                         =   @admin
        RW      master              =   bob
        RW      developing$         =   alice
        -                           =   bob
        RW      tmp/                =   @all
        RW      refs/tags/v[0-9]    =   alice
这里有一点需要指出,那就是当同一个用户或者组有多个授权时,Gitolite 将采用所有授权的并集。
 
为了将上面的介绍与实际应用相结合,不妨考虑为 Gitolite 增加一个版本库 myproj、两个用户组 @admin 与 @myteam,以及用户 Alice 并授予她 myproj.git 的读写权限与用户 Bob 但只给予 myproj.git 的读权限。先让 Gitolite 的管理者 James 执行下述命令:
$ cd ~/gitolite-admin
$ cp /path/to/alice.pub keydir/
$ cp /path/to/bob.pub keydir/
$ nano -w conf/gitolite.conf
@admin james
@myteam james alice

repo    gitolite-admin
        RW+     =   @admin

repo    testing
        RW+     =   @all

repo    myproj
        RW+     =   @myteam
        RW+CD   =   @admin
        R       =   bob
$ git add conf/gitolite.conf keydir/alice.pub keydir/bob.pub
$ git commit -m 'new user and repo created.'
$ git push origin master
接着再让 James 在自己的主机家目录下创建新项目 myproj 并推至服务器:
$ mkdir -p ~/myproj
$ cd ~/myproj
$ git init
$ echo "hello, Gitolite!" >> hello.txt
$ git add hello.txt
$ git commit -m 'initial commit.'
$ git remote add origin gitolite@serverIP:myproj.git
$ git push origin master
现在试试让 Alice 在自己的主机上克隆并做一次提交
$ cd ~
$ git clone gitolite@serverIP:myproj.git
$ cd myproj
$ echo "hi, I'm Alice." >> hello.txt
$ git add hello.txt
$ git commit  -m 'say hello to Git Server."
$ git push origin master

再来让 Bob 试试 GIT 服务器的情况:

$ cd ~
$ git clone gitolite@serverIP:gitolite-admin.git

正常情况,Bob 无法克隆该仓库;

$ git clone gitolite@serverIP:testing.git
$ cd testing
$ echo 1 > index.txt
$ git add index.txt
$ git commit -m 'write permission for testing repo.'
$ git push origin master

这表明 Bob 拥有 testing.git 的读写权限;再来看

$ cd
$ git clone gitolite@serverIP:myproj.git
$ myproj
$ echo "hello, I'm Bob." >> hello.txt
$ git add hello.txt
$ git commit -m 'say hello to Gitolite.'
$ git push origin master

这表明 Bob 只能读取 myproj 但没有写权限。这几个操作表明 Gitolite 比较完美的控制了用户操作 GIT 服务器的权限。

2、与 gitweb 的整合
 
最初 gitweb 是为 GIT 服务器的 http 协议提供 Web 界面的。这里正是借助这个功能,为 Gitolite 实现同样的功能。同时,Gitolite 也为 gitweb 提供了两个便利:为 gitweb 提供了设置版本库的描述信息途径,可用于在 gitweb 的项目列表页面显示;为 gitweb 自动生成项目的列表文件,避免 gitweb 使用效率低的目录递归搜索查找 Git 版本库列表。
 
安装 gitweb 与 highlight:
$ sudo yum install highlight gitweb
其中 highlight 是为了在网页中查看代码的时候可以高亮代码的语法。查看 gitweb 的配置文件所在的位置:
$ rpm -qc gitweb
/etc/gitweb.conf
/etc/httpd/conf.d/git.conf
/etc/httpd/conf.d/git.conf 是与 Apache 相关的配置。gitweb 本身的配置文件是 /etc/gitweb.conf,下面开始配置 gitweb:
$ sudo nano -w /etc/gitweb.conf
根据需要调整里面的内容,下面这几行的内容必须调整
our $projectroot="/var/lib/gitolite/repositories";
our $projects_list="/var/lib/gitolite/projects.list";
至于语法高亮,自己看着办
# Add highlighting at the end 
$feature{'highlight'}{'default'}= [1];
 
为了能够让 Apache 访问到 Gitolite 目录中的文件,需要将 apache 加入到 gitolite 组中,并修改相关文件的权限
$ sudo usermod -a -G gitolite apache
$ sudo chmod g+r /var/lib/gitolite/projects.list
$ sudo chmod -R g+rx /var/lib/gitolite/repositories
由于 Gitolite 中默认新建文件的权限是 0077,这意味着要让 Apache 访问之后新建版本库,必须对之后新建版本库权限作类似的修改。为了免去这一麻烦,这里利用 Gitolite 的配置文件 ~/.gitolite.rc 来指定新建文件的权限:
$ sudo nano -w /var/lib/gitolite/.gitolite.rc
根据自己的需要修改下面的行
#$REPO_UMASK = 0077; # gets you 'rwx------'
#$REPO_UMASK = 0027; # gets you 'rwxr-x---'
$REPO_UMASK = 0022; # gets you 'rwxr-xr-x'
好了,一切就绪了,重启 Apache 服务:
$ sudo service httpd restart
 
以上配置基本都是在告诉 Apache 一些 GIT 服务器的信息。不过,为了让 gitweb 工作,还需要让 Gitolite 知道哪些仓库启用了 gitweb。这可以通过下面这个例子来了解这个机制,让管理者再次修改 Gitolite 的配置文件
$ cd
$ cd gitolite-admin
$ nano -w conf/gitolite.conf
repo    gitolite-admin
        RW+     =   james

repo    testing
        testing "James" =   "Git repository for testing"
        RW+     =   @all
        R       =   gitweb
$ git add conf/gitolite.conf
$ git commit -m 'gitweb support for Gitolite.'
$ git push origin master

很明显,'R = gitweb' 这一行告诉 Gitolite 版本库 testing.git 启用 gitweb,这样 Gitolite 就会把 testing.git 写入 projects.list 文件;而 'testing "James" = "git repository for testing"' 这一行则是为了在网页中显示 testing 仓库的额外信息,也即仓库的创建者以及仓库的概述。其他版本库启用 gitweb 的设置完全类同,这里就不一一修改了。实际上,版本库的信息的设定也通过如下的命令来完成:

repo    testing
        config gitweb.owner         =   some person's name
        config gitweb.description   =   some description
        config gitweb.category      =   some category 
 
是时候打开网页浏览器开始测试了,在地址栏中输入 http://serverIP/git,正常情况应该出现下图。
 
3、与 git-daemon  的整合
 
最后,让我们利用 git-daemon 为 Gitolite 开启匿名访问功能。首先安装它:
$ sudo yum install git-daemon
需要注意的是,由 Fedora 19 中 git-daemon 的 RPM 包存在问题,详见 https://bugzilla.redhat.com/show_bug.cgi?id=980574。因此,如果要在 Fedora 19 中启动 git-daemon 的服务以配合 Gitolite,那么需要作如下的修改:
$ mv /usr/lib/systemd/system/git.service /usr/lib/systemd/system/git@.service
随后将它按如下方式修改:
$ cat /usr/lib/systemd/system/git@.service
[Unit]
Description=Git Repositories Server Daemon
Documentation=man:git-daemon(1)
Wants=git.socket

[Service]
User=nobody
ExecStart=-/usr/libexec/git-core/git-daemon --base-path=/var/lib/gitolite/repositories --export-all --user-path=public_git --syslog --inetd --verbose
StandardInput=socket
由该段代码可以看到,Fedora 19 中 git-daemon 包并没有创建新的用户组,而是以 nobody 用户启动了系统服务。但是,Gitolite 的目录则是赋予了 gitolite 组:
$ ls -ald /var/lib/gitolite
drwxr-xr-- 5 gitolite gitolite 4096 Jul 29 22:31 /var/lib/gitolite
因此,为了让 git-daemon 能够访问 Gitolite 的家目录,需要将 nobody 加入 gitolite 组并赋予 Gitolite 家目录以可读权限
$ sudo usermod -a -G gitolite nobody
$ sudo chmod g+r -R /var/lib/gitolite/repositories
现在启动 git-daemon 服务并让它随机器启动:
$ sudo systemctl start git.socket
$ sudo systemctl enable git.socket
git-daemon 默认端口是 9418,查看该端口是否打开
$ netstat -nat | grep 9418
如果没有打开的话,请编辑 /etc/sysconfig/iptables:
$ nano -w /etc/sysconfig/iptables
在 Drop 与 Reject 规则之前,增加一行
-A INPUT -p tcp -m conntrack --ctstate NEW -m tcp --dport 9418 -j ACCEPT
重启防火墙:
$ sudo service iptables restart
至此,git-daemon 已经知道 Gitolite 的工作目录了。不过,Gitolite 中的版本库还需要为 git-daemon 自动生成 git-daemon-export-ok 文件, 这便是下面的设置,仍需管理者运行:
$ cd 
$ cd gitolite-admin
$ nano -w conf/gitolite.conf
repo    gitolite-admin
        RW+     =   james

repo    testing
        RW+     =   @all
        R       =   daemon
$ git add conf/gitolite.conf
$ git commit -m 'git daemon support for Gitolite.'
$ git push origin master

'R=daemon' 这一行就是告诉 Gitolite 为 git-daemon 生成 git-daemon-export-ok 文件。

好了,现在让我们做简单的测试:

$ git clone git://serverIP/testing.git
一切正常。