Gitosis是Gitolite的鼻祖,同样也是一款基于SSH公钥认证的Git服务管理工具,但是功能要比之前介绍的Gitolite要弱的多。Gitosis由Python语言开发,对于偏爱Python不喜欢Perl的开发者(我就是其中之一),可以对Gitosis加以关注。
Gitosis的出现远早于Gitolite,作者Tommi Virtanen从2007年5月就开始了gitosis的开发,最后一次提交是在2009年9月,已经停止更新了。但是Gitosis依然有其生命力。
配置简洁,可以直接在服务器端编辑,成为为某些服务定制的内置的无需管理的Git服务。
Gitosis的配置文件非常简单,直接保存于服务安装用户(如git)的主目录下.gitosis.conf文件中,可以直接在服务器端创建和编辑。
Gitolite的授权文件需要复杂的编译,因此一般需要管理员克隆gitolite-admin库,远程编辑并推送至服务器。因此用Gitolite实现一个无需管理的Git服务难度要大很多。
支持版本库重定向。
版本库重定向一方面在版本库路径变更后保持旧的URL仍可工作,另一方面用在客户端用简洁的地址屏蔽服务器端复杂的地址。
例如我开发的一款备份工具(Gistore),版本库位于/etc/gistore/tasks/system/repo.git(符号链接),客户端使用system.git即映射到复杂的服务器端地址。
这个功能我已经在定制的Gitolite中实现。
Python语言开发,对于喜欢Python,不喜欢Perl的用户,可以选择Gitosis。
在Github上有很多Gitosis的克隆,我对gitosis的改动放在了github上:
Gitosis因为是Gitolite的鼻祖,因此下面的Gitosis实现机理,似曾相识:
Gitosis安装在服务器(server.name)某个帐号之下,例如git帐号。
管理员通过Git命令检出名为gitosis-admin的版本库。
$ git clone git@server.name:gitosis-admin.git
管理员将git用户的公钥保存在gitosis-admin库的keydir目录下,并编辑gitosis.conf文件为用户授权。
当管理员对gitosis-admin库的修改提交并PUSH到服务器之后,服务器上gitosis-admin版本库的钩子脚本将执行相应的设置工作。
新用户公钥自动追加到服务器端安装帐号的.ssh/authorized_keys中,并设置该用户的shell为gitosis的一条命令gitosis-serve。
command="gitosis-serve jiangxin",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa <公钥内容来自于 jiangxin.pub ...>
更新服务器端的授权文件~/.gitosis.conf。
用户可以用Git命令访问授权的版本库。
当管理员授权,用户可以远程在服务器上创建新版本库。
下面介绍Gitosis的部署和使用。在下面的示例中,约定:服务器的名称为server,Gitolite的安装帐号为git。
Gitosis的部署和使用可以直接参考源代码中的README.rst。可以直接访问Github上我的gitosis克隆,因为Github能够直接将rst文件显示为网页。
参考:
http://github.com/ossxp-com/gitosis/blob/master/README.rst
Gitosis安装需要在服务器端执行。下面介绍直接从源代码进行安装,以便获得最新的改进。
Gitosis的官方Git库位于git://eagain.net/gitosis.git。我在Github上创建了一个分支:
使用git下载Gitosis的源代码。
$ git clone git://github.com/ossxp-com/gitosis.git
进入 gitosis目录,执行安装。
$ cd gitosis
$ sudo python setup.py install
可执行脚本安装在/usr/local/bin目录下。
$ ls /usr/local/bin/gitosis-*
/usr/local/bin/gitosis-init /usr/local/bin/gitosis-run-hook /usr/local/bin/gitosis-serve
安装Gitosis,还需要在服务器端创建专用帐号,所有用户都通过此帐号访问Git库。一般为方便易记,选择git作为专用帐号名称。
$ sudo adduser --system --shell /bin/bash --disabled-password --group it
创建用户git,并设置用户的shell为可登录的shell,如/bin/bash,同时添加同名的用户组。
有的系统,只允许特定的用户组(如ssh用户组)的用户才可以通过SSH协议登录,这就需要将新建的git用户添加到ssh用户组中。
$ sudo adduser git ssh
Gitosis服务初始化,就是初始化一个gitosis-admin库,并为管理员分配权限,还要将Gitosis管理员的公钥添加到专用帐号的~/.ssh/authorized_keys文件中。
如果管理员在客户端没有公钥,使用下面命令创建。
$ ssh-keygen
管理员上传公钥到服务器。
$ scp ~/.ssh/id_rsa.pub server:/tmp
服务器端进行Gitosis服务初始化。
以git用户身份执行gitosis-init命令,并向其提供管理员公钥。
$ sudo su - git
$ gitosis-init < /tmp/id_rsa.pub
确保gitosis-admin版本库的钩子脚本可执行。
$ sudo chmod a+x ~git/repositories/gitosis-admin.git/hooks/post-update
当Gitosis安装完成后,在服务器端自动创建了一个用于Gitosis自身管理的Git库:gitosis-admin.git。
管理员在客户端克隆gitosis-admin.git库,注意要确保认证中使用正确的公钥:
$ git clone git@server:gitosis-admin.git
$ cd gitosis-admin/
$ ls -F
gitosis.conf keydir/
$ ls keydir/
jiangxin.pub
可以看出gitosis-admin目录下有一个陪孩子文件和一个目录keydir。
keydir/jiangxin.pub文件
keydir目录下初始时只有一个用户公钥,即管理员的公钥。管理员的用户名来自公钥文件末尾的用户名。
gitosis.conf文件
该文件为授权文件。初始内容为:
1 [gitosis]
2
3 [group gitosis-admin]
4 writable = gitosis-admin
5 members = jiangxin
可以看到授权文件的语法完全不同于之前介绍的Gitolite的授权文件。整个授权文件是以用户组为核心,而非版本库为核心。
定义了一个用户组gitosis-admin。
第3行开始定义了一个用户组gitosis-admin。
第5行设定了该用户组包含的用户列表。
初始时只有一个用户,即管理员公钥所属的用户。
第4行设定了该用户组对那些版本库具有写操作。
这里配置对gitosis-admin版本库具有写操作。写操作自动包含了读操作。
增加新用户,就是允许新用户能够通过其公钥访问Git服务。只要将新用户的公钥添加到gitosis-admin版本库的keydir目录下,即完成新用户的添加。
管理员从用户获取公钥,并将公钥按照username.pub格式进行重命名。
用户可以通过邮件或者其他方式将公钥传递给管理员,切记不要将私钥误传给管 理员。如果发生私钥泄漏,马上重新生成新的公钥/私钥对,并将新的公钥传递给管理员,并申请将旧的公钥作废。
关于公钥名称,我引入了类似Gitolite的实现:
管理员进入gitosis-admin本地克隆版本库中,复制新用户公钥到keydir目录。
$ cp /path/to/dev1.pub keydir/
$ cp /path/to/dev2.pub keydir/
执行git add命令,将公钥添加入版本库。
$ git add keydir
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: keydir/dev1.pub
# new file: keydir/dev2.pub
#
执行git commit,完成提交。
$ git commit -m "add user: dev1, dev2"
[master d7952a5] add user: dev1, dev2
2 files changed, 2 insertions(+), 0 deletions(-)
create mode 100644 keydir/dev1.pub
create mode 100644 keydir/dev2.pub
执行git push,同步到服务器,才真正完成新用户的添加。
$ git push
Counting objects: 7, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 1.03 KiB, done.
Total 5 (delta 0), reused 0 (delta 0)
To git@server:gitosis-admin.git
2482e1b..d7952a5 master -> master
如果这时查看服务器端~git/.ssh/authorized_keys文件,会发现新增的用户公钥也附加其中:
### autogenerated by gitosis, DO NOT EDIT
command="gitosis-serve jiangxin",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty <用户jiangxin的公钥...>
command="gitosis-serve dev1",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa <用户 dev1 的公钥...>
command="gitosis-serve dev2",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa <用户 dev1 的公钥...>
新用户添加完毕,可能需要重新进行授权。更改授权的方法也非常简单,即修改gitosis.conf配置文件,提交并推送。
首先管理员进入gitosis-admin本地克隆版本库中,编辑gitosis.conf。
$ vi gitosis.conf
授权指令比较复杂,先通过建立一个新用户组并授权新版本库testing尝试一下更改授权文件。例如在gitosis.conf中添加如下授权内容:
1 [group testing-admin]
2 members = jiangxin @gitosis-admin
3 admin = testing
4
5 [group testing-devloper]
6 members = dev1 dev2
7 writable = testing
8
9 [group testing-reader]
10 members = @all
11 readonly = testing
上面的授权文件为版本库testing赋予了三个角色。分别是@testing-admin用户组,@testing-developer用户组和@testing-reader用户组。
第1行开始的testing-admin小节,定义了用户组@testing-admin。
第2行设定该用户组包含的用户有jiangxin,以及前面定义的@gitosis-admin用户组用户。
第3行用admin指令,设定该用户组用户可以创建版本库testing。
admin指令是笔者新增的授权指令,请确认安装的Gitosis包含笔者的改进。
第7行用writable授权指令,设定该@testing-developer用户组用户可以读写版本库testing。
笔者改进后的Gitosis也可以使用write作为writable指令的同义词指令。
第11行用readonly授权指令,设定该@testing-reader用户组用户(所有用户)可以只读访问版本库testing。
笔者改进后的Gitosis也可以使用read作为readonly指令的同义词指令。
编辑结束,提交改动。
$ git add gitosis.conf
$ git commit -q -m "auth for repo testing."
执行git push,同步到服务器,才真正完成授权文件的编辑。
$ git push
在[gitosis]小节中定义Gitosis的缺省设置。如下:
1 [gitosis]
2 repositories = /gitroot
3 #loglevel=DEBUG
4 gitweb = yes
5 daemon = yes
6 generate-files-in = /home/git/gitosis
其中:
第2行,设置版本库缺省的根目录是/gitroot目录。
否则缺省路径是安装用户主目录下的repositories目录。
第3行,如果打开注释,则版本库操作时显示Gitosis调试信息。
第4行,启用gitweb的整合。
可以通过[repo name]小节为版本库设置描述字段,用户显示在Gitweb中。
第5行,启用git-daemon的整合。
即新创建的版本库中,创建文件git-daemon-export-ok。
第6行,设置创建的项目列表文件(供gitweb使用)所在的目录。
缺省即为安装用户的主目录下的gitosis目录。
1 [group gitosis-admin]
2 write = gitosis-admin
3 members = jiangxin
4 repositories = /home/git
除了第4行,其他内容在前面都已经介绍过了,是Gitosis自身管理版本库的用户组设置。
第4行,重新设置了版本库的缺省根路经,覆盖缺省的[gitosis]小节中的缺省根路径。实际的gitosis-admin版本库的路径为/home/git/gitosis-admin.git。
下面的两个示例小节定义了两个用户组,并且用到了路径变换的指令。
1 [group ossxp-admin]
2 members = @gitosis-admin jiangxin
3 admin = ossxp/**
4 read = gistore/*
5 map admin redmine-* = ossxp/redmine/\1
6 map admin ossxp/redmine-* = ossxp/(redmine-.*):ossxp/redmine/\1
7 map admin ossxp/testlink-* = ossxp/(testlink-.*):ossxp/testlink/\1
8 map admin ossxp/docbones* = ossxp/(docbones.*):ossxp/docutils/\1
9
10 [group all]
11 read = ossxp/**
12 map read redmine-* = ossxp/redmine/\1
13 map read testlink-* = ossxp/testlink/\1
14 map read pysvnmanager-gitsvn = mirrors/pysvnmanager-gitsvn
15 map read ossxp/redmine-* = ossxp/(redmine-.*):ossxp/redmine/\1
16 map read ossxp/testlink-* = ossxp/(testlink-.*):ossxp/testlink/\1
17 map read ossxp/docbones* = ossxp/(docbones.*):ossxp/docutils/\1
18 repositories = /gitroot
在上面的示例中,演示了授权指令以及Gitosis特色的map指令。
第1行,定义了用户组@ossxp-admin。
第2行,设定该用户组包含用户jiangxin以及用户组@gitosis-admin的所有用户。
第3行,设定该用户组具有创建及读写与通配符ossxp/**匹配的版本库。
两个星号匹配任意字符包括路径分隔符(/)。此功能属于笔者扩展的功能。
第4行,设定该用户组可以只读访问gistore/*匹配的版本库。
一个星号匹配任意字符包括路径分隔符(/)。 此功能也属于笔者扩展的功能。
第5行,是Gitosis特有的版本库名称重定位功能。
即对redmine-*匹配的版本库,先经过名称重定位,在名称前面加上ossxp/remdine。其中\\1代表匹配的整个版本库名称。
用户组@ossxp-admin的用户对于重定位后的版本库,具有admin(创建和读写)权限。
第6行,是我扩展的版本库名称重定位功能,支持正则表达式。
等号左边的名称进行通配符匹配,匹配后,再经过右侧的一对正则表达式进行转换(冒号前的用于匹配,冒号后的用于替换)。
第10行,使用了内置的@all用户组,因此不需要通过members设定用户,因为所有用户均属于该用户组。
第11行,设定所有用户均可以只读访问ossxp/**匹配的版本库。
第12-17行,对特定路径进行映射,并分配只读权限。
第18行,设置版本库的根路径为/gitroot,而非缺省的版本库根路径。
Gitosis和Gitweb的整合,提供了两个方面的内容。一个是可以设置版本库的描述信息,用于在gitweb的项目列表页面显示。另外一个是自动生成项目的列表文件供Gitweb参考,避免Gitweb使用效率低的目录递归搜索查找Git版本库列表。
例如在gitosis.conf中下面的配置用于对redmine-1.0.x版本库的Gitweb整合进行设置。
1 [repo ossxp/redmine/redmine-1.0.x]
2 gitweb = yes
3 owner = Jiang Xin
4 description = Redmine 1.0.x 群英汇定制开发
第1行,repo小节用于设置版本库的Gitweb整合。
版本库的实际路径是用版本库缺省的根(即在[gitosis]小节中定义的或者缺省的)加上此小节中的版本库路径组合而成的。
第2行,启用Gitweb整合。如果省略,使用全局[gitosis]小节中gitweb的设置。
第3行,用于设置版本库的属主。
第4行,用于设置版本库的描述信息,显示在Gitweb的版本库列表中。
每一个repo小节所指向的版本库,如果启用了Gitweb选项,则版本库名称汇总到一个项目列表文件中。该项目列表文件缺省保存在~/gitosis/projects.list中。
Gitosis维护的版本库位于安装用户主目录下的repositories目录中,即如果安装用户为git,则版本库都创建在/home/git/repositories目录之下。可以通过配置文件gitosis.conf修改缺省的版本库的根路径。
可以直接在服务器端创建,或者在客户端远程创建版本库。
克隆即创建,还是PUSH即创建?
在客户端远程创建版本库时,Gitosis的原始实现是对版本库具有writable(读写)权限的用户,对不存在的版本库执行克隆操作时,自动创建。但是我认为这不是一个好的实践,会经常因为克隆的URL写错,导致在服务器端创建垃圾版本库。笔者改进的实现如下:
远程在服务器上创建版本库的方法如下:
首先,本地建库。
$ mkdir somerepo
$ cd somerepo
$ git init
$ git commit --allow-empty
使用git remote指令添加远程的源。
$ git remote add origin git@server:ossxp/somerepo.git
运行git push完成在服务器端版本库的创建
$ git push origin master
轻量级管理的含义是不采用缺省的稍显复杂的管理模式(远程克隆gitosis-admin库,修改并PUSH的管理模式),而是直接在服务器端通过预先定制的配置文件提供Git服务。这种轻量级管理模式,对于为某些应用建立快速的Git库服务提供了便利。
例如在使用备份工具Gistore进行文件备份时,可以用Gitosis架设轻量级的Git服务,可以在远程使用Git命令进行双机甚至是异地备份。
首先创建一个专用帐号,并设置该用户只能执行gitosis-serve命令。例如创建帐号gistore,通过修改/etc/ssh/sshd_config配置文件,实现限制该帐号登录的可执行命令。
Match user gistore
ForceCommand gitosis-serve gistore
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
PubkeyAuthentication yes
#PasswordAuthentication no
上述配置信息告诉SSH服务器,凡是以gistore用户登录的帐号,强制执行Gitosis的命令。
然后,在该用户的主目录下创建一个配置文件.gitosis.conf(注意文件名前面的点号),如下:
[gitosis]
repositories = /etc/gistore/tasks
gitweb = yes
daemon = no
[group gistore]
members = gistore
map readonly * = (.*):\1/repo
上述配置的含义是: