搭建自己的 Git 伺服器

aneasystone本尊發表於2018-12-03

根據 RhodeCode 在 2016 年做過的一項分析報告 Version Control Systems Popularity in 2016,在如今的 VCS(版本控制系統)領域,Git 幾乎已經一統江山,在選擇自己的 VCS 時,有 87% 的人會選擇使用 Git,排在第二名的 SVN 只佔 6%,無論是從 Google Trends,還是在 Stack Overflow 上的提問,都可以看到 Git 的爆發式增長。另外,根據 Eclipse 的社群調查 (Eclipse Community Survey),在 2010 年前後,SVN 的使用率都遠超其他幾款 VCS,從 2010 年開始,SVN 的使用率開始快速下滑,相應的,Git 的使用率快速上升,並在 2014 年超過了 SVN。

vcs-popularity-eclipse-community.jpg

現在,Git 已經成為了程式設計師的必備技能,越來越多的企業開始採用 Git。在開源的世界裡,Github 是程式設計師聚集的狂歡之地,但這並不適合企業的私有專案,雖然 Github 也支援建立私有專案,但是搭建一個自己的 Git 伺服器在很多時候可能是更好的選擇,這篇部落格將介紹並學習幾種搭建 Git 伺服器的方法。

Git 支援四種不同的傳輸協議:本地協議(Local)、HTTP(S) 協議、SSH(Secure Shell)協議以及 Git 協議,這四種協議在不同的場合有不同的用途,並且各有利弊,可以根據實際情況來選擇。

一、本地協議

本地協議是 Git 最基本的協議,當我們想在本地做一些 Git 實驗時,這將非常有用。我們首先建立兩個目錄:/git/repo~/working,前者作為遠端版本庫,後者作為本地工作目錄。

aneasystone@little-stone:~$ sudo mkdir -p /git/repo
aneasystone@little-stone:~$ sudo git init --bare /git/repo/test.git
已初始化空的 Git 倉庫於 /git/repo/test.git/
複製程式碼

我們在 /git/repo 目錄通過 git init --bare 命令建立一個裸倉庫(bare repository,即一個不包含當前工作目錄的倉庫),只要這一步,我們就可以開始使用了。接著我們在工作目錄 clone 這個版本庫:

aneasystone@little-stone:~$ cd ~/working/
aneasystone@little-stone:~/working$ git clone /git/repo/test.git
正克隆到 'test'...
warning: 您似乎克隆了一個空倉庫。
完成。
複製程式碼

然後我們可以使用 pullpush 就像操作其他的版本庫一樣。

aneasystone@little-stone:~/working$ cd test/
aneasystone@little-stone:~/working/test$ touch 1
aneasystone@little-stone:~/working/test$ touch 2
aneasystone@little-stone:~/working/test$ git add .
aneasystone@little-stone:~/working/test$ git commit -m 'first commit'
[master (根提交) 4983f84] first commit
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 1
 create mode 100644 2
aneasystone@little-stone:~/working/test$ sudo git push
[sudo] aneasystone 的密碼: 
物件計數中: 3, 完成.
Delta compression using up to 8 threads.
壓縮物件中: 100% (2/2), 完成.
寫入物件中: 100% (3/3), 205 bytes | 205.00 KiB/s, 完成.
Total 3 (delta 0), reused 0 (delta 0)
To /git/repo/test.git
 * [new branch]      master -> master
複製程式碼

本地協議不僅在做 Git 實驗時很有用,如果你的團隊有一個共享檔案系統,可以在這個共享檔案系統上建立一個遠端版本庫,團隊成員把這個共享檔案系統掛在本地,就可以直接使用本地協議進行協作開發,完全不需要搭建一臺專門的 Git 伺服器。

二、SSH 協議

本地協議雖然簡單,但是一般來說並不適用,因為你無法控制使用者對共享檔案系統的操作,使用者擁有 push 許可權也就意味著使用者對遠端目錄擁有完整的 Shell 許可權,他們有可能會無意甚至有意的修改或刪除 Git 內部檔案,損壞 Git 倉庫。

更安全的做法是使用專門的 Git 伺服器,如果你有一臺可以使用 SSH 連線的伺服器,搭建 Git 服務將會非常簡單。首先我們要確保伺服器上執行著 SSH 服務(sshd),大多數 Linux 伺服器版本都預設包含了該服務,如果沒有,可以先安裝 openssh-server。然後在伺服器上建立 Git 遠端版本庫:

root@myserver:~# mkdir -p /git/repo
root@myserver:~# git init --bare /git/repo/test.git
已初始化空的 Git 倉庫於 /git/repo/test.git/
複製程式碼

然後在本地 clone 這個版本庫:

aneasystone@little-stone:~/working$ git clone ssh://root@myserver/git/repo/test.git
正克隆到 'test'...
root@myserver's password: 
warning: 您似乎克隆了一個空倉庫。
複製程式碼

可以看到和使用本地協議幾乎一樣,不同的地方在於,在 clone 的時候需要在 URL 前面加上 ssh://root@myserver,你也可以使用 scp 式的寫法:

$ git clone root@myserver:/git/repo/test.git
複製程式碼

另外一點不同的地方是,每次 pullpush 的時候都需要輸入遠端伺服器的 root 密碼。很顯然,讓每個 Git 使用者都使用 root 來訪問伺服器是一種很不安全的做法,有幾種方法可以解決這個問題:

  • 最顯而易見的方法是為每個 Git 使用者建立一個獨立的賬號,並分別為他們分配對倉庫的讀寫許可權,這種方法行的通,但是對賬號的管理非常麻煩,在團隊人員不是很多的時候可以嘗試,但是並不推薦;
  • 另一種方法是配置 SSH 伺服器使用某個已有的認證系統來管理使用者,比如 LDAP,這在很多企業中是很常見的,這樣可以省去用 adduser 手工管理伺服器賬號的麻煩;
  • 還有一種方法是隻建立一個賬號,比如叫做 git,他對倉庫具有讀寫許可權,大家都使用這個賬號來訪問倉庫。這種方法的好處是使用者管理起來比較簡單,而且可以使用後面介紹的 authorized_keys 檔案對使用者的公鑰進行管理;

下面我們嘗試下第三種方法。首先在伺服器上建立一個名叫 git 的賬號:

root@myserver:~# adduser git
Adding user `git' ...
Adding new group `git' (1000) ...
Adding new user `git' (1000) with group `git' ...
Creating home directory `/home/git' ...
Copying files from `/etc/skel' ...
Enter new UNIX password: 
Retype new UNIX password: 
passwd: password updated successfully
Changing the user information for git
Enter the new value, or press ENTER for the default
	Full Name []: git
	Room Number []:   
	Work Phone []: 
	Home Phone []: 
	Other []: 
Is the information correct? [Y/n] Y
複製程式碼

再設定一下 git 倉庫的許可權(預設情況下,git 倉庫的許可權為 rwxr-xr-x,只有建立者 root 有寫的許可權,也就意味著使用 git 賬號只能 clone pull,不能 push):

# chmod a+w -R /git/repo/test.git
複製程式碼

我們這裡非常粗暴的使用 chmod a+w 將 git 倉庫設定為對所有人可寫,這裡可以想一想,如果我們希望設定某些使用者對倉庫具有隻讀的許可權,該怎麼做呢?

然後就可以在本地愉快的進行 git 操作了:

$ git clone git@myserver:/git/repo/test.git
複製程式碼

到這裡似乎一切都很正常,但是幾次實操之後你就會發現,每次 git 操作都要輸入一次密碼,這也太麻煩了,能不能“免密提交程式碼”呢?首先我們要知道,只要能通過 SSH 登陸到伺服器,我們就能操作 git,所以如果 SSH 能支援免密登陸,我們就可以“免密提交程式碼”。還好,SSH 支援公鑰認證,這種認證方式無需密碼登陸。在 Linux 作業系統中,每個使用者都可以擁有自己的一個或多個金鑰對(公鑰和私鑰成對出現),這些金鑰一般情況會儲存在 ~/.ssh 目錄下,在開始之前,我們先確認下自己是否已經生成過公鑰了,可以看下這個目錄下是否有 id_dsa.pubid_rsa.pub 這樣的檔案,如果沒有,我們通過 ssh-keygen 來生成:

aneasystone@little-stone:~/.ssh$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/aneasystone/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/aneasystone/.ssh/id_rsa.
Your public key has been saved in /home/aneasystone/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:4Ulpufuhs/AgDMb0VXnqMUTw6bD/HrAOI2z9c1cod9I aneasystone@little-stone
The key's randomart image is:
+---[RSA 2048]----+
|      .oo.       |
|       oo+.      |
|  .   o.Oo       |
| o . . B++       |
|  + . ..So   o   |
| . + . ..+. + E  |
|    * * + oo +   |
|   . o Oo+.o.    |
|        **+.     |
+----[SHA256]-----+
複製程式碼

這樣我們在 ~/.ssh 目錄生成了兩個檔案,id_rsa 是你的私鑰,id_rsa.pub 是你的公鑰。關於私鑰和公鑰的原理以及 RSA 加密演算法等內容可以參考我之前寫過的一篇介紹 HTTPS 和證照 的文章。

我們假設你的 Git 伺服器是由專門的伺服器管理員負責維護和管理,當你生成你的公鑰之後,就可以給伺服器管理員傳送一封申請 Git 服務的郵件,並附上你的公鑰。伺服器管理員在收到你的申請之後,如果同意了,就可以進行下面的操作:

首先將公鑰檔案拷貝到伺服器上:

# scp id_rsa.pub root@myserver:/home/git
複製程式碼

將公鑰檔案的內容追加到 git 賬戶的 authorized_keys 檔案中(要注意的是,如果是第一次操作,/home/git 目錄下是沒有 .ssh 目錄的,需要手工建立 .ssh 目錄和 authorized_keys 檔案):

root@myserver:/home/git# cat id_rsa.pub >> /home/git/.ssh/authorized_keys
複製程式碼

後續如果有其他的使用者申請 Git 服務,都可以按照這個步驟操作。一旦完成這個操作,伺服器管理員將會回覆你的郵件,通知你 Git 服務已經開通,這個時候你再進行 git 操作就可以不用輸入密碼了。關於 SSH 的使用,更詳細的步驟可以參考 Github 上的這篇指南:Connecting to GitHub with SSH

作為伺服器管理員,關於 SSH 還有一點需要考慮,那就是 SSH 的安全問題。在上面介紹本地協議時,我們說這種方式無法控制使用者對 Git 倉庫的操作,無法防止使用者有意或無意的損壞 Git 倉庫,使用 SSH 協議一樣存在這樣的問題,使用者能通過 SSH 拉取和提交程式碼,也就意味著使用者可以通過 SSH 連線到伺服器,對 Git 倉庫進行任何操作,這是一件很讓人擔心的事情。

因此,我們還需要對 git 賬號做一些限制。預設情況下,我們新建賬號的登陸 shell 是 /bin/bash,這個配置在 /etc/passwd 檔案中:

git:x:1000:1000:git,,,:/home/git:/bin/bash
複製程式碼

可以使用 chsh 命令修改使用者的登陸 shell,讓他不能通過 SSH 訪問伺服器,怎麼修改呢?我們可以看一下 /etc/shells 檔案,這裡定義了所有可以使用的登陸 shell,你可以將 /bin/bash 改成這裡的任何一個:

root@myserver:~# cat /etc/shells 
# /etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash
複製程式碼

很顯然,這些 shell 並不是我們想要的,有沒有一個 shell 只允許使用者進行 git 操作,而不允許其他操作呢?還好,Git 的軟體包提供了一個名叫 git-shell 的登陸 shell,我們可以把他加進去,一般情況下位於 /usr/bin/git-shell。我們使用 chsh 修改 git 的登陸 shell:

root@myserver:~# chsh git
Changing the login shell for git
Enter the new value, or press ENTER for the default
	Login Shell [/bin/bash]: /usr/bin/git-shell
複製程式碼

這樣當使用者 git 通過 SSH 連線伺服器時,就會直接被拒絕了。

三、Git 協議

SSH 協議解決了使用者直接操作 Git 倉庫的許可權問題,但是如果我們希望對除倉庫維護者之外的所有人都開放 Git 倉庫的只讀許可權,這在開源專案中和企業內部往往是很常見的,任何人都可以去檢視倉庫的程式碼,這時管理員需要給每一個使用者配置 SSH 金鑰是非常麻煩的。雖然也可以使用變通的方法來達到這個效果,但是很繁瑣,下面是具體的步驟:

  • 使用 g+w 設定 Git 倉庫的許可權,讓倉庫建立者所在的使用者組具有寫許可權,而不是所有人都有寫許可權(這一步通常也可以在 git init 的時候加上 --shared 引數);
  • 然後將 git 賬號加到倉庫建立者的使用者組;
  • 再建立一個 git_ro 賬戶,這個賬戶對倉庫只有只讀許可權;
  • 最後為 git_ro 賬戶建立一個金鑰對,把 git_ro 的私鑰公開出來供所有人使用。

可以看到使用 SSH 協議最終都逃不過授權這一步,而且公開私鑰的做法也不是很優雅。實際上,Git 提供了另一種方式來讓這個操作更簡單,那就是 Git 協議。使用 Git 協議必須要在伺服器上執行 Git 守護程式,git 命令自帶了一個 daemon 引數:

root@myserver:~# git daemon --reuseaddr --base-path=/git/repo/ /git/repo/
複製程式碼

上面的各個引數可以 參考 git-daemon 的文件。git-daemon 會監聽 9418 埠,如果你的伺服器有防火牆,需要將該埠新增到白名單,如果你使用的是阿里雲伺服器,需要像下面這樣新增一個安全組規則:

security-group.jpg

為了讓所有的使用者都可以訪問我們的倉庫,還需要在倉庫目錄下建立一個名為 git-daemon-export-ok 的檔案:

root@myserver:~# cd /git/repo/test.git/
root@myserver:/git/repo/test.git/# touch git-daemon-export-ok
複製程式碼

至此,所有人都可以通過 Git 協議來克隆或拉取專案原始碼了(注意上面指定了 base-path 引數為 /git/repo/,所以 URL 可以直接寫 git://myserver/test.git):

aneasystone@little-stone:~/working$ git clone git://myserver/test.git
複製程式碼

一般情況下,伺服器管理員還會做一些其他的配置,譬如在伺服器重啟時讓 Git 守護程式自動啟動,這有很多種方式可以實現,可以參考《Pro Git》 Git 守護程式 這一節的內容。

四、HTTP(S) 協議

我們一般通過 Git 協議進行無授權訪問,通過 SSH 協議進行授權訪問,如果你的專案是內部專案,只針對部分授權使用者,那使用 SSH 協議就足夠了,但是如果既需要授權訪問也需要無授權訪問,可能需要 SSH 協議和 Git 協議搭配使用,這在維護上成本很高。這時就到了我們的壓軸戲 —— HTTP 協議出場的時候了,它同時支援上面兩種訪問方式。

通過 HTTP 協議訪問 Git 服務是目前使用最廣泛的方式,它支援兩種模式:舊版本的 Dumb HTTP 和 新版本的 Smart HTTP,Dumb HTTP 一般很少使用,下面除非特殊說明,所說的 HTTP 協議都是 Smart HTTP。使用 HTTP 協議的好處是可以使用各種 HTTP 認證機制,比如使用者名稱/密碼,這比配置 SSH 金鑰要簡單的多,對普通使用者來說也更能接受。如果擔心資料傳輸安全,也可以配置 HTTPS 協議,這和普通的 Web 服務是一樣的。

下面我們就來嘗試搭建一個基於 HTTP 協議的 Git 伺服器。《Pro Git》上提供了一個基於 Apache 的配置示例,如果你是使用 Apache 作為 Web 伺服器,可以參考之,我們這裡使用 Nginx 來作為 Web 伺服器,其原理本質上是一樣的,都是通過 Web 伺服器接受 HTTP 請求,並將請求轉發到 Git 自帶的一個名為 git-http-backend 的 CGI 指令碼

首先我們安裝所需的軟體:

# apt-get install -y git-core nginx fcgiwrap apache2-utils
複製程式碼

其中,Nginx 作為 Web 伺服器,本身是不能執行外部 CGI 指令碼的,需要通過 fcgiwrap 來中轉,就像使用 php-fpm 來執行 PHP 指令碼一樣。apache2-utils 是 Apache 提供的一個 Web 伺服器的工具集,包含了一些有用的小工具,譬如下面我們會用到的 htpasswd 可以生成 Basic 認證檔案。

啟動 nginx 和 fcgiwrap,並訪問 http://myserver 測試 Web 伺服器是否能正常訪問:

# service nginx start
# service fcgiwrap start
複製程式碼

然後我們開啟並編輯 Nginx 的配置檔案(/etc/nginx/sites-available/default):

location / {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
        fastcgi_param GIT_HTTP_EXPORT_ALL "";
        fastcgi_param GIT_PROJECT_ROOT /git/repo;
        fastcgi_param PATH_INFO $uri;
        fastcgi_param REMOTE_USER $remote_user;
        fastcgi_pass unix:/var/run/fcgiwrap.socket;
}
複製程式碼

這裡通過 fastcgi_param 設定了一堆的 FastCGI 引數,如下:

  • SCRIPT_FILENAME:指定 CGI 指令碼 git-http-backend 的位置,表示每次 HTTP 請求會被轉發到該 CGI 指令碼;
  • GIT_HTTP_EXPORT_ALL:git-http-backend 預設只能訪問目錄下有 git-daemon-export-ok 檔案的 Git 倉庫,和上面介紹的 Git 協議是一樣的,如果指定了 GIT_HTTP_EXPORT_ALL,表示允許訪問所有倉庫;
  • GIT_PROJECT_ROOT:Git 倉庫的根目錄;
  • REMOTE_USER:如果有認證,將認證的使用者資訊傳到 CGI 指令碼;

改完之後我們重啟 Nginx,並通過 HTTP 協議 clone 倉庫:

aneasystone@little-stone:~/working$ git clone http://myserver/test.git
複製程式碼

4.1 開啟身份認證

到這裡一切 OK,但是當我們 push 程式碼的時候,卻會報下面的 403 錯誤:

aneasystone@little-stone:~/working/test$ git push origin master
fatal: unable to access 'http://myserver/test.git/': The requested URL returned error: 403
複製程式碼

為了解決這個錯誤,我們可以在 git-http-backend 的官網文件 上找到這樣的一段描述:

By default, only the upload-pack service is enabled, which serves git fetch-pack and git ls-remote clients, which are invoked from git fetch, git pull, and git clone. If the client is authenticated, the receive-pack service is enabled, which serves git send-pack clients, which is invoked from git push.

第一次讀這段話可能會有些不知所云,這是因為我們對這裡提到的 upload-packfetch-packreceive-packsend-pack 這幾個概念還沒有什麼認識。但是我們大抵可以猜出來,預設情況下,只有認證的使用者才可以 push 程式碼,如果某個 Git 倉庫希望所有使用者都有許可權 push 程式碼,可以為相應的倉庫設定 http.receivepack

root@myserver:/# cd /git/repo/test.git/
root@myserver:/git/repo/test.git# git config http.receivepack true
複製程式碼

當然最好的做法還是對 push 操作開啟認證,官網文件上有一個 lighttpd 的配置 我們可以借鑑:

$HTTP["querystring"] =~ "service=git-receive-pack" {
	include "git-auth.conf"
}
$HTTP["url"] =~ "^/git/.*/git-receive-pack$" {
	include "git-auth.conf"
}
複製程式碼

這個配置看上去非常簡單,但是想要理解為什麼這樣配置,就必須去了解下 Git 的內部原理。正如上面 git-http-backend 文件上的那段描述,當 Git 客戶端執行 git fetch, git pull, and git clone 時,會呼叫 upload-pack 服務,當執行 git push 時,會呼叫 receive-pack 服務,為了更清楚的說明這一點,我們來看看 Nginx 的訪問日誌。

執行 git clone

[27/Nov/2018:22:18:00] "GET /test.git/info/refs?service=git-upload-pack HTTP/1.1" 200 363 "-" "git/1.9.1"
[27/Nov/2018:22:18:00] "POST /test.git/git-upload-pack HTTP/1.1" 200 306 "-" "git/1.9.1"
複製程式碼

執行 git pull

[27/Nov/2018:22:20:25] "GET /test.git/info/refs?service=git-upload-pack HTTP/1.1" 200 363 "-" "git/1.9.1"
[27/Nov/2018:22:20:25] "POST /test.git/git-upload-pack HTTP/1.1" 200 551 "-" "git/1.9.1"
複製程式碼

執行 git push

[27/Nov/2018:22:19:33] "GET /test.git/info/refs?service=git-receive-pack HTTP/1.1" 401 204 "-" "git/1.9.1"
admin [27/Nov/2018:22:19:33] "GET /test.git/info/refs?service=git-receive-pack HTTP/1.1" 200 193 "-" "git/1.9.1"
admin [27/Nov/2018:22:19:33] "POST /test.git/git-receive-pack HTTP/1.1" 200 63 "-" "git/1.9.1"
複製程式碼

可以看到執行 clone 和 pull 請求的介面是一樣的,先請求 /info/refs?service=git-upload-pack,然後再請求 /git-upload-pack;而 push 是先請求 /info/refs?service=git-receive-pack,然後再請求 /git-receive-pack,所以在上面的 lighttpd 的配置中我們看到了兩條記錄,如果要對 push 做訪問控制,那麼對這兩個請求都要限制。關於 Git 傳輸的原理可以參考 《Pro Git》的 Git 內部原理 - 傳輸協議 這一節。

我們依葫蘆畫瓢,Nginx 配置檔案如下:

location @auth {
        auth_basic "Git Server";
        auth_basic_user_file /etc/nginx/passwd;

        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
        fastcgi_param GIT_HTTP_EXPORT_ALL "";
        fastcgi_param GIT_PROJECT_ROOT /git/repo;
        fastcgi_param PATH_INFO $uri;
        fastcgi_param REMOTE_USER $remote_user;
        fastcgi_pass unix:/var/run/fcgiwrap.socket;
}

location / {
        error_page 418 = @auth;
        if ( $query_string = "service=git-receive-pack" ) {  return 418; }
        if ( $uri ~ "git-receive-pack$" ) { return 418; }

        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
        fastcgi_param GIT_HTTP_EXPORT_ALL "";
        fastcgi_param GIT_PROJECT_ROOT /git/repo;
        fastcgi_param PATH_INFO $uri;
        fastcgi_param REMOTE_USER $remote_user;
        fastcgi_pass unix:/var/run/fcgiwrap.socket;
}
複製程式碼

其中相同的配置我們也可以用 include 指令放在一個共用的配置檔案裡,這樣我們就實現了在 push 的時候需要填寫使用者名稱和密碼了。我們通過 Nginx 的 auth_basic_user_file 指令來做身份認證,使用者名稱和密碼儲存在 /etc/nginx/passwd 檔案中,這個檔案可以使用上面提到的 apache2-utils 包裡的 htpasswd 來生成:

root@myserver:/# htpasswd -cb /etc/nginx/passwd admin 123456
複製程式碼

另外,在 push 的時候,有時候可能會遇到 unpack failed: unable to create temporary object directory 這樣的提示錯誤:

aneasystone@little-stone:~/working/test$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 193 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
error: unpack failed: unable to create temporary object directory
To http://myserver/test.git
 ! [remote rejected] master -> master (unpacker error)
error: failed to push some refs to 'http://myserver/test.git'
複製程式碼

這一般情況下都是由於 Git 倉庫目錄的許可權問題導致的,在這裡 Git 倉庫的根目錄 /git/repo 是 root 建立的,而執行 nginx 和 fcgiwrap 的使用者都是 www-data,我們可以把 Git 倉庫目錄設定成對所有人可讀可寫,也可以像下面這樣將它的擁有者設定成 www-data 使用者:

root@myserver:/# chown -R www-data:www-data /git/repo
複製程式碼

4.2 憑證管理

上面我們站在管理員的角度解決了使用者身份認證的問題,但是站在使用者的角度,每次提交程式碼都要輸入使用者名稱和密碼是一件很痛苦的事情。在上面介紹 SSH 協議時,我們可以使用 SSH 協議自帶的公鑰認證機制來省去輸入密碼的麻煩,那麼在 HTTP 協議中是否存在類似的方法呢?答案是肯定的,那就是 Git 的憑證儲存工具:credential.helper

譬如像下面這樣,將使用者名稱和密碼資訊儲存在快取中:

$ git config --global credential.helper cache
複製程式碼

這種方式預設只保留 15 分鐘,如果要改變保留的時間,可以通過 --timeout 引數設定,或者像下面這樣,將密碼儲存在檔案中:

$ git config --global credential.helper store
複製程式碼

這種方式雖然可以保證密碼不過期,但是要記住的是,這種方式密碼是以明文的方式儲存在你的 home 目錄下的。可以借鑑作業系統自帶的憑證管理工具來解決這個問題, 比如 OSX Keychain 或者 Git Credential Manager for Windows。更多的內容可以參考《Pro Git》憑證儲存 這一節。

除此之外,還有一種更簡單粗暴的方式:

aneasystone@little-stone:~/working$ git clone http://admin:123456@myserver/test.git
複製程式碼

五、綜合對比

這一節對 Git 的四大協議做一個綜合對比。

  • 本地協議
    • 優點:架設簡單,不依賴外部服務,直接使用現有檔案和網路許可權,常用於共享檔案系統
    • 缺點:共享檔案系統的配置和使用不方便,且無法保護倉庫被意外損壞,傳輸效能較低
  • SSH 協議
    • 優點:架設簡單,所有資料經過授權加密,資料傳輸很安全,傳輸效能很高
    • 缺點:不支援匿名訪問,配置 SSH 的金鑰對小白使用者有一定的門檻
  • Git 協議
    • 優點:對開放的專案很適用,無需授權,傳輸效能最高
    • 缺點:缺乏授權機制,架設較麻煩,企業一般不會預設開放 9418 埠需要另行新增
  • HTTP/S 協議
    • 優點:同時支援授權訪問和無授權訪問,傳輸效能較高,配合 HTTPS 也可以實現資料安全
    • 缺點:架設 HTTP 服務較麻煩,認證憑證不好管理

六、更高階的工具

上面介紹的是搭建 Git 伺服器最基本的方法,如果你只是希望能找一個版本控制系統來替代現有的 SVN,這也許就足夠了。但如果你希望你的版本控制系統能擁有一個更友好的 UI 介面,能更好的管理你的使用者和許可權,能支援更現代的 Pull Request 功能以及能和 CI/CD 系統更緊密的聯絡起來,你就需要一個更高階的工具,你可以試試 GitWebGitoliteGitlabGogsGitea,當然,如果你願意,你也可以把程式碼放在那些流行的程式碼託管平臺上,比如 GithubBitbucket 等等。

參考

  1. Version Control Systems Popularity in 2016
  2. Pro Git 第二版
  3. git-http-backend 的官網文件
  4. Connecting to GitHub with SSH
  5. nginx fastcgi 配置
  6. Git遠端推送時記住使用者名稱和密碼
  7. 搭建Git伺服器 - 廖雪峰的官方網站
  8. 在 Ubuntu 系統上配置 Nginx Git 伺服器- 張志敏的技術專欄
  9. Git 伺服器基於nginx配置http(s)協議 | Yvanの平行時空

相關文章