Git之深入解析如何运行自己的Git仓库托管服务器

Posted Forever_wj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Git之深入解析如何运行自己的Git仓库托管服务器相关的知识,希望对你有一定的参考价值。

一、协议

  • 了解了 Git 的基础使用流程和 Git 的分支管理之后,我们应该已经有办法使用 Git 来完成日常的工作。然而,为了使用 Git 协作功能,还需要有远程的 Git 仓库。尽管在技术上可以从个人仓库进行推送(push)和拉取(pull)来修改内容,但不鼓励使用这种方法,因为一不留心就很容易弄混其他人的进度。如果还不清楚 Git 的基础使用流程和分支的管理,请参考博客:
  • 此外,如果希望合作者们即使在我们的电脑未联机时亦能存取仓库,拥有一个更可靠的公用仓库十分有用。因此,与他人合作的最佳方法即是建立一个我们与合作者们都有权利访问,且可从那里推送和拉取资料的共用仓库。
  • 架设一台 Git 服务器并不难,首先需要选择服务器使用的通讯协议,那么这些通讯协议如何设置以及各自的优缺点是什么? 它们如何在我们的服务器上运行呢?通常,Git 可以使用四种不同的协议来传输资料:本地协议(Local),HTTP 协议,SSH(Secure Shell)协议及 Git 协议。
  • 一个远程仓库通常只是一个裸仓库(bare repository),即一个没有当前工作目录的仓库。因为该仓库仅仅作为合作媒介,不需要从磁盘检查快照;存放的只有 Git 的资料。简单的说,裸仓库就是工程目录内的 .git 子目录内容,不包含其它资料。

① 本地协议

  • 最基本的就是本地协议(Local protocol),其中的远程版本库就是同一主机上的另一个目录,这常见于团队每一个成员都对一个共享的文件系统(例如一个挂载的 NFS)拥有访问权,或者比较少见的多人共用同一台电脑的情况。后者并不理想,因为所有代码版本库如果长存于同一台电脑,更可能发生灾难性的损失。
  • 如果使用共享文件系统,就可以从本地版本库克隆(clone)、推送(push)以及拉取(pull),像这样去克隆一个版本库或者增加一个远程到现有的项目中,使用版本库路径作为 URL。例如,克隆一个本地版本库,可以执行如下的命令:
$ git clone /srv/git/project.git
// 或执行命令:
$ git clone file:///srv/git/project.git
  • 如果在 URL 开头明确的指定 file://,那么 Git 的行为会略有不同;如果仅是指定路径,Git 会尝试使用硬链接(hard link)或直接复制所需要的文件;如果指定 file://,Git 会触发平时用于网路传输资料的进程,那样传输效率会更低。指定 file:// 的主要目的是取得一个没有外部参考(extraneous references) 或对象(object)的干净版本库副本,通常是在从其他版本控制系统导入后或一些类似情况需要这么做处理,在此将使用普通路径,因为这样通常更快。
  • 要增加一个本地版本库到现有的 Git 项目,可以执行如下的命令:
$ git remote add local_proj /srv/git/project.git
  • 然后,就可以通过新的远程仓库名 local_proj 像在网络上一样从远端版本库推送和拉取更新。
  • 优点:
    • 基于文件系统的版本库的优点是简单,并且直接使用了现有的文件权限和网络访问权限。如果你的团队已经有共享文件系统,建立版本库会十分容易,只需要像设置其他共享目录一样,把一个裸版本库的副本放到大家都可以访问的路径,并设置好读/写的权限就可以。
    • 这也是快速从别人的工作目录中拉取更新的方法,如果你和别人一起合作一个项目,他想让你从版本库中拉取更新时,运行类似 git pull /home/john/project 的命令比推送到服务器再抓取回来简单多了。
  • 缺点:
    • 这种方法的缺点是,通常共享文件系统比较难配置,并且比起基本的网络连接访问,这不方便从多个位置访问。如果你想从家里推送内容,必须先挂载一个远程磁盘,相比网络连接的访问方式,配置不方便,速度也慢。
    • 值得一提的是,如果你使用的是类似于共享挂载的文件系统时,这个方法不一定是最快的。访问本地版本库的速度与你访问数据的速度是一样的。在同一个服务器上,如果允许 Git 访问本地硬盘,一般的通过 NFS 访问版本库要比通过 SSH 访问慢。
    • 最终,这个协议并不保护仓库避免意外的损坏。每一个用户都有“远程”目录的完整 shell 权限,没有方法可以阻止他们修改或删除 Git 内部文件和损坏仓库。

② HTTP 协议

  • Git 通过 HTTP 通信有两种模式,在 Git 1.6.6 版本之前只有一个方式可用,十分简单并且通常是只读模式的,Git 1.6.6 版本引入了一种新的、更智能的协议,让 Git 可以像通过 SSH 那样智能的协商和传输数据。之后几年,这个新的 HTTP 协议因为其简单、智能变的十分流行,新版本的 HTTP 协议一般被称为 智能 HTTP 协议,旧版本的一般被称为 哑 HTTP 协议。
  • 智能 HTTP 协议
    • 智能 HTTP 的运行方式和 SSH 及 Git 协议类似,只是运行在标准的 HTTP/S 端口上并且可以使用各种 HTTP 验证机制,这意味着使用起来会比 SSH 协议简单的多,比如可以使用 HTTP 协议的用户名/密码授权,免去设置 SSH 公钥。
    • 智能 HTTP 协议或许已经是最流行的使用 Git 的方式,它即支持像 git:// 协议一样设置匿名服务,也可以像 SSH 协议一样提供传输时的授权和加密,而且只用一个 URL 就可以都做到,省去了为不同的需求设置不同的 URL。如果你要推送到一个需要授权的服务器上(一般来讲都需要),服务器会提示你输入用户名和密码,从服务器获取数据时也一样。
    • 事实上,类似 GitHub 的服务,你在网页上看到的 URL,和在克隆、推送(如果你有权限)时使用的是一样的。
  • 哑(Dumb) HTTP 协议
    • 如果服务器没有提供智能 HTTP 协议的服务,Git 客户端会尝试使用更简单的“哑” HTTP 协议。哑 HTTP 协议里 web 服务器仅把裸版本库当作普通文件来对待,提供文件服务。哑 HTTP 协议的优美之处在于设置起来简单,基本上,只需要把一个裸版本库放在 HTTP 根目录,设置一个叫做 post-update 的挂钩就可以了。此时,只要能访问 web 服务器上你的版本库,就可以克隆你的版本库。下面是设置从 HTTP 访问版本库的方法:
$ cd /var/www/htdocs/
$ git clone --bare /path/to/git_project gitproject.git
$ cd gitproject.git
$ mv hooks/post-update.sample hooks/post-update
$ chmod a+x hooks/post-update
    • Git 自带的 post-update 挂钩会默认执行合适的命令(git update-server-info),来确保通过 HTTP 的获取和克隆操作正常工作,这条命令会在你通过 SSH 向版本库推送之后被执行,然后别人就可以通过类似下面的命令来克隆:
$ git clone https://example.com/gitproject.git
    • 这里使用了 Apache 里设置了常用的路径 /var/www/htdocs,不过可以使用任何静态 Web 服务器,只需要把裸版本库放到正确的目录下就可以,Git 的数据是以基本的静态文件形式提供的,通常会在可以提供读/写的智能 HTTP 服务和简单的只读的哑 HTTP 服务之间选一个,极少会将二者混合提供服务。
  • 优点
    • 只关注智能 HTTP 协议,不同的访问方式只需要一个 URL 以及服务器只在需要授权时提示输入授权信息,这两个简便性让终端用户使用 Git 变得非常简单。相比 SSH 协议,可以使用用户名/密码授权是一个很大的优势,这样用户就不必须在使用 Git 之前先在本地生成 SSH 密钥对再把公钥上传到服务器。对非资深的使用者,或者系统上缺少 SSH 相关程序的使用者,HTTP 协议的可用性是主要的优势。与 SSH 协议类似,HTTP 协议也非常快和高效。
    • 也可以在 HTTPS 协议上提供只读版本库的服务,如此你在传输数据的时候就可以加密数据;或者,你甚至可以让客户端使用指定的 SSL 证书。
    • 另一个好处是 HTTPS 协议被广泛使用,一般的企业防火墙都会允许这些端口的数据通过。
  • 缺点:
    • 在一些服务器上,架设 HTTPS 协议的服务端会比 SSH 协议的棘手一些。除了这一点,用其他协议提供 Git 服务与智能 HTTP 协议相比就几乎没有优势。
    • 如果你在 HTTP 上使用需授权的推送,管理凭证会比使用 SSH 密钥认证麻烦一些。然而,你可以选择使用凭证存储工具,比如 macOS 的 Keychain 或者 Windows 的凭证管理器。

③ SSH 协议

  • 架设 Git 服务器时常用 SSH 协议作为传输协议,因为大多数环境下服务器已经支持通过 SSH 访问,即使没有也很容易架设。SSH 协议也是一个验证授权的网络协议,并且因为其普遍性,架设和使用都很容易。
  • 通过 SSH 协议克隆版本库,可以指定一个 ssh:// 的 URL:
$ git clone ssh://[user@]server/project.git
  • 或者使用一个简短的 scp 式的写法:
$ git clone [user@]server:project.git
  • 在上面两种情况中,如果你不指定可选的用户名,那么 Git 会使用当前登录的用的名字。
  • 优势:
    • 首先,SSH 架设相对简单,守护进程很常见,多数管理员都有使用经验,并且多数操作系统都包含了它及相关的管理工具;
    • 其次,通过 SSH 访问是安全的,所有传输数据都要经过授权和加密;
    • 最后,与 HTTPS 协议、Git 协议及本地协议一样,SSH 协议很高效,在传输前也会尽量压缩数据。
  • 缺点:
    • SSH 协议的缺点在于它不支持匿名访问 Git 仓库,如果你使用 SSH,那么即便只是读取数据,使用者也必须通过 SSH 访问你的主机,这使得 SSH 协议不利于开源的项目,毕竟人们可能只想把你的仓库克隆下来查看;
    • 如果你只在公司网络使用,SSH 协议可能是你唯一要用到的协议;
    • 如果你要同时提供匿名只读访问和 SSH 协议,那么你除了为自己推送架设 SSH 服务以外,还得架设一个可以让其他人访问的服务。

④ Git 协议

  • Git 协议是包含在 Git 里的一个特殊的守护进程;它监听在一个特定的端口(9418),类似于 SSH 服务,但是访问无需任何授权。要让版本库支持 Git 协议,需要先创建一个 git-daemon-export-ok 文件,它是 Git 协议守护进程为这个版本库提供服务的必要条件。但是除此之外没有任何安全措施,要么谁都可以克隆这个版本库,要么谁也不能,这意味着,通常不能通过 Git 协议推送。由于没有授权机制,一旦你开放推送操作,意味着网络上知道这个项目 URL 的人都可以向项目推送数据。不用说,极少会有人这么做。
  • 优点:
    • 目前,Git 协议是 Git 使用的网络传输协议里最快的,如果你的项目有很大的访问量,或者你的项目很庞大并且不需要为写进行用户授权,架设 Git 守护进程来提供服务是不错的选择;
    • 它使用与 SSH 相同的数据传输机制,但是省去了加密和授权的开销。
  • 缺点:
    • Git 协议缺点是缺乏授权机制,把 Git 协议作为访问项目版本库的唯一手段是不可取的,一般的做法里,会同时提供 SSH 或者 HTTPS 协议的访问服务,只让少数几个开发者有推送(写)权限,其他人通过 git:// 访问只有读权限;
    • Git 协议也许也是最难架设的,它要求有自己的守护进程,这就要配置 xinetd、systemd 或者其他的程序,这些工作并不简单;
    • 它还要求防火墙开放 9418 端口,但是企业防火墙一般不会开放这个非标准端口,而大型的企业防火墙通常会封锁这个端口。

二、在服务器上搭建 Git

  • 在开始架设 Git 服务器前,需要把现有仓库导出为裸仓库,即一个不包含当前工作目录的仓库,这通常是很简单的。为了通过克隆你的仓库来创建一个新的裸仓库,需要在克隆命令后加上 --bare 选项,按照惯例,裸仓库的目录名以 .git 结尾,就像这样:
$ git clone --bare my_project my_project.git
Cloning into bare repository 'my_project.git'...
done.
  • 现在,你的 my_project.git 目录中应该有 Git 目录的副本。整体上效果大致相当于:
$ cp -Rf my_project/.git my_project.git
  • 虽然在配置文件中有若干不同,但是对于你的目的来说,这两种方式都是一样的,它只取出 Git 仓库自身,不要工作目录,然后特别为它单独创建一个目录。

① 把裸仓库放到服务器上

  • 既然有了裸仓库的副本,剩下要做的就是把裸仓库放到服务器上并设置你的协议。假设一个域名为 git.example.com 的服务器已经架设好,并可以通过 SSH 连接,你想把所有的 Git 仓库放在 /srv/git 目录下,服务器上存在 /srv/git/ 目录,可以通过以下命令复制裸仓库来创建一个新仓库:
$ scp -r my_project.git user@git.example.com:/srv/git
  • 此时,其它可通过 SSH 读取此服务器上 /srv/git 目录的用户,可运行以下命令来克隆仓库:
$ git clone user@git.example.com:/srv/git/my_project.git
  • 如果一个用户,通过使用 SSH 连接到一个服务器,并且其对 /srv/git/my_project.git 目录拥有可写权限,那么它将自动拥有推送权限。
  • 如果到该项目目录中运行 git init 命令,并加上 --shared 选项,那么 Git 会自动修改该仓库目录的组权限为可写。运行此命令的工程中不会摧毁任何提交、引用等内容。
$ ssh user@git.example.com
$ cd /srv/git/my_project.git
$ git init --bare --shared
  • 由此可见,根据现有的 Git 仓库创建一个裸仓库,然后把它放上你和协作者都有 SSH 访问权的服务器是多么容易,现在已经准备好在同一项目上展开合作。
  • 值得注意的是,这的确是架设一个几个人拥有连接权的 Git 服务的全部,只要在服务器上加入可以用 SSH 登录的帐号,然后把裸仓库放在大家都有读写权限的地方。

② 小型安装

  • 如果设备较少或者你只想在小型开发团队里尝试 Git ,那么一切都很简单。架设 Git 服务最复杂的地方在于用户管理,如果需要仓库对特定的用户可读,而给另一部分用户读写权限,那么访问和许可安排就会比较困难。

③ SSH 连接

  • 如果你有一台所有开发者都可以用 SSH 连接的服务器,架设你的第一个仓库就十分简单了,因为你几乎什么都不用做(正如我们上一节所说的)。如果你想在你的仓库上设置更复杂的访问控制权限,只要使用服务器操作系统的普通的文件系统权限就行了。
  • 如果需要团队里的每个人都对仓库有写权限,又不能给每个人在服务器上建立账户,那么提供 SSH 连接就是唯一的选择。
  • 假设用来共享仓库的服务器已经安装了 SSH 服务,并且已经通过它访问服务器,现在有几个方法可以使你给团队每个成员提供访问权:
    • 第一个就是给团队里的每个人创建账号,这种方法很直接但也很麻烦,或许你不会想要为每个人运行一次 adduser(或者 useradd)并且设置临时密码。
    • 第二个办法是在主机上建立一个 git 账户,让每个需要写权限的人发送一个 SSH 公钥,然后将其加入 git 账户的 ~/.ssh/authorized_keys 文件。这样一来,所有人都将通过 git 账户访问主机,这一点也不会影响提交的数据,访问主机用的身份不会影响提交对象的提交者信息。
    • 另一个办法是让 SSH 服务器通过某个 LDAP 服务,或者其它已经设定好的集中授权机制,来进行授权。只要每个用户可以获得主机的 shell 访问权限,任何 SSH 授权机制你都可视为是有效的。

三、生成 SSH 公钥

  • 如前所述,许多 Git 服务器都使用 SSH 公钥进行认证,为了向 Git 服务器提供 SSH 公钥,如果某系统用户尚未拥有密钥,必须事先为其生成一份,这个过程在所有操作系统上都是相似的。首先,你需要确认自己是否已经拥有密钥,默认情况下,用户的 SSH 密钥存储在其 ~/.ssh 目录,进入该目录并列出其中内容,便可以快速确认自己是否已拥有密钥:
$ cd ~/.ssh
$ ls
authorized_keys2  id_dsa       known_hosts
config            id_dsa.pub
  • 我们需要寻找一对以 id_dsa 或 id_rsa 命名的文件,其中一个带有 .pub 扩展名,.pub 文件是公钥,另一个则是与之对应的私钥。如果找不到这样的文件(或者根本没有 .ssh 目录),可以通过运行 ssh-keygen 程序来创建它们。在 Linux/macOS 系统中,ssh-keygen 随 SSH 软件包提供,在 Windows 上,该程序包含于 MSysGit 软件包中:
$ ssh-keygen -o
Generating public/private rsa key pair.
Enter file in which to save the key (/home/kody/.ssh/id_rsa):
Created directory '/home/kody/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/kody/.ssh/id_rsa.
Your public key has been saved in /home/kody/.ssh/id_rsa.pub.
The key fingerprint is:
d0:82:24:8e:d7:f1:bb:9b:33:53:96:93:49:da:9b:e3 kody@mylaptop.local
  • 首先 ssh-keygen 会确认密钥的存储位置(默认是 .ssh/id_rsa),然后它会要求你输入两次密钥口令,如果你不想在使用密钥时输入口令,将其留空即可。然而,如果你使用了密码,那么请确保添加了 -o 选项,它会以比默认格式更能抗暴力破解的格式保存私钥,也可以用 ssh-agent 工具来避免每次都要输入密码。
  • 现在,进行了上述操作的用户需要将各自的公钥发送给任意一个 Git 服务器管理员 (假设服务器正在使用基于公钥的 SSH 验证设置)。它们所要做的就是复制各自的 .pub 文件内容,并将其通过邮件发送,公钥看起来是这样的:
$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU
GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3
Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA
t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En
mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx
NrRFi9wrf+M7Q== kody@mylaptop.local

四、配置服务器

  • 配置服务器端的 SSH 访问,我们将使用 authorized_keys 方法来对用户进行认证,同时假设使用的操作系统是标准的 Linux 发行版,比如 Ubuntu。
  • 首先,创建一个操作系统用户 git,并为其建立一个 .ssh 目录:
$ sudo adduser git
$ su git
$ cd
$ mkdir .ssh && chmod 700 .ssh
$ touch .ssh/authorized_keys && chmod 600 .ssh/authorized_keys
  • 接着,需要为系统用户 git 的 authorized_keys 文件添加一些开发者 SSH 公钥,假设我们已经获得了若干受信任的公钥,并将它们保存在临时文件中,与前文类似,这些公钥看起来是这样的:
$ cat /tmp/id_rsa.john.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCB007n/ww+ouN4gSLKssMxXnBOvf9LGt4L
ojG6rs6hPB09j9R/T17/x4lhJA0F3FR1rP6kYBRsWj2aThGw6HXLm9/5zytK6Ztg3RPKK+4k
Yjh6541NYsnEAZuXz0jTTyAUfrtU3Z5E003C4oxOj6H0rfIF1kKI9MAQLMdpGW1GYEIgS9Ez
Sdfd8AcCIicTDWbqLAcU4UpkaX8KyGlLwsNuuGztobF8m72ALC/nLF6JLtPofwFBlgc+myiv
O7TCUSBdLQlgMVOFq1I2uPWQOkOWQAHukEOmfjy2jctxSDBQ220ymjaNsHT4kgtZg2AYYgPq
dAv8JggJICUvax2T9va5 gsg-keypair
  • 将这些公钥加入系统用户 git 的 .ssh 目录下 authorized_keys 文件的末尾:
$ cat /tmp/id_rsa.john.pub >> ~/.ssh/authorized_keys
$ cat /tmp/id_rsa.josie.pub >> ~/.ssh/authorized_keys
$ cat /tmp/id_rsa.jessica.pub >> ~/.ssh/authorized_keys
  • 现在来为开发者新建一个空仓库,可以借助带 --bare 选项的 git init 命令,该命令在初始化仓库时不会创建工作目录:
$ cd /srv/git
$ mkdir project.git
$ cd project.git
$ git init --bare
Initialized empty Git repository in /srv/git/project.git/
  • 接着,John、Josie 或者 Jessica 中的任意一人可以将他们项目的最初版本推送到这个仓库中,他只需将此仓库设置为项目的远程仓库并向其推送分支。请注意,每添加一个新项目,都需要有人登录服务器取得 shell,并创建一个裸仓库。假定这个设置了 git 用户和 Git 仓库的服务器使用 gitserver 作为主机名,同时,假设该服务器运行在内网,并且你已在 DNS 配置中将 gitserver 指向此服务器,那么可以运行如下命令(假定 myproject 是已有项目且其中已包含文件):
# on John's computer
$ cd myproject
$ git init
$ git add .
$ git commit -m 'initial commit'
$ git remote add origin git@gitserver:/srv/git/project.git
$ git push origin master
  • 此时,其他开发者可以克隆此仓库,并推回各自的改动,步骤很简单:
$ git clone git@gitserver:/srv/git/project.git
$ cd project
$ vim README
$ git commit -am 'fix for the README file'
$ git push origin master
  • 通过这种方法,就可以快速搭建一个具有读写权限、面向多个开发者的 Git 服务器。
  • 需要注意的是,目前所有(获得授权的)开发者用户都能以系统用户 git 的身份登录服务器从而获得一个普通 shell,如果想对此加以限制,则需要修改 /etc/passwd 文件中(git 用户所对应)的 shell 值。借助一个名为 git-shell 的受限 shell 工具,可以方便地将用户 git 的活动限制在与 Git 相关的范围内,该工具随 Git 软件包一同提供。如果将 git-shell 设置为用户 git 的登录 shell(login shell),那么该用户便不能获得此服务器的普通 shell 访问权限,若要使用 git-shell,需要用它替换掉 bash 或 csh,使其成为该用户的登录 shell。
  • 为进行上述操作,首先必须确保 git-shell 的完整路径名已存在于 /etc/shells 文件中:
$ cat /etc/shells   # see if git-shell is already in there. If not...
$ which git-shell   # make sure git-shell is installed on your system.
$ sudo -e /etc/shells  # and add the path to git-shell from last command
  • 现在可以使用 chsh -s 命令修改任一系统用户的 shell:
$ sudo chsh git -s $(which git-shell)
  • 这样,用户 git 就只能利用 SSH 连接对 Git 仓库进行推送和拉取操作,而不能登录机器并取得普通 shell。如果试图登录,会发现尝试被拒绝,像这样:
$ ssh git@gitserver
fatal: Interactive git shell is not enabled.
hint: ~/git-shell-commands should exist and have read and execute access.
Connection to gitserver closed.
  • 此时,用户仍可通过 SSH 端口转发来访问任何可达的 git 服务器。如果你想要避免它,可编辑 authorized_keys 文件并在所有想要限制的公钥之前添加以下选项:
no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty
  • 其结果如下:
$ cat ~/.ssh/authorized_keys
no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABAQCB007n/ww+ouN4gSLKssMxXnBOvf9LGt4LojG6rs6h
PB09j9R/T17/x4lhJA0F3FR1rP6kYBRsWj2aThGw6HXLm9/5zytK6Ztg3RPKK+4kYjh6541N
YsnEAZuXz0jTTyAUfrtU3Z5E003C4oxOj6H0rfIF1kKI9MAQLMdpGW1GYEIgS9EzSdfd8AcC
IicTDWbqLAcU4UpkaX8KyGlLwsNuuGztobF8m72ALC/nLF6JLtPofwFBlgc+myivO7TCUSBd
LQlgMVOFq1I2uPWQOkOWQAHukEOmfjy2jctxSDBQ220ymjaNsHT4kgtZg2AYYgPqdAv8JggJ
ICUvax2T9va5 gsg-keypair

no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABAQDEwENNMomTboYI+LJieaAY16qiXiH3wuvENhBG...
  • 现在,网络相关的 Git 命令依然能够正常工作,但是开发者用户已经无法得到一个普通 shell 了。正如输出信息所提示的,也可以在 git 用户的主目录下建立一个目录,来对 git-shell 命令进行一定程度的自定义。比如,可以限制掉某些本应被服务器接受的 Git 命令,或者对刚才的 SSH 拒绝登录信息进行自定义,这样,当有开发者用户以类似方式尝试登录时,便会看到你的信息。要了解更多有关自定义 shell 的信息,请运行 git help shell。

五、Git 守护进程

  • 通过 Git 协议建立一个基于守护进程的仓库,对于快速且无需授权的 Git 数据访问,这是一个理想之选。但要注意,因为其不包含授权服务,任何通过该协议管理的内容将在其网络上公开。
  • 如果运行在防火墙之外的服务器上,它应该只对那些公开的只读项目服务;如果运行在防火墙之内的服务器上,它可用于支撑大量参与人员或自动系统 (用于持续集成或编译的主机)只读访问的项目,这样可以省去逐一配置 SSH 公钥的麻烦。
  • 无论何时,该 Git 协议都是相对容易设定的,通常只需要以守护进程的形式运行该命令:
$ git daemon --reuseaddr --base-path=/srv/git/ /srv/git/
  • –reuseaddr 选项允许服务器在无需等待旧连接超时的情况下重启,而 --base-path 选项允许用户在未完全指定路径的条件下克隆项目,结尾的路径将告诉 Git 守护进程从何处寻找仓库来导出。如果有防火墙正在运行,需要开放端口 9418 的通信权限,可以通过许多方式将该进程以守护进程的方式运行,这主要取决于所使用的操作系统。
  • 由于在现代的 Linux 发行版中,systemd 是最常见的初始化系统,因此可以用它来达到此目的,只要在 /etc/systemd/system/git-daemon.service 中放一个文件即可,其内容如下:
[Unit]
Description=Start Git Daemon

[Service]
ExecStart=/usr/bin/git daemon --reuseaddr --base-path=/srv/git/ /srv/git/

Restart=always
RestartSec=500ms

StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=git-daemon

User=git
Group=git

[Install]
WantedBy=multi-user.target
  • 可能会注意到这里以 git 启动的 Git 驻留程序同时使用了 Group 和 User 权限,按需修改它并确保提供的用户在此系统上。此外,请确保 Git 二进制文件位于 /usr/bin/git,必要时可修改此路径。
  • 最后,需要运行 systemctl enable git-daemon 以让它在系统启动时自动运行,这样也能让它通过 systemctl start git-daemon 启动,通过 systemctl stop git-daemon 停止。
  • 在其它系统中,可以使用 sysvinit 系统中的 xinetd 脚本,或者另外的方式来实现,只要能够将其命令守护进程化并实现监控。
  • 接下来,需要告诉 Git 哪些仓库允许基于服务器的无授权访问,可以在每个仓库下创建一个名为 git-daemon-export-ok 的文件来实现:
$ cd /path/to/project.git
$ touch git-daemon-export-ok
  • 该文件将允许 Git 提供无需授权的项目访问服务。

六、Smart HTTP

  • 一般通过 SSH 进行授权访问,通过 git:// 进行无授权访问,但是还有一种协议可以同时实现以上两种方式的访问。设置 Smart HTTP 一般只需要在服务器上启用一个 Git 自带的名为 git-http-backend 的 CGI 脚本,该 CGI 脚本将会读取由 git fetch 或 git push 命令向 HTTP URL 发送的请求路径和头部信息, 来判断该客户端是否支持 HTTP 通信(不低于 1.6.6 版本的客户端支持此特性)。 如果 CGI 发现该客户端支持智能(Smart)模式,它将会以智能模式与它进行通信, 否则它将会回落到哑(Dumb)模式下(因此它可以对某些老的客户端实现向下兼容)。
  • 在完成以上简单的安装步骤后,将用 Apache 来作为 CGI 服务器,如果没有安装 Apache,可以在 Linux 环境下执行如下或类似的命令来安装:
$ sudo apt-get install apache2 apache2-utils
$ a2enmod cgi alias env
  • 该操作将会启用 mod_cgi, mod_alias, 和 mod_env 等 Apache 模块,这些模块都是使该功能正常工作所必须的,还需要将 /srv/git 的 Unix 用户组设置为 www-data,这样 Web 服务器才能读写该仓库,因为 运行 CGI 脚本的 Apache 实例默认会以该用户的权限运行:
$ chgrp -R www-data /srv/git
  • 接下来要向 Apache 配置文件添加一些内容,来让 git-http-backend 作为 Web 服务器对 /git 路径请求的处理器:
SetEnv GIT_PROJECT_ROOT /srv/git
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAlias /git/ /usr/lib/git-core/git-http-backend/
  • 如果留空 GIT_HTTP_EXPORT_ALL 这个环境变量,Git 将只对无授权客户端提供带 git-daemon-export-ok 文件的版本库,就像 Git 守护进程一样。
  • 最后,如果想让 Apache 允许 git-http-backend 请求并实现写入操作的授权验证,使用如下授权屏蔽配置即可:
<Files "git-http-backend">
    AuthType Basic
    AuthName "Git Access"
    AuthUserFile /srv/git/.htpasswd
    Require expr !(%{QUERY_STRING} -strmatch '*service=git-receive-pack*' || %{REQUEST_URI} =~ m#/git-receive-pack$#)
    Require valid-user
</Files>
  • 这需要创建一个包含所有合法用户密码的 .htpasswd 文件,如下所示,下是一个添加 “kody” 用户到此文件的例子:
$ htpasswd -c /srv/git/.htpasswd kody
  • 可以通过许多方式添加 Apache 授权用户,选择使用其中一种方式即可。也可以通过 SSL 运行它,以保证所有数据是在加密状态下进行传输的。
  • 这里不想深入去讲解 Apache 配置文件,因为可能会使用不同的 Web 服务器,或者可能有不同的授权需求。它的主要原理是使用一个 Git 附带的,名为 git-http-backend 的 CGI,它被引用来处理协商通过 HTTP 发送和接收的数据,它本身并不包含任何授权功能,但是授权功能可以在 Web 服务器层引用它时被轻松实现。可以在任何所有可以处理 CGI 的 Web 服务器上办到这点,所以随便挑一个最熟悉的 Web 服务器试手。

七、GitWeb

  • 如果对项目有读写权限或只读权限,可能需要建立起一个基于网页的简易查看器。Git 提供了一个叫做 GitWeb 的 CGI 脚本来做这项工作。
  • 如果想要查看 GitWeb 如何展示项目,并且在服务器上安装了轻量级 Web 服务器比如 lighttpd 或 webrick, Git 提供了一个命令来启动一个临时的服务器:
    • 在 Linux 系统的电脑上,lighttpd 通常已经安装了,所以只需要在项目目录里执行 git instaweb 命令即可;
    • 如果使用 Mac 系统,Mac OS X Leopard 系统已经预安装了 Ruby,所以 webrick 或许是你最好的选择;
    • 如果不想使用 lighttpd 启动 instaweb 命令,需要在执行时加入 --httpd 参数。
$ git instaweb --httpd=webrick
[2021-09-15 10:02:21] INFO  WEBrick 1.3.1
[2021-09-15 10:02:21] INFO  ruby 1.8.6 (2021-09-15) [universal-darwin9.0]
  • 这个命令启动了一个监听 1234 端口的 HTTP 服务器,并且自动打开了浏览器。当已经完成了工作并想关闭这个服务器,可以执行同一个命令,并加上 --stop 选项:
$ git instaweb --httpd=webrick --stop
  • 如果现在想为团队或托管的开源项目持续的运行这个页面,需要通过普通的 Web 服务器来设置 CGI 脚本,一些 Linux 发行版的软件库有 gitweb 包,可以通过 apt 或 dnf 来安装。接下来来快速的了解一下如何手动安装 GitWeb。首先需要获得 Git 的源代码,它包含了 GitWeb ,并可以生成自定义的 CGI 脚本:
$ git clone git://git.kernel.org/pub/scm/git/git.git
$ cd git/
$ make GITWEB_PROJECTROOT="/srv/git" prefix=/usr gitweb
    SUBDIR gitweb
    SUBDIR ../
make[2]: `GIT-VERSION-FILE' is up to date.
    GEN gitweb.cgi
    GEN static/gitweb.js
$ sudo cp -Rf gitweb /var/www/
  • 需要注意的是,需要在命令中指定 GITWEB_PROJECTROOT 变量来让程序知道 Git 版本库的位置。现在,需要在 Apache 中使用这个 CGI 脚本,为此添加一个虚拟主机:
<VirtualHost *:80>
    ServerName gitserver
    DocumentRoot /var/www/gitweb
    <Directory /var/www/gitweb>
        Options +ExecCGI +FollowSymLinks +SymLinksIfOwnerMatch
        AllowOverride All
        order allow,deny
        Allow from all
        AddHandler cgi-script cgi
        DirectoryIndex gitweb.cgi
    </Directory>
</VirtualHost>
  • 再次提醒,GitWeb 可以通过任何一个支持 CGI 或 Perl 的网络服务器架设;如果需要的话,架设起来应该不会很困难,现在可以访问 http://gitserver/ 在线查看版本库。

八、GitLab

  • 虽然 GitWeb 相当简单,但如果你正在寻找一个更现代,功能更全的 Git 服务器,这里有几个开源的解决方案可供你选择安装。
  • GitLab 是其中最出名的一个,我们将它作为示例并讨论它的安装和使用,这比 GitWeb 要复杂的多并且需要更多的维护,但它的确是一个功能更全的选择。

① 安装

  • GitLab 是一个数据库支持的 web 应用,所以相比于其他 git 服务器,它的安装过程涉及到更多的东西。幸运的是,这个过程有非常详细的文档说明和支持。
  • 为了更快速的启动和运行,可以下载虚拟机镜像或者在 GitLab CE 上获取一键安装包,同时调整配置使之符合你特定的环境。Bitnami 的一个优点在于它的登录界面(通过 alt+→ 键进入), 它会告诉你安装好的 GitLab 的 IP 地址以及默认的用户名和密码。例如,Bitnami GitLab 虚拟机登录界面,如下所示:

  • 无论如何,跟着 GitLab 社区版的 readme 文件一步步来,可以在这里找到它 gitlab-foss ,你将会在主菜单中找到安装 GitLab 的帮助,一个可以在 Digital Ocean 上运行的虚拟机,以及 RPM 和 DEB 包(都是测试版),还有 “非官方” 的引导让 GitLab 运行在非标准的操作系统和数据库上,一个全手动的安装脚本,以及许多其他的话题。

② 管理

  • GitLab 的管理界面是通过网络进入的,将你的浏览器转到已经安装 GitLab 的 主机名或 IP 地址,然后以管理员身份登录即可,默认的用户名是 admin@local.host,默认的密码是 5iveL!fe(会得到类似“请登录后尽快更换密码”的提示), 登录后,点击主栏上方靠右位置的 “Admin area” 图标进行管理。

③ 使用者

  • GitLab 上的用户指的是对应协作者的帐号,用户帐号没有很多复杂的地方,主要是包含登录数据的用户信息集合。每一个用户账号都有一个 命名空间 ,即该用户项目的逻辑集合。如果一个叫 jane 的用户拥有一个名称是 project 的项目,那么这个项目的 url 会是 http://server/jane/project。GitLab 用户管理界面如下:

  • 移除一个用户有两种方法,“屏蔽(Blocking)” 一个用户阻止他登录 GitLab 实例,但是该用户命名空间下的所有数据仍然会被保存,并且仍可以通过该用户提交对应的登录邮箱链接回他的个人信息页。
  • 而另一方面,“销毁(Destroying)” 一个用户,会彻底的将他从数据库和文件系统中移除。他命名空间下的所有项目和数据都会被删除,拥有的任何组也会被移除。这显然是一个更永久且更具破坏力的行为,所以很少用到这种方法。

④ 组

  • 一个 GitLab 的组是一些项目的集合,连同关于多少用户可以访问这些项目的数据。每一个组都有一个项目命名空间(与用户一样),所以如果一个叫 training 的组拥有一个名称是 materials 的项目,那么这个项目的 url 会是 http://server/training/materials。GitLab 组管理界面如下所示:

  • 每一个组都有许多用户与之关联,每一个用户对组中的项目以及组本身的权限都有级别区分。权限的范围从 “访客”(仅能提问题和讨论) 到 “拥有者”(完全控制组、成员和项目),权限的种类太多以至于难以在这里一一列举,不过在 GitLab 的管理界面上有帮助链接。

⑤ 项目