原文地址:
http://debugtalk.com/post/head-first-git-authority-verification/
藉助上次“掉坑”的經歷,我對Git權限校驗的兩種方式重頭進行了梳理,形成了這篇總結記錄。
在本地計算機與GitHub(或GitLab)進行通信時,傳輸主要基於兩種協議,HTTPS
和SSH
,對應的倉庫地址就是HTTPS URLs
和SSH URLs
。
首先需要強調的是,HTTPS URLs
和SSH URLs
對應的是兩套完全獨立的權限校驗方式,主要的區別就是HTTPS URLs
採用賬號密碼進行校驗,SSH URLs
採用SSH
祕鑰對進行校驗。平時使用的時候我們可以根據實際情況,選擇一種即可。
HTTPS URLs
GitHub官方推薦採用HTTPS URLs
的方式,因爲該種方式適用面更廣(即使在有防火牆或代理的情況下也同樣適用),使用更方便(配置更簡單)。
採用HTTPS URLs
地址clone
/fetch
/pull
/push
倉庫時,事先無需對本地系統進行任何配置,只需要輸入GitHub的賬號和密碼即可。不過如果每次都要手動輸入賬號密碼,也是一件很繁瑣的事情。
好在已經有多個機制可以讓操作不用這麼麻煩。
在Mac系統中,在啓用Keychain
機制的情況下,首次輸入GitHub賬號密碼後,認證信息就會自動保存到系統的Keychain
中,下次再次訪問倉庫時就會自動讀取Keychain
中保存的認證信息。
在非Mac系統中,雖然沒有Keychain
機制,但是Git提供了credential helper
機制,可以將賬號密碼以cache的形式在內存中緩存一段時間(默認15分鐘),或者以文件的形式存儲起來(~/.git-credentials
)。當然,Mac系統如果不啓用Keychain
機制,也可以採用這種方式。
1 2 3 4
|
# cache credential in memory $ git config --global credential.helper cache # store credential in ~/.git-credential $ git config --global credential.helper store
|
在credential.helper
設置爲store
的情況下,首次輸入GitHub賬號密碼後,就會自動保存到~/.git-credentials
文件中,保存形式爲https://user:[email protected]
;下次再次訪問倉庫時就會自動讀取~/.git-credentials
中保存的認證信息。
另一個需要說明的情況是,如果在GitHub中開啓了2FA(two-factor authentication)
,那麼在本地系統中輸入GitHub賬號密碼時,不能輸入原始的密碼(即GitHub網站的登錄密碼),而是需要事先在GitHub網站中創建一個Personal access token
,後續在訪問代碼倉庫需要進行權限校驗的時候,採用access token
作爲密碼進行輸入。
SSH URLs
除了HTTPS URLs
,還可以採用SSH URLs
的方式訪問GitHub代碼倉庫。
採用SSH URLs
方式之前,需要先在本地計算機中生成SSH keypair
(祕鑰對,包括私鑰和公鑰)。默認情況下,生成的祕鑰位於$HOME/.ssh/
目錄中,文件名稱分別爲id_rsa
和id_rsa.pub
,通常無需修改,保持默認即可。不過,如果一臺計算機中存在多個祕鑰對,就需要修改祕鑰文件名,名稱沒有強制的命名規範,便於自己辨識即可。
如下是創建祕鑰對的過程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
➜ ssh-keygen -t rsa -b 4096 -C "[email protected]" Generating public/private rsa key pair. Enter file in which to save the key (/Users/Leo/.ssh/id_rsa): /Users/Leo/.ssh/debugtalk_id_rsa Enter passphrase (empty for no passphrase): <myPassphrase> Enter same passphrase again: <myPassphrase> Your identification has been saved in /Users/Leo/.ssh/debugtalk_id_rsa. Your public key has been saved in /Users/Leo/.ssh/debugtalk_id_rsa.pub. The key fingerprint is: SHA256:jCyEEKjlCU1klROnuBg+UH08GJ1u252rQMADdD9kYMo [email protected] The key's randomart image is: +---[RSA 4096]----+ |+*BoBO+. | |o=oO=** | |++E.*+o. | |+ooo +o+ | |.o. ..+oS. . | | . o. . o | | . . | | . . | | .. | +----[SHA256]-----+
|
在創建祕鑰的過程中,系統還建議創建一個名爲passphrase
的東西,這是用來幹嘛的呢?
首先,單獨採用密碼肯定是不夠安全的。如果密碼太簡單,那麼就很容易被暴力破解,如果密碼太複雜,那麼用戶就很難記憶,記錄到小本子裏面更不安全。
因此,
SSH keys
誕生了。SSH
祕鑰對的可靠性非常高,被暴力破解的可能性基本沒有。不過,這要求用戶非常謹慎地保管好私鑰,如果別人使用你的計算機時偷偷地將你的私鑰拷走了,那麼就好比是別人拿到了你家裏的鑰匙,也能隨時打開你家的門。基於以上情況,解決辦法就是在
SSH keys
之外再增加一個密碼,即passphrase
。只有同時具備SSH private key
和passphrase
的情況下,才能通過SSH
的權限校驗,這就大大地增加了安全性。當然,這個passphrase
也不是必須的,在創建祕鑰對時也可以不設置passphrase
。另外,如果每次權限校驗時都要輸入
passphrase
,這也是挺麻煩的。好在我們不用再擔心這個問題,因爲ssh-agent
可以幫我們記住passphrase
,Mac系統的Keychain也可以記住passphrase
,這樣我們在同一臺計算機中就不用重新輸入密碼了。
祕鑰對創建好以後,私鑰存放於本地計算機(~/.ssh/id_rsa
),將公鑰(~/.ssh/id_rsa.pub
)中的內容添加至GitHub賬戶。
1 2 3 4 5
|
# copy the contents of id_rsa.pub to the clipboard ➜ pbcopy < ~/.ssh/id_rsa.pub # paste to GitHub # Login GitHub, 【Settings】->【SSH and GPG keys】->【New SSH Key】
|
不過,如果此時檢測本地計算機與GitHub的連接狀態,會發現系統仍提示權限校驗失敗。
1 2
|
➜ ssh -T [email protected] Permission denied (publickey).
|
這是因爲在本地計算機與GitHub建立連接的時候,實際上是本機計算機的ssh-agent
與GitHub服務器進行通信。雖然本地計算機有了私鑰,但是ssh-agent
並不知道私鑰存儲在哪兒。因此,要想正常使用祕鑰對,需要先將私鑰加入到本地計算機的ssh-agent
中(添加過程中需要輸入passphrase
)。
1 2 3 4 5 6 7
|
# start ssh-agent in the background ➜ eval "$(ssh-agent -s)" Agent pid 78370 ➜ ssh-add ~/.ssh/id_rsa Enter passphrase for /Users/Leo/.ssh/id_rsa: <myPassphrase> Identity added: /Users/Leo/.ssh/id_rsa (/Users/Leo/.ssh/id_rsa)
|
添加完成後,就可以查看到當前計算機中存儲的密鑰。
1 2
|
➜ ssh-add -l 4096 SHA256:xRg49AgTxxxxxxxx8q2SPPOfxxxxxxxxRlBY /Users/Leo/.ssh/id_rsa (RSA)
|
再次檢測本地計算機與GitHub的連接狀態,校驗就正常通過了。
1 2
|
➜ ssh -T [email protected] Hi leolee! You've successfully authenticated, but GitHub does not provide shell access.
|
後續再進行clone
/fetch
/pull
/push
操作時,就可以正常訪問GitHub代碼倉庫了,並且也不需要再重新輸入賬號密碼。
而且,將私鑰加入ssh-agent
後,即使刪除私鑰文件,本地計算機仍可以正常訪問GitHub代碼倉庫。
1 2 3 4 5 6 7 8 9
|
➜ rm -rf ~/.ssh ➜ ssh-add -l 4096 SHA256:xRg49AgTxxxxxxxx8q2SPPOfxxxxxxxxRlBY /Users/Leo/.ssh/id_rsa (RSA) ➜ ssh -T [email protected] The authenticity of host 'github.com (192.30.252.130)' can't be established. RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added 'github.com,192.30.252.130' (RSA) to the list of known hosts. Hi leolee! You've successfully authenticated, but GitHub does not provide shell access.
|
只有執行ssh-add -D
或ssh-add -d pub_key
命令,將私鑰從ssh-agent
刪除後,認證信息纔會失效。
1 2 3 4 5 6
|
➜ ssh-add -d ~/.ssh/id_rsa.pub Identity removed: /Users/Leo/.ssh/id_rsa.pub ([email protected]) ➜ ssh-add -l The agent has no identities. ➜ ssh -T [email protected] Permission denied (publickey).
|
同時使用多個GitHub賬號
熟悉了HTTPS URLs
和SSH URLs
這兩種校驗方式之後,我們再來看之前遇到的問題。要想在一臺計算機上同時使用多個GitHub賬號訪問不同的倉庫,需要怎麼做呢?
爲了更好地演示,現假設有兩個GitHub賬號,debugtalk
和leolee
,在兩個賬號中各自有一個倉庫,debugtalk/DroidMeter
和DebugTalk/MobileStore
(公司私有庫)。
前面已經說過,HTTPS URLs
和SSH URLs
對應着兩套獨立的權限校驗方式,因此這兩套方式應該是都能單獨實現我們的需求的。
不過在詳細講解Git權限校驗的問題之前,我們先來回顧下Git配置文件的優先級。
Git配置存儲位置及其優先級
Unix-like
系統中,保存Git用戶信息的主要有3個地方(Mac系統多一個Keychain
):
/etc/gitconfig
:存儲當前系統所有用戶的git配置信息,使用帶有--system
選項的git config
時,配置信息會寫入該文件;~/.gitconfig
或~/.config/git/config
:存儲當前用戶的git配置信息,使用帶有--global
選項的git config
時,配置信息會寫入該文件;Keychain Access
:在開啓Keychain
機制的情況下,進行權限校驗後會自動將賬號密碼保存至Keychain Access
。- 倉庫的Git目錄中的config文件(即
repo/.git/config
):存儲當前倉庫的git配置信息,在倉庫中使用帶有--local
選項的git config
時,配置信息會寫入該文件;
在優先級方面,以上4個配置項的優先級從上往下依次上升,即repo/.git/config
的優先級最高,然後Keychain Access
會覆蓋~/.gitconfig
中的配置,~/.gitconfig
會覆蓋/etc/gitconfig
中的配置。
基於SSH
協議實現多賬號共存
先來看下如何採用SSH URLs
實現我們的需求。
在處理多賬號共存問題之前,兩個賬號均已分別創建SSH
祕鑰對,並且SSH-key
均已加入本地計算機的ssh-agent
。
1 2 3
|
➜ ssh-add -l 4096 SHA256:lqujbjkWM1xxxxxxxxxxG6ERK6DNYj9tXExxxxxx8ew /Users/Leo/.ssh/debugtalk_id_rsa (RSA) 4096 SHA256:II2O9vZutdQr8xxxxxxxxxxD7EYvxxxxxxbynx2hHtg /Users/Leo/.ssh/id_rsa (RSA)
|
在詳細講解多賬號共存的問題之前,我們先來回想下平時在Terminal中與GitHub倉庫進行交互的場景。
1 2 3 4 5 6 7 8 9 10 11 12 13
|
➜ DroidMeter git:(master) git pull Already up-to-date. ➜ DroidMeter git:(master) touch README.md ➜ DroidMeter git:(master) ✗ git add . ➜ DroidMeter git:(master) ✗ git commit -m "add README" ➜ DroidMeter git:(master) git push Counting objects: 3, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 310 bytes | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@debugtalk:debugtalk/DroidMeter.git 7df6839..68d085b master -> master
|
在操作過程中,本地計算機的ssh-agent
與GitHub服務器建立了連接,並進行了賬號權限校驗。
當本地計算機只有一個GitHub賬號時,這個行爲並不難理解,系統應該會採用這個唯一的GitHub賬號進行操作。那如果本地計算機中有多個Github賬號時,系統是根據什麼來判斷應該選擇哪個賬號呢?
實際情況是,系統沒法進行判斷。系統只會有一個默認的賬號,然後採用這個默認的賬號去操作所有的代碼倉庫,當賬號與倉庫不匹配時,就會報權限校驗失敗的錯誤。
那要怎樣才能讓系統正確區分賬號呢?這就需要我們手動進行配置,配置文件即是~/.ssh/config
。
創建~/.ssh/config
文件,在其中填寫如下內容。
1 2 3 4 5 6 7 8 9 10 11
|
# debugtalk Host debugtalk HostName github.com User git IdentityFile ~/.ssh/id_rsa # DT Host leolee HostName github.com User git IdentityFile ~/.ssh/dt_id_rsa
|
要理解以上配置文件的含義並不難,我們可以對比看下兩個項目的SSH URLs
:
1 2
|
[email protected]:debugtalk/DroidMeter.git [email protected]:DTSZ/Store_Android.git
|
其中,git
是本地ssh-agent
與GitHub服務器建立SSH
連接採用的用戶名(即User
),github.com
是GitHub服務器的主機(即HostName
)。
可以看出,如果採用原始的SSH URLs
,由於User
和HostName
都相同,本地計算機並不知道應該採用哪個SSH-key
去建立連接。
因此,通過創建~/.ssh/config
文件,在Host
中進行區分,然後經過CNAME
映射到HostName
,然後分別指向不同的SSH-key
,即IdentityFile
。由於HostName
纔是真正指定GitHub服務器主機的字段,因此這麼配置不會對本地ssh-agent
連接GitHub主機產生影響,再加上Host
別名指向了不同的SSH-key
,從而實現了對兩個GitHub賬號的分離。
配置完畢後,兩個GitHub賬號就可以通過Host
別名來進行區分了。後續再與GitHub服務器進行通信時,就可以採用Host
別名代替原先的github.com
。例如,測試本地ssh-agent
與GitHub服務器的連通性時,可採用如下方式:
1 2 3 4
|
➜ ssh -T git@debugtalk Hi debugtalk! You have successfully authenticated, but GitHub does not provide shell access. ➜ ssh -T git@leolee Hi leolee! You have successfully authenticated, but GitHub does not provide shell access.
|
可以看出,此時兩個賬號各司其職,不會再出現混淆的情況。
不過,我們還遺漏了很重要的一點。在本地代碼倉庫中執行push
/pull
/fetch
等操作的時候,命令中並不會包含Host
信息,那系統怎麼知道我們要採用哪個GitHub賬號進行操作呢?
答案是,系統還是沒法判斷,需要我們進行配置指定。
顯然,不同的倉庫可能對應着不同的GitHub賬號,因此這個配置不能配置成全局的,而只能在各個項目中分別進行配置,即repo/.git/config
文件。
配置的方式如下:
在debugtalk/DroidMeter
倉庫中:
1
|
➜ git remote add origin git@debugtalk:debugtalk/DroidMeter.git
|
在DebugTalk/MobileStore.git
倉庫中:
1
|
➜ git remote add origin git@leolee:DebugTalk/MobileStore.git
|
配置的原理也很容易理解,就是將倉庫的Host
更換爲之前設置的別名。添加完畢後,後續再在兩個倉庫中執行任何git
操作時,系統就可以選擇正確的SSH-key
與GitHub服務器進行交互了。
基於HTTPS
協議實現多賬號共存
再來看下如何採用HTTPS URLs
實現我們的需求。
有了前面的經驗,我們的思路就清晰了許多。採用HTTPS URLs
的方式進行Git權限校驗後,系統會將GitHub賬號密碼存儲到Keychain
中(Mac系統),或者存儲到~/.git-credentials
文件中(Git credential helper
)。
不管是存儲到哪裏,我們面臨的問題都是相同的,即如何在代碼倉庫中區分採用哪個GitHub賬號。
配置的方式其實也很簡單:
在debugtalk/DroidMeter
倉庫中:
1
|
➜ git remote add origin https://[email protected]/debugtalk/DroidMeter.git
|
在DebugTalk/MobileStore.git
倉庫中:
1
|
➜ git remote add origin https://[email protected]/DebugTalk/MobileStore.git
|
配置的原理也很容易理解,將GitHub用戶名添加到倉庫的Git地址中,這樣在執行git命令的時候,系統就會採用指定的GitHub用戶名去Keychain
或~/.git-credentials
中尋找對應的認證信息,賬號使用錯亂的問題也就不復存在了。
Done!