5.5. Gitosis服务架设

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上:

    http://github.com/ossxp-com/gitosis

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

5.5.1. 安装Gitosis

Gitosis的部署和使用可以直接参考源代码中的README.rst。可以直接访问Github上我的gitosis克隆,因为Github能够直接将rst文件显示为网页。

参考:

http://github.com/ossxp-com/gitosis/blob/master/README.rst

5.5.1.1. Gitosis的安装

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
    

5.5.1.2. 服务器端创建专用帐号

安装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

5.5.1.3. Gitosis服务初始化

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
    

5.5.2. 管理Gitosis

5.5.2.1. 管理员克隆gitolit-admin管理库

当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版本库具有写操作。写操作自动包含了读操作。

5.5.2.2. 增加新用户

增加新用户,就是允许新用户能够通过其公钥访问Git服务。只要将新用户的公钥添加到gitosis-admin版本库的keydir目录下,即完成新用户的添加。

  • 管理员从用户获取公钥,并将公钥按照username.pub格式进行重命名。

    用户可以通过邮件或者其他方式将公钥传递给管理员,切记不要将私钥误传给管 理员。如果发生私钥泄漏,马上重新生成新的公钥/私钥对,并将新的公钥传递给管理员,并申请将旧的公钥作废。

    关于公钥名称,我引入了类似Gitolite的实现:

    • 用户从不同的客户端主机访问有着不同的公钥,如果希望使用同一个用户名进行授权,可以按照username@host.pub方式命名公钥文件,和名为username@pub的公钥指向同一个用户username
    • 也支持邮件地址格式的公钥,即形如username@gmail.com.pub的公钥。Gitosis能够很智能的区分是以邮件地址命名的公钥还是相同用户在不同主机上的公钥。如果是邮件地址命名的公钥,将以整个邮件地址作为用户名。
  • 管理员进入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 的公钥...>

5.5.2.3. 更改授权

新用户添加完毕,可能需要重新进行授权。更改授权的方法也非常简单,即修改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

5.5.3. Gitosis授权详解

5.5.3.1. Gitosis缺省设置

[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目录。

5.5.3.2. 管理版本库gitosis-admin

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

5.5.3.3. 定义用户组和授权

下面的两个示例小节定义了两个用户组,并且用到了路径变换的指令。

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,而非缺省的版本库根路径。

5.5.3.4. Gitweb整合

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中。

5.5.4. 创建新版本库

Gitosis维护的版本库位于安装用户主目录下的repositories目录中,即如果安装用户为git,则版本库都创建在/home/git/repositories目录之下。可以通过配置文件gitosis.conf修改缺省的版本库的根路径。

可以直接在服务器端创建,或者在客户端远程创建版本库。

克隆即创建,还是PUSH即创建?

在客户端远程创建版本库时,Gitosis的原始实现是对版本库具有writable(读写)权限的用户,对不存在的版本库执行克隆操作时,自动创建。但是我认为这不是一个好的实践,会经常因为克隆的URL写错,导致在服务器端创建垃圾版本库。笔者改进的实现如下:

  • 增加了名为admin(或init)的授权指令,只有具有此授权的用户,才能够创建版本库。
  • 只具有writable(或write)权限的用户,不能在服务器上创建版本库。
  • 不通过克隆创建版本库,而是在对版本库进行PUSH的时候进行创建。当克隆一个不存在的版本库,会报错退出。

远程在服务器上创建版本库的方法如下:

  • 首先,本地建库。

    $ 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
    

5.5.5. 轻量级管理的Git服务

轻量级管理的含义是不采用缺省的稍显复杂的管理模式(远程克隆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

上述配置的含义是:

  • 用户gistore才能够访问/etc/gistore/tasks下的Git库。
  • 版本库的名称要经过变换,例如system库会变换为实际路径/etc/gistore/tasks/system/repo.git