簡介
Git 可以使用四種主要的協議來傳輸資料:
-
本地協議(Local):
最基本的就是 本地協議(Local protocol), 其中的遠程版本庫就是硬盤內的另一個目錄。 這常見於團隊每一個成員都對一個共享的文件系統(例如一個掛載的 NFS)擁有訪問權,或者比較少見的多人共用同一台電腦的情況。 后者並不理想,因為你的所有代碼版本庫如果長存於同一台電腦,更可能發生災難性的損失。
如果你使用共享文件系統,就可以從本地版本庫克隆(clone)、推送(push)以及拉取(pull)。 像這樣去克隆一個版本庫或者增加一個遠程到現有的項目中,使用版本庫路徑作為 URL。 例如,克隆一個本地版本庫,可以執行如下的命令:git clone /opt/git/project.git 或 git clone file:///opt/git/project.git
如果在 URL 開頭明確的指定 file://,那么 Git 的行為會略有不同。 如果僅是指定路徑,Git 會嘗試使用硬鏈接(hard link)或直接復制所需要的文件。
如果指定 file://,Git 會觸發平時用於網路傳輸資料的進程,那通常是傳輸效率較低的方法。 指定 file:// 的主要目的是取得一個沒有外部參考(extraneous references)或對象(object)的干凈版本庫副本– 通常是在從其他版本控制系統導入后或一些類似情況(參見 Git 內部原理 for maintenance tasks)需要這么做。 在此我們將使用普通路徑,因為這樣通常更快。
要增加一個本地版本庫到現有的 Git 項目,可以執行如下的命令:git remote add local_proj /opt/git/project.git
然后,就可以像在網絡上一樣從遠端版本庫推送和拉取更新了。
優點: 基於文件系統的版本庫的優點是簡單,並且直接使用了現有的文件權限和網絡訪問權限。 如果你的團隊已經有共享文件系統,建立版本庫會十分容易。 只需要像設置其他共享目錄一樣,把一個裸版本庫的副本放到大家都可以訪問的路徑,並設置好讀/寫的權限,就可以了, 我們會在 在服務器上搭建 Git 討論如何導出一個裸版本庫。
這也是快速從別人的工作目錄中拉取更新的方法。 如果你和別人一起合作一個項目,他想讓你從版本庫中拉取更新時,運行類似 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 協議。
智能(Smart) HTTP 協議: “智能” HTTP 協議的運行方式和 SSH 及 Git 協議類似,只是運行在標准的 HTTP/S 端口上並且可以使用各種 HTTP 驗證機制,這意味着使用起來會比 SSH 協議簡單的多,比如可以使用 HTTP 協議的用戶名/密碼的基礎授權,免去設置 SSH 公鑰。
智能 HTTP 協議或許已經是最流行的使用 Git 的方式了,它即支持像 git:// 協議一樣設置匿名服務,也可以像 SSH 協議一樣提供傳輸時的授權和加密。 而且只用一個 URL 就可以都做到,省去了為不同的需求設置不同的 URL。 如果你要推送到一個需要授權的服務器上(一般來講都需要),服務器會提示你輸入用戶名和密碼。 從服務器獲取數據時也一樣。
事實上,類似 GitHub 的服務,你在網頁上看到的 URL (比如,https://github.com/schacon/simplegit[]),和你在克隆、推送(如果你有權限)時使用的是一樣的。
啞(Dumb) HTTP 協議: 如果服務器沒有提供智能 HTTP 協議的服務,Git 客戶端會嘗試使用更簡單的“啞” HTTP 協議。 啞 HTTP 協議里 web 服務器僅把裸版本庫當作普通文件來對待,提供文件服務。 啞 HTTP 協議的優美之處在於設置起來簡單。 基本上,只需要把一個裸版本庫放在 HTTP 根目錄,設置一個叫做 post-update 的掛鈎就可以了(見 Git 鈎子)。 此時,只要能訪問 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 的數據是以基本的靜態文件形式提供的(詳情見 Git 內部原理)。
通常的,會在可以提供讀/寫的智能 HTTP 服務和簡單的只讀的啞 HTTP 服務之間選一個。 極少會將二者混合提供服務。
優點: 我們將只關注智能 HTTP 協議的優點。不同的訪問方式只需要一個 URL 以及服務器只在需要授權時提示輸入授權信息,這兩個簡便性讓終端用戶使用 Git 變得非常簡單。 相比 SSH 協議,可以使用用戶名/密碼授權是一個很大的優勢,這樣用戶就不必須在使用 Git 之前先在本地生成 SSH 密鑰對再把公鑰上傳到服務器。 對非資深的使用者,或者系統上缺少 SSH 相關程序的使用者,HTTP 協議的可用性是主要的優勢。 與 SSH 協議類似,HTTP 協議也非常快和高效。
你也可以在 HTTPS 協議上提供只讀版本庫的服務,如此你在傳輸數據的時候就可以加密數據;或者,你甚至可以讓客戶端使用指定的 SSL 證書。
另一個好處是 HTTP/S 協議被廣泛使用,一般的企業防火牆都會允許這些端口的數據通過。
缺點: 在一些服務器上,架設 HTTP/S 協議的服務端會比 SSH 協議的棘手一些。 除了這一點,用其他協議提供 Git 服務與 “智能” HTTP 協議相比就幾乎沒有優勢了。
如果你在 HTTP 上使用需授權的推送,管理憑證會比使用 SSH 密鑰認證麻煩一些。 然而,你可以選擇使用憑證存儲工具,比如 OSX 的 Keychain 或者 Windows 的憑證管理器。 參考 憑證存儲 如何安全地保存 HTTP 密碼。 -
SSH(Secure Shell)協議
架設 Git 服務器時常用 SSH 協議作為傳輸協議。 因為大多數環境下已經支持通過 SSH 訪問 —— 即時沒有也比較很容易架設。 SSH 協議也是一個驗證授權的網絡協議;並且,因為其普遍性,架設和使用都很容易。
通過 SSH 協議克隆版本庫,你可以指定一個 ssh:// 的 URL:git clone ssh://user@server/project.git
或者使用一個簡短的 scp 式的寫法:
git clone user@server:project.git
你也可以不指定用戶,Git 會使用當前登錄的用戶名。
優勢: 用 SSH 協議的優勢有很多。 首先,SSH 架設相對簡單 —— SSH 守護進程很常見,多數管理員都有使用經驗,並且多數操作系統都包含了它及相關的管理工具。 其次,通過 SSH 訪問是安全的 —— 所有傳輸數據都要經過授權和加密。 最后,與 HTTP/S 協議、Git 協議及本地協議一樣,SSH 協議很高效,在傳輸前也會盡量壓縮數據。缺點: 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 或者其他的程序,這些工作並不簡單。 它還要求防火牆開放 9418 端口,但是企業防火牆一般不會開放這個非標准端口。 而大型的企業防火牆通常會封鎖這個端口。
這里使用的是ssh協議,采用的是的gitcore搭建的,如果想搭建一個高端一點的(類似github,有web界面的)可以點擊參考,里面由關於gitweb和gitlab的介紹。
環境
服務端: ubuntu 14.04 、 gitcore 1.9.1
客戶端: ubuntu 16.04 、 git 2.7.4
搭建過程
服務端
-
先創建一個賬戶用於操作git倉庫
sudo adduser git #用戶名隨意叫,普通用戶即可
-
安裝git core
sudo apt-get update && sudo apt-get upgrade && sudo apt-get install git-core
-
創建測試git倉庫
su git #切換到git用戶,這樣接下來的創建的倉庫保證都是具有讀寫權限的 cd ~ && mkdir -p ~/github/test.git cd ~/github/test.git && git init --shared --bare #--bare 創建一個空倉庫 --shared 用於配置倉庫的訪問權限 ,我們這里采用默認配置,同屬於git group的人可以訪問。
此時會顯示:
Initialized empty Git repository in /home/git/github/test.git
客戶端
-
生成用於ssh登錄的證書:
ssh-keygen -t rsa #產生證書過程中會有兩步提示,第一步讓你選擇文件保存位置,這一步直接回車;第二步讓你輸入私鑰保護密碼,建議設置密碼為了安全。
這時在home目錄下會產生一個.ssh的隱藏文件夾,里面會產生一個 id_rsa 和 id_rsa.pub的公私鑰對.
-
上傳證書到服務器
cat ~/.ssh/id_rsa.pub | ssh git@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys" #server是你服務器的域名或者ip地址
-
登錄服務器(optional)
ssh git@server #如果在創建私鑰的時候輸入了保護密碼,此時會向你索取保護密碼
-
測試上傳代碼
如果本地沒有已經存在的代碼:git clone git@server:~/git/github/test.git cd test touch README.md git add README.md git commit -a #提交代碼的時候會要求你寫入日志,如果是采用的nano編輯器,寫入結束后按ctrl + x 保存退出 git push -u origin master
如果你本地已經有寫好的代碼了則執行以下步驟:
cd existing_dir git init git remote add origin git@server:~/git/github/test.git git add . git commit -a git push -u origin master
注意: 當倉庫為空時push操作無法成功,會報出error: src refspec master does not match any 錯誤。
添加用戶
第一種
-
創建一個新的公私密鑰對,然后將公鑰添加到/home/git/.ssh/authorized_keys文件的末尾
cat ~/.ssh/new.pub | ssh git@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys" #server是你服務器的域名或者ip地址
-
然后將公私密鑰對分發給新用戶,放在新用戶本地的~/.ssh 文件夾下面即可。
注: 已驗證
第二種
- 在服務端創建一個git group的用戶,然后為其生成公私密鑰分發給新用戶用於ssh通信
注:待驗證
git shell 安全 (Optional)
上面創建的git是一個普通用戶,但是仍然可以登錄到服務器中,且對git倉庫具備操作權限,存在潛在風險,為此我們要限制它的能力。這可以借助一個名為 git-shell 的受限 shell 工具,你可以方便地將用戶 git 的活動限制在與 Git 相關的范圍內。該工具隨 Git 軟件包一同提供。
如果將 git-shell 設置為用戶 git 的登錄 shell(login shell),那么用戶 git 便不能獲得此服務器的普通 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 vim /etc/shells # and add the path to git-shell from last command
現在你可以使用 chsh
sudo chsh git # and enter the path to git-shell, usually: /usr/bin/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.
現在,網絡相關的 Git 命令依然能夠正常工作,但是開發者用戶已經無法得到一個普通 shell 了。 正如輸出信息所提示的,你也可以在 git 用戶的家目錄下建立一個目錄,來對 git-shell 命令進行一定程度的自定義。 比如,你可以限制掉某些本應被服務器接受的 Git 命令,或者對剛才的 SSH 拒絕登錄信息進行自定義,這樣,當有開發者用戶以類似方式嘗試登錄時,便會看到你的信息。 要了解更多有關自定義 shell 的信息,請運行 git help shell。
Git守護進程(Optional)
接下來我們將通過 “Git” 協議建立一個基於守護進程的倉庫。 對於快速且無需授權的 Git 數據訪問,這是一個理想之選。 請注意,因為其不包含授權服務,任何通過該協議管理的內容將在其網絡上公開。
如果運行在防火牆之外的服務器上,它應該只對那些公開的只讀項目服務。 如果運行在防火牆之內的服務器上,它可用於支撐大量參與人員或自動系統(用於持續集成或編譯的主機)只讀訪問的項目,這樣可以省去逐一配置 SSH 公鑰的麻煩。
無論何時,該 Git 協議都是相對容易設定的。 通常,你只需要以守護進程的形式運行該命令:
git daemon --reuseaddr --base-path=/opt/git/ /opt/git/
--reuseaddr 允許服務器在無需等待舊連接超時的情況下重啟,--base-path 選項允許用戶在未完全指定路徑的條件下克隆項目,結尾的路徑將告訴 Git 守護進程從何處尋找倉庫來導出。 如果有防火牆正在運行,你需要開放端口 9418 的通信權限。
你可以通過許多方式將該進程以守護進程的方式運行,這主要取決於你所使用的操作系統。 在一台 Ubuntu 機器上,你可以使用一份 Upstart 腳本。 因此,找到如下文件:
/etc/event.d/local-git-daemon
並添加下列腳本內容:
start on startup
stop on shutdown
exec /usr/bin/git daemon \
--user=git --group=git \
--reuseaddr \
--base-path=/opt/git/ \
/opt/git/
respawn
出於安全考慮,強烈建議使用一個對倉庫擁有只讀權限的用戶身份來運行該守護進程 - 你可以創建一個新用戶 git-ro 並且以該用戶身份來運行守護進程。 為簡便起見,我們將像 git-shell 一樣,同樣使用 git 用戶來運行它。
當你重啟機器時,你的 Git 守護進程將會自動啟動,並且如果進程被意外結束它會自動重新運行。 為了在不重啟的情況下直接運行,你可以運行以下命令:
initctl start local-git-daemon
在其他系統中,你可以使用 sysvinit 系統中的 xinetd 腳本,或者另外的方式來實現 - 只要你能夠將其命令守護進程化並實現監控。
接下來,你需要告訴 Git 哪些倉庫允許基於服務器的無授權訪問。 你可以在每個倉庫下創建一個名為 git-daemon-export-ok 的文件來實現。
cd /path/to/project.git
touch git-daemon-export-ok
該文件將允許 Git 提供無需授權的項目訪問服務。