OpenSSH/食譜/公鑰認證
如果正確使用,認證金鑰可以提高效率。作為額外優勢,密碼和私鑰永遠不會離開客戶端[1]。對於面向外部的系統,通常建議使用基於金鑰的認證,以便可以關閉密碼認證。
OpenSSH 可以使用公鑰密碼學進行認證。在公鑰密碼學中,加密和解密是非對稱的。金鑰成對使用,公鑰用於加密,私鑰用於解密。 ssh-keygen(1) 實用程式可以建立 RSA、Ed25519、ECDSA、Ed25519-SK 或 ECDSA-SK 金鑰用於認證。雖然 DSA 金鑰仍然可以建立,但大小固定為 1024 位,不再推薦使用,應避免使用。RSA 金鑰允許從 1024 位開始變化。預設值現在是 3072。但是,在 2048 位之後,只有有限的益處,這使得橢圓曲線演算法更可取。ECDSA 的大小可以是 256、384 或 521 位。Ed25519、Ed25519-SK 和 ECDSA-SK 金鑰的長度都固定為 256 位。較短的金鑰速度更快,但安全性較低。較長的金鑰處理起來要慢得多,但提供了更好的保護,直到一定程度為止。
金鑰可以命名以幫助記住它們的用途。因為金鑰檔案可以命名為任何東西,所以可以擁有許多金鑰,每個金鑰都為不同的服務或任務命名。公鑰末尾的註釋欄位也可以在幫助對金鑰進行排序方面發揮作用,如果你有很多金鑰或不經常使用它們。
基於金鑰的認證過程使用這些金鑰進行幾次交換,使用金鑰來加密和解密一些簡短的訊息。在開始時,客戶端公鑰的副本儲存在伺服器上,客戶端私鑰儲存在客戶端上,兩者都保留在各自的位置。私鑰永遠不會離開客戶端。當客戶端首次聯絡伺服器時,伺服器會使用客戶端公鑰加密一個隨機數,並將該加密的隨機數作為挑戰返回給客戶端。客戶端透過使用匹配的私鑰解密訊息並提取隨機數來響應挑戰。然後,客戶端對會話 ID 和來自挑戰的隨機數進行 MD5 雜湊,並將該雜湊返回給伺服器。然後,伺服器對會話 ID 和隨機數進行自己的雜湊,並將結果與客戶端返回的雜湊進行比較。如果匹配,則允許登入。如果不匹配,則嘗試伺服器上註冊為屬於同一帳戶的下一個公鑰,直到找到匹配項或所有金鑰都已嘗試或達到最大失敗次數為止。[2]
當在客戶端使用代理來管理認證時,該過程類似。不同之處在於 ssh(1) 將挑戰傳遞給代理,代理計算響應並將其傳遞迴 ssh(1),然後 ssh(1) 將代理的響應傳遞迴伺服器。
公鑰認證需要一對匹配的金鑰, ssh-keygen(1) 用於生成金鑰對。在這對金鑰中,公鑰必須正確儲存在遠端主機上。在伺服器上,公鑰被新增到該遠端使用者帳戶指定的 authorized_keys 檔案中。私鑰安全地儲存在客戶端上。一旦金鑰準備就緒,就可以反覆使用。
基於金鑰的認證準備工作包含四個步驟。
1) 準備金鑰存放的目錄。如果遠端機器上不存在 authorized_keys 檔案或 .ssh 目錄,或者客戶端機器上不存在 .ssh 目錄,請建立它們並正確設定許可權。在客戶端上只需要一個目錄,但該目錄不應被除所有者以外的任何帳戶寫入。
$ mkdir ~/.ssh/
$ chmod 0700 ~/.ssh/
在遠端機器上,需要 .ssh 目錄和一個用於儲存公鑰的特殊檔案,預設情況下是 authorized_keys。
$ mkdir ~/.ssh/
$ touch ~/.ssh/authorized_keys
$ chmod 0700 ~/.ssh/
$ chmod 0600 ~/.ssh/authorized_keys
2) 建立金鑰對。此處的示例在 ~/.ssh 目錄中建立了一個 Ed25519 金鑰對。-t 選項指定金鑰型別,-f 選項指定金鑰檔案的名稱。最好給金鑰檔案賦予描述性的名稱,尤其是在管理大量金鑰時。下面,公鑰將被命名為 mykey_ed25519.pub,私鑰將被命名為 mykey_ed25519。請務必輸入可靠的密碼,使用 128 位 AES 加密私鑰。
$ ssh-keygen -t ed25519 -f ~/.ssh/mykey_ed25519
Ed25519、Ed25519-SK 和 ECDSA-SK 金鑰具有固定長度。對於 RSA 和 ECDSA 金鑰,-b 選項設定使用的位數。
從版本 6.5 開始,可以使用新的私鑰格式,使用 bcrypt(3) 金鑰派生函式 (KDF) 來更好地保護靜止狀態下的金鑰。這種新格式始終用於 Ed25519 金鑰,並且將來可能會成為所有金鑰的預設格式。但目前,可以透過 ssh-keygen(1) 中的 -o 選項在生成或儲存其他型別金鑰時請求它。
$ ssh-keygen -o -b 4096 -t rsa -f ~/.ssh/mykey_rsa
有關新格式的詳細資訊可以在原始碼中的 PROTOCOL.key 檔案中找到。
3) 將金鑰放到正確的位置。僅將公鑰傳輸到遠端機器。
$ ssh-copy-id -i ~/.ssh/mykey_ed25519 fred@remotehost.example.org
如果 ssh-copy-id(1) 不可用,可以使用任何不會換行的編輯器。例如,nano(1) 可以使用 -w 選項啟動,以防止長行換行。另一種方法是永久設定它,方法是編輯 nanorc(5)。但是,在編輯 authorized_keys 檔案以新增金鑰時,金鑰本身必須完整無缺地位於檔案中的單行上。
然後,如果它們尚未存在於客戶端上,請將公鑰和私鑰都傳輸到那裡。通常最好將公鑰和私鑰都儲存在 ~/.ssh/ 目錄中,儘管在此步驟之後客戶端不需要公鑰,並且可以再次生成。如果需要。
4) 測試金鑰。
在仍然登入的情況下,使用客戶端在新的視窗中啟動另一個 SSH 會話,並嘗試使用私鑰從客戶端進行身份驗證以連線到遠端機器。
$ ssh -i ~/.ssh/mykey_ed25519 -l fred remotehost.example.org
-i 選項告訴 ssh(1) 嘗試哪個私鑰。僅在驗證基於金鑰的認證有效後,關閉原始 SSH 會話。
一旦基於金鑰的身份驗證被驗證為工作正常,就可以使用 ssh_config(5) 在客戶端建立永久快捷方式,下面將進一步解釋。特別是,請參閱 IdentityFile、IdentitiesOnly 和 AddKeysToAgent 配置指令,僅舉三例。
➥ 基於金鑰的身份驗證的故障排除
如果伺服器拒絕接受金鑰並切換到下一個身份驗證方法(例如:“伺服器拒絕我們的金鑰”),那麼在伺服器端有幾個可能的錯誤需要查詢。
最常見的錯誤之一是檔案和目錄許可權錯誤。授權金鑰檔案必須由相關使用者擁有,並且不能由組寫入。金鑰檔案的目錄也不能由組或世界寫入。
$ chmod u=rwx,g=rx,o= ~/.ssh
$ chmod u=rw,g=,o= ~/.ssh/authorized_keys
另一個可能發生的錯誤是,如果遠端主機上的 authorized_keys 檔案中的金鑰被換行符或其他空格隔開,則該金鑰會損壞。可以透過合併行並刪除空格,或更仔細地重新複製金鑰來解決此問題。
而且,雖然應該不言而喻,但金鑰對的兩個部分需要匹配。伺服器上的公鑰需要與客戶端持有的私鑰匹配。如果公鑰丟失,則可以使用 -y 選項生成一個新的公鑰,但反之則不行。如果私鑰丟失,則應擦除公鑰,因為它不再有任何用處。如果一個帳戶使用多個金鑰,添加註釋可能是個好主意。在客戶端,最好了解金鑰適用於哪個伺服器,可以透過檔名本身或註釋欄位來實現。可以使用 -C 選項添加註釋。
$ ssh-keygen -t ed25519 -f ~/.ssh/mykey_ed25519 -C "web server mirror"
在伺服器上,如果一個帳戶中存在多個公鑰,則註釋金鑰來自哪個客戶端非常重要。如果註釋不存在,則可以在伺服器上將註釋新增到授權金鑰檔案的最後一列中。同樣,授權金鑰檔案的格式在 sshd(8) 手冊頁的“AUTHORIZED_KEYS FILE FORMAT”部分給出。如果金鑰沒有標籤,它們可能很難匹配,這可能正是你想要的,也可能不是。
將金鑰永久關聯到伺服器
[edit | edit source]金鑰可以在執行時指定,但為了節省重複輸入相同路徑的時間,ssh_config(5) 中的 Host 指令可以將特定設定應用於目標主機。在這種情況下,透過更改 ~/.ssh/config,可以將特定的金鑰分配給在連線到該特定主機時自動嘗試。在將以下幾行新增到 ~/.ssh/config 後,只需要鍵入 ssh web1 就可以使用該伺服器的金鑰進行連線。
Host web1
Hostname 198.51.100.32
IdentitiesOnly yes
IdentityFile /home/fred/.ssh/web_key_ed25519
下面的 ~/.ssh/config 對 server 和 server.example.org 使用不同的金鑰,無論它們是否解析到同一臺機器。這是可能的,因為傳遞給 ssh(1) 的主機名引數在匹配之前不會轉換為規範化的主機名。
Host server
IdentitiesOnly yes
IdentityFile /home/fred/.ssh/key_a_rsa
Host server.example.org
IdentitiesOnly yes
IdentityFile /home/fred/.ssh/key_b_rsa
在這個例子中,較短的名稱首先被嘗試,當然也可以使用不太模糊的快捷方式。配置檔案按照首個匹配的原則進行解析。因此,最具體的規則放在開頭,最通用的規則放在結尾。
加密的家目錄
[edit | edit source]當使用加密的家目錄時,金鑰必須儲存在未加密的目錄中。這意味著儲存在實際家目錄之外的某個地方,這意味著 sshd(8) 需要被適當地配置,以便在該特殊位置找到金鑰。
以下是一種解決訪問問題的方法。每個使用者在 /etc/ssh/keys/ 下都有一個子目錄,他們可以使用它來儲存 authorized_keys 檔案。這在伺服器的配置檔案 /etc/ssh/sshd_config 中設定。
AuthorizedKeysFile /etc/ssh/keys/%u/authorized_keys
為金鑰設定特殊位置為金鑰的管理提供了更多可能性,如果多個金鑰檔案位置用空格隔開,則可以指定多個金鑰檔案位置。使用者不必對 authorized_keys 檔案具有寫入許可權。只需要讀取許可權就可以登入。但是,如果使用者被允許新增、刪除或更改他們的金鑰,那麼他們將需要對檔案進行寫入操作才能做到這一點。
加密家目錄的一個症狀是,基於金鑰的身份驗證僅在您已經登入到同一帳戶時有效,但在嘗試進行第一次連線並首次登入時會失敗。
有時還需要在身份驗證後立即從 /etc/ssh/sshrc 中新增指令碼或呼叫程式來解密家目錄。
無密碼登入
[edit | edit source]允許無密碼登入的一種方法是遵循上述步驟,但在建立金鑰時,在提示輸入密碼時,只需不輸入密碼即可。請注意,使用沒有密碼的金鑰風險很大,因此金鑰檔案應受到非常好的保護並進行跟蹤。這包括它們只能用作下面描述的單用途金鑰。及時輪換金鑰變得尤為重要。一般來說,不建議建立沒有密碼的金鑰。一個更好的解決方案是設定密碼,並結合使用身份驗證代理和單用途金鑰。如今,大多數桌面環境會自動啟動 SSH 代理。如果它是,它將在 SSH_AUTH_SOCK 環境變數中可見。在具有代理的帳戶上,ssh-add(1) 可以將私鑰載入到可用的代理中。
$ ssh-add ~/.ssh/mykey_ed25519
此後,客戶端將在需要時自動檢查代理以獲取金鑰。如果代理中有許多金鑰,則需要設定 IdentitiesOnly。請參閱上面關於使用 ~/.ssh/config 的部分。請參閱下面的 [OpenSSH/Cookbook/Public_Key_Authentication#Key-based_Authentication_Using_an_Agent 基於金鑰的身份驗證使用代理]。
同時要求金鑰和密碼
[edit | edit source]雖然使用者應該為他們的金鑰設定強密碼,但無法強制或驗證這一點。事實上,由於私鑰及其密碼從未離開客戶端機器,伺服器無法對它們有任何影響。相反,可以同時要求金鑰和密碼。從 OpenSSH 6.2 開始,伺服器可以使用 AuthenticationMethods 指令要求使用者使用多種身份驗證方法登入。
AuthenticationMethods publickey,password
來自 http://man.openbsd.org/sshd_config.5 sshd_config(5)] 的這個例子要求使用者首先使用金鑰進行身份驗證,只有當金鑰成功時才會查詢密碼。因此,使用該配置,如果沒有先使用有效的金鑰進行身份驗證,就無法進入系統密碼提示符。更改引數的順序會更改身份驗證方法的順序。
要求兩個或更多個金鑰
[edit | edit source]從 OpenSSH 6.8 開始,伺服器現在會記住哪些公鑰已用於身份驗證,並拒絕接受之前使用過的金鑰。這允許設定要求使用者使用兩個不同的公鑰進行身份驗證,例如一個在檔案系統中,另一個在硬體令牌中。
AuthenticationMethods publickey,publickey
AuthenticationMethods 指令(無論用於金鑰還是密碼),也可以在伺服器上的 Match 指令下設定,以便僅應用於某些組或情況。
要求特定型別的金鑰進行身份驗證
[edit | edit source]同樣,從 OpenSSH 6.8 開始,PubkeyAcceptedKeyTypes 指令(後來改為 PubkeyAcceptedAlgorithms)可以指定哪些金鑰演算法可用於身份驗證。不在逗號分隔的模式列表中的演算法不允許使用。
PubkeyAcceptedAlgorithms ssh-ed25519*,ssh-rsa*,ecdsa-sha2*,sk-ssh-ed25519*,sk-ecdsa-sha2*
模式列表中可以是實際的金鑰型別,也可以是模式。模式列表中不允許使用空格。可以使用客戶端的 -Q 選項找到用於身份驗證的受支援的金鑰型別的完整列表。以下兩行是等效的。
$ ssh -Q key-sig | sort
$ ssh -Q PubkeyAcceptedAlgorithms | sort
對於基於主機的身份驗證,HostbasedAcceptedAlgorithms 指令決定哪些金鑰型別允許用於身份驗證。
當使用身份驗證代理(例如 ssh-agent(1))時,它通常應該在會話開始時啟動,並用於啟動登入會話或 X 會話,以便指向代理及其 UNIX 域套接字的環境變數傳遞給每個後續 shell 和程序。許多桌面發行版在登入或啟動時自動執行此操作。
啟動代理需要設定一對環境變數
- SSH_AGENT_PID : 代理的程序 ID
- SSH_AUTH_SOCK : UNIX 域套接字的檔名和完整路徑
各種 SSH 和 SFTP 客戶端會自動找到這些變數,並在需要身份驗證時使用它們來聯絡代理並嘗試。但是,主要使用 SSH_AUTH_SOCK。如果 shell 或桌面會話是使用 ssh-agent(1) 啟動的,那麼這些變數已經設定並可用。如果它們不可用,則需要在每個 shell 內或每個應用程式中手動設定變數,以便使用代理,或者使用客戶端配置檔案中的指令 IdentityAgent 指向代理的套接字。
代理可用後,需要載入相關的私鑰才能使用代理。私鑰載入到代理後,就可以多次使用。私鑰使用 ssh-add(1) 載入到代理中。
$ ssh-add /home/fred/.ssh/mykey_ed25519
Identity added: /home/fred/.ssh/mykey_ed25519 (/home/fred/.ssh/mykey_ed25519)
金鑰在代理執行期間一直保留在代理中,除非另有說明。可以使用啟動代理本身時的 -t 選項,或使用 ssh-add(1) 載入金鑰時設定超時。在這兩種情況下,-t 選項都將設定一個超時間隔,在此之後,金鑰將從代理中清除。
$ ssh-add -t 1h30m /home/fred/.ssh/mykey_ed25519
Identity added: /home/fred/.ssh/mykey_ed25519 (/home/fred/.ssh/mykey_ed25519)
Lifetime set to 5400 seconds
選項 -l 將列出代理中所有身份的指紋。
$ ssh-add -l
256 SHA256:77mfUupj364g1WQ+O8NM1ELj0G1QRx/pHtvzvDvDlOk mykey for task x (ED25519)
3072 SHA256:7unq90B/XjrRbucm/fqTOJu0I1vPygVkN9FgzsJdXbk myotherkey rsa for task y (RSA)
也可以使用 -d 從代理中刪除單個身份,如果按檔名標識,則會一次刪除一個。但是,只有在給出檔名且沒有給出要刪除的私鑰檔名時,-d 才會靜默失敗。使用 -D 而不是 -d 將一次刪除所有身份,而無需按名稱指定任何身份。
預設情況下,ssh-add(1) 使用透過環境變數 SSH_AUTH_SOCK 中命名的套接字連線的代理(如果已設定)。目前,這是它唯一的選項。但是,對於 ssh(1),使用環境變數的另一種方法是客戶端配置檔案指令 IdentityAgent,它告訴 SSH 客戶端使用哪個套接字與代理通訊。如果環境變數和配置檔案指令同時存在,那麼 IdentityAgent 中的值將優先於環境變數中的值。IdentityAgent 也可以設定為 none,以防止連線嘗試使用任何代理。
客戶端配置檔案指令 AddKeysToAgent 也可以在將金鑰新增到代理時很有用。當設定此選項時,如果金鑰尚未載入,它會在第一次呼叫金鑰時自動將金鑰載入到正在執行的代理中。同樣,IdentitiesOnly 指令可以確保在第一次嘗試時提供相關金鑰。與其在每次執行客戶端時鍵入這些指令,不如將它們新增到 ~/.ssh/config 中,從而為指定的宿主連線自動新增這些指令。
代理轉發是透過一個或多箇中間主機的一種方法。但是,-J 選項用於 ProxyJump 會是一個更安全的選擇。請參閱跳轉主機部分中的 透過閘道器或兩個閘道器,瞭解這方面的知識。使用代理轉發,中間機器會在客戶端和最終目標之間來回轉發挑戰和響應。這帶來了一些風險,但省去了在這些中間機器上使用密碼或持有金鑰的必要性。
代理轉發的主要優點是私鑰本身不需要放在任何遠端機器上,從而阻止了對私鑰的無意檔案系統訪問。[3] 另一個優點是,使用者已認證的實際代理不會轉到任何地方,因此不易受到分析。
代理的一個風險是,如果許可權允許,它們可以被重複使用以尾隨進入。金鑰無法以這種方式複製,但當權限不正確時,身份驗證是可能的。請注意,停用代理轉發不會提高安全性,除非使用者也被拒絕 shell 訪問許可權,因為他們總是可以安裝自己的轉發器。
可以透過在將金鑰新增到代理時新增 -c 選項來確認每次使用金鑰,從而減輕代理轉發的風險。這需要設定 SSH_ASKPASS 變數,並使其可供代理程序使用,但會在每次遠端系統使用金鑰時,在執行代理的主機上生成一個提示。因此,如果要透過一個或多箇中間主機,通常最好讓 SSH 客戶端使用帶有 -W 或 -J 的 stdio 轉發。
在客戶端側,代理轉發預設情況下是停用的,因此如果要使用它,必須顯式啟用它。將以下行新增到 ssh_config(5) 中,以啟用特定伺服器的代理轉發
Host gateway.example.org
ForwardAgent yes
在伺服器側,預設配置檔案允許身份驗證代理轉發,因此要使用它,不需要在伺服器側做任何操作,只需在客戶端側做操作。但是,同樣,最好還是看看 ProxyJump 而不是代理轉發。
透過一個或多箇中間主機的最佳方法是使用 ProxyJump 選項,而不是身份驗證代理轉發,從而避免將任何私鑰暴露給風險。如果必須使用身份驗證代理轉發,那麼出於遵循最小特權原則的考慮,建議轉發包含最少必要數量金鑰的代理。有幾種方法可以解決這個問題。
在 8.8 及更早版本中,一種部分解決方案是建立一個一次性、臨時的代理,專門用於儲存當前任務所需的金鑰。另一種部分解決方案是,在作業系統級別設定一個使用者可訪問的服務,然後使用 ssh_config 來完成剩餘的操作。
可以透過建立特殊的 shell 別名或函式來啟動每個會話唯一的臨時代理,從而自動啟動臨時代理。該函式或別名可以被編寫為需要確認每個請求的簽名。以下示例是一個別名,基於 Vincent Bernat[4] 在 SSH 代理轉發方面的更新部落格文章。
$ alias assh="ssh-agent ssh -o AddKeysToAgent=confirm -o ForwardAgent=yes"
請注意 ssh-agent(1) 的使用。當呼叫該別名時,SSH 客戶端將使用一個獨特的、臨時的支援金鑰代理啟動。該別名設定一個新的代理,包括設定兩個環境變數,然後在呼叫客戶端時設定兩個客戶端選項。這種安排仍然會檢查 ssh_config(5) 中的其他選項和設定。當 SSH 會話結束時,啟動它的代理結束並消失,從而自動清理自身。
另一種方法是依賴客戶端的配置檔案來完成一些設定。這些方法主要依賴於 ssh_config(5),但仍然需要一種獨立的方法來啟動一個臨時代理,因為 OpenSSH 客戶端在讀取配置檔案之前就已經在執行,因此不受配置檔案導致的環境變數更改的影響,而代理資訊是透過環境變數傳遞的。但是,當用於與身份驗證代理通訊的 UNIX 域套接字的路徑預先確定時,IdentityAgent 選項可以指向它,一旦一次性代理[5] 實際啟動。以下方法在連線到兩個特定域中的任何一個時,使用特定代理的預定義套接字
Host *.wikimedia.org *.wmflabs.org
User fred
IdentitiesOnly yes
IdentityFile %d/.ssh/id_cloud_01
IdentityAgent /run/user/%i/ssh-cloud-01.socket
ForwardAgent yes
AddKeysToAgent yes
%d 代表主目錄的路徑,%i 代表當前帳戶的使用者 ID (UID)。在某些情況下,%i 標記在配置 檔案內設定 IdentityAgent 選項時也會很有用。同樣,在轉發代理時,請注意轉發代理中使用哪些金鑰。有關更多此類縮寫,請參閱 ssh_config(5) 中的“TOKENS”部分。
使用這些配置設定,身份驗證代理必須已經在執行,並且在啟動 SSH 客戶端以使該配置生效之前指向指定的套接字。此外,它應該將套接字放在任何其他帳戶都無法訪問的目錄中。ssh-agent(1) 必須使用 -a 選項來命名套接字。
$ ssh-agent -a /run/user/${UID}/ssh-cloud-01.socket
該代理配置可以手動啟動,也可以透過指令碼或服務管理器啟動。但是,出於隱私和安全考慮,應避免代理轉發。配置指令 ProxyJump 是最好的替代方案,在舊系統上,使用 ProxyCommand 和 netcat 進行主機遍歷是更可取的。同樣,請參閱有關 代理和跳轉主機 的部分,瞭解這些方法的使用方式。
新的 SSH 代理目標約束
[edit | edit source]從 8.9 版本開始,ssh-agent(1) 將允許代理限制使用哪些主機進行身份驗證,如 ssh-add(1) 使用 -h 選項指定的。這些約束是透過兩個代理協議擴充套件和對公鑰身份驗證協議的修改新增的。此功能可能會發展,但目前的結果是帳戶身份驗證的金鑰可以透過四種方式載入到代理中
- 轉發沒有限制(不推薦)
- 僅限本地使用,這些金鑰不會轉發
- 轉發,但僅限於特定遠端主機
- 透過指定路由轉發到特定遠端主機
目的是讓限制安全失敗,這樣就不會在路由中的一臺或多臺主機缺乏必要的協議功能時允許身份驗證。在金鑰載入後,目標和路由無法修改,但可以載入到同一個目標的多條路由,並且路由可以是任意數量的跳躍。如果需要更改路由,則必須使用新路由將金鑰重新載入到代理中。
客戶端的一般預設行為是將金鑰保留在代理中以供本地使用。但是,可以透過在啟動客戶端時新增 -a 選項,或者在相關配置塊中將 ssh_config(5) 中的 ForwardAgent 指令設定為 'no' 來顯式強制執行。
為了載入用於無限制轉發的金鑰(這不是最佳做法),只需使用 ssh-add(1) 像往常一樣新增它們。然後,在客戶端中使用 -A 選項,或者在相關配置塊中將 ssh_config(5) 中的 ForwardAgent 指令設定為 'yes'。
為了將金鑰限制為僅連線到特定的遠端主機,或者將金鑰載入到特定遠端主機以透過一個或多箇中間主機進行轉發,請在將金鑰載入到代理中時使用 -h 選項。這裡,一個金鑰只能用於連線到特定目標。
$ ssh-agent -h server.example.org server.key.ed25519
如果透過中間系統,最好的方法是使用 ProxyJump,即 SSH 客戶端的 -J 選項。如果必須允許代理轉發,那麼最嚴格的方法是約束哪些系統可以使用金鑰,同樣可以使用 -h 選項。
$ ssh-agent -h middle.example.org -h "middle.example.org>server.example.org" server.key.ed25519
可以包含多個步驟,甚至可以包含多條路由。它們只需要顯式列舉,儘管模式仍然可以用於目標主機以及特定名稱。鏈中的每個主機都必須支援這些協議擴展才能完成連線。
任何被指定用於轉發的金鑰都無法用於對除那些被明確識別為轉發目標以外的任何其他主機進行身份驗證。這些允許的主機透過 known_hosts 檔案或在使用 ssh-add(1) 載入金鑰時由 -H 選項指定的另一個檔案中的主機金鑰或主機證書進行識別。如果在將金鑰載入到代理時不使用 -H,則將使用預設的已知主機檔案:~/.ssh/known_hosts、/etc/ssh/ssh_known_hosts、~/.ssh/known_hosts2 和 /etc/ssh/ssh_known_hosts2。
對於金鑰,必須認真維護 [6] known_hosts 列表,也許可以使用 UpdateHostkeys 和 CanonicalizeHostname 客戶端配置指令。使用證書要求代理只需要知道證書頒發機構 (CA)。
同樣,請參閱跳轉主機部分中的 透過閘道器,瞭解如何在不需要轉發 SSH 代理的情況下透過一臺或多臺中間機器。
檢查代理中的特定金鑰
[edit | edit source]ssh_add(1) 實用程式的 -T 選項可以透過查詢匹配的公鑰來測試特定私鑰是否在代理中可用。這在 shell 指令碼中很有用。
#!/bin/sh
key=/home/fred/.ssh/some.key.ed25519.pub
if ssh-add -T ${key}; then
echo "Key ${key} Found"
else
echo "Key ${key} missing"
fi
或者,它也可以使用另一種語法完成,無論是指令碼還是互動式 shell 會話,
$ key=/home/fred/.ssh/some.key.ed25519.pub
$ ssh-add -T ${key} && echo "Key found" || echo "Key missing"
但是,如果需要將金鑰新增到代理中,則 AddKeysToAgent 客戶端配置選項可以確保在任何給定的登入會話期間第一次使用時將特定金鑰新增到 SSH 代理中。這可以透過使用 -o AddKeysToAgent=yes 作為執行時引數來完成,也可以透過修改 ssh_config(5) 來完成,具體取決於情況。
Host www
HostName www.example.com
IdentityFile %d/.ssh/www.ed25519
IdentitiesOnly yes
AddKeysToAgent yes
使用配置 檔案中的這些選項,第一次執行 ssh www 時,指定的金鑰將被新增到代理中並保持可用。
使用硬體安全令牌進行基於金鑰的身份驗證
[edit | edit source]雖然獨立金鑰已經存在很長時間了,但從 8.2 版本開始,可以使用由硬體安全令牌支援的金鑰,例如 OnlyKey、Yubikey 或其他許多金鑰,但透過 FIDO2 協議。通用第二因素 (U2F) 身份驗證透過 FIDO2 在 OpenSSH 中直接受支援,不需要第三方軟體。目前,有兩種型別的硬體支援的金鑰,ECDSA-SK 和 Ed25519-SK,但只有最新的硬體令牌支援後者。如果令牌韌體不支援 Ed25519-SK 金鑰格式,那麼在嘗試使用該金鑰型別時,將顯示以下錯誤訊息
Key enrollment failed: invalid format
如果受支援,可以使用 ssh-keygen(1) 建立這兩種金鑰型別。步驟幾乎與建立普通金鑰相同,但令牌必須先對系統可用(插入)。然後,如果需要,必須輸入令牌的 PIN 碼,並觸控令牌或以其他方式啟用它。之後,金鑰建立將像往常一樣進行。注意由 -t 選項指定的金鑰型別。
$ ssh-keygen -t ed25519-sk -f /home/fred/.ssh/server.ed25519-sk -C "web server for fred"
Generating public/private ed25519-sk key pair.
You may need to touch your authenticator to authorize key generation.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/fred/.ssh/server.ed25519-sk
Your public key has been saved in /home/fred/.ssh/server.ed25519-sk.pub
The key fingerprint is:
SHA256:41wVVDnKJ9gKr2Sj4CFuYMhcNvYebZ6zq0PWyP4rRDo web server
The key's randomart image is:
+[ED25519-SK 256]-+
| .o... |
| .o |
| +.. . |
| = . . ..= . |
|+ + * + So.. o |
|o+.EoO *+oo |
|.o oBo+++o |
| o .=.+. |
| . .=== |
+----[SHA256]-----+
建立完成後,公鑰和私鑰檔案像處理任何其他型別的金鑰一樣處理。但在進行身份驗證時,硬體令牌必須存在並在需要時啟用。
$ ssh -i /home/fred/.ssh/server.ed25519-sk server.example.org
Enter passphrase for key '/home/fred/.ssh/server.ed25519-sk':
Confirm user presence for key ED25519-SK SHA256:41wVVDnKJ9gKr2Sj4CFuYMhcNvYebZ6zq0PWyP4rRDo
生成的私鑰檔案實際上不是金鑰本身,而是“金鑰控制代碼”,硬體安全令牌使用該控制代碼根據需要在實際使用時派生真正的私鑰 [7]。因此,沒有配套的硬體令牌,硬體支援的私鑰檔案毫無用處。這也意味著這些金鑰檔案不能跨硬體令牌移植,例如當有多個令牌作為備用或備份時,即使由同一個帳戶使用也是如此。因此,當使用多個硬體令牌時,必須為每個令牌生成不同的金鑰對。
硬體安全令牌駐留私鑰
[edit | edit source]可以將私鑰儲存在令牌本身中,但目前它無法直接從令牌中使用,必須先將其儲存為檔案。此外,金鑰只能在使用 ssh-keygen(1) 中的 -O resident 選項建立時載入到 FIDO 身份驗證器中。否則,過程與上述相同。
$ ssh-keygen -O resident -t ed25519-sk -f /home/fred/.ssh/server.ed25519-sk -C "web server for fred"
. . .
如有需要,可以使用-K選項從FIDO2硬體令牌中提取居民金鑰並儲存到檔案。在此階段,可以向檔案新增密碼,但令牌本身不會保留任何密碼,只有可選的PIN保護那裡的金鑰。
$ ssh-keygen -K
Enter PIN for authenticator:
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Saved ED25519-SK key to id_ed25519_sk_rk
$ mv -i id_ed25519_sk_rk /home/fred/.ssh/server.ed25519-sk
由於輸出檔名是固定的,任何具有該名稱的現有檔案都可能被覆蓋,但會先發出警告。但是,不建議將金鑰儲存在硬體令牌上,因為將其單獨儲存可以提供更多保護。
單用途金鑰
[edit | edit source]定製的單用途金鑰可以消除許多管理活動中對遠端root登入的使用。需要一個精心定製的sudoers以及一個非特權帳戶。如果操作正確,它將提供足夠的操作許可權,遵循最小許可權的安全原則。
單用途金鑰與在sshd_config(5)中使用ForceCommand指令或在authorized_keys檔案中使用command="..."指令一起使用。該方法是生成一個新的金鑰對,將公鑰傳輸到遠端系統上的authorized-keys,然後將相應的命令或指令碼新增到帶有金鑰的那一行。
$ grep '^command' ~/.ssh/authorized_keys
command="/usr/local/bin/somescript.sh" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcgdzDvSebOEjuegEx4W1I/aA7MM3owHfMr9yg2WH8H
插入的command="..."指令將覆蓋其他所有內容,並確保在僅使用該金鑰登入時,僅執行指令碼/usr/local/bin/somescript.sh。如果需要將引數傳遞給指令碼,請檢視SSH_ORIGINAL_COMMAND環境變數的內容,並在 case 語句中使用它。永遠不要信任該變數的內容,也不要直接使用內容,始終間接使用。
單用途金鑰可用於僅允許隧道而無其他操作。以下金鑰將僅回顯一些文字然後退出,除非在-N選項的非互動模式下使用。
$ grep '^command' ~/.ssh/authorized_keys
command="/bin/echo do-not-send-commands" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBzTIWCaILN3tHx5WW+PMVDc7DfPM9xYNY61JgFmBGrA
無論使用者在使用該金鑰登入時嘗試什麼操作,會話都將僅回顯給定的文字然後退出。使用-N選項將停用執行遠端程式,允許連線保持開啟狀態,允許隧道。
$ ssh -L 3306:localhost:3306 \
-i ~/.ssh/tunnel_ed25519 \
-N \
-l fred \
server.example.com
這將建立一個隧道,即使在金鑰配置將關閉互動式會話的情況下也會保持連線。另請參閱ssh(1)的-n或-f選項。
避免遠端root訪問的單用途金鑰
[edit | edit source]簡單的方法是編寫一個簡短的 shell 指令碼,將其放在/usr/local/bin/中,然後配置sudoers以允許其他非特權帳戶僅執行該指令碼,並且只能執行該指令碼。
%wheel ALL=(root:root) NOPASSWD: /usr/sbin/service httpd stop
%wheel ALL=(root:root) NOPASSWD: /usr/sbin/service httpd start
然後金鑰使用authorized_keys中的command="..."呼叫該指令碼。這裡,一個金鑰啟動web伺服器,另一個金鑰停止web伺服器。
$ grep '^command' ~/.ssh/authorized_keys
command="/usr/bin/sudo /usr/sbin/service httpd stop" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcgdzDvSebOEjuegEx4W1I/aA7MM3owHfMr9yg2WH8H
command="/usr/bin/sudo /usr/sbin/service httpd start" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMidyqZ6OCvbWqA8Zn+FjhpYE6NoWSxVjFnFUk6MrNZ4
構建單用途金鑰時,複雜的程式(如rsync(1)、tar(1)、mysqldump(1)等)需要高階方法。對於它們,-v選項可以準確地顯示傳遞給伺服器的內容,以便正確設定sudoers。這樣,它們就可以被限制為僅訪問指定的檔案系統部分。例如,以下是ssh -v從rsync(1)的特定使用方式中顯示的內容,注意“Sending command”行
$ rsync -e 'ssh -v' fred@server.example.org:/etc/ ./backup/etc/
. . .
debug1: Sending command: rsync --server --sender -e.LsfxC . /etc/
. . .
然後可以將該輸出新增到sudoers中,以便金鑰只能執行該功能。
%backup ALL=(root:root) NOPASSWD: /usr/bin/rsync --server --sender -e.LsfxC . /etc/
然後,將所有這些結合在一起,帳戶“backup”需要一個金鑰
$ grep '^command' ~/.ssh/authorized_keys
command="/usr/bin/rsync --server --sender -e.LsfxC . /etc/" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMm0rs4eY8djqBb3dIEgbQ8lmdlxb9IAEuX/qFCTxFgb
這些程式中的許多都有一個---dry-run或等效選項。在確定正確設定時,請記住使用它。
對金鑰的只讀訪問
[edit | edit source]在某些情況下,需要防止帳戶更改自己的身份驗證金鑰。但是,這種情況可能更適合使用證書。但是,如果使用金鑰完成,則可以透過將金鑰檔案放在使用者具有隻讀訪問許可權的外部目錄中來實現,包括對目錄和金鑰檔案的訪問許可權。然後,AuthorizedKeysFile指令指定sshd(8)查詢金鑰的位置,並可以指向金鑰的安全位置,而不是預設位置。
一個很好的替代位置可能是新目錄/etc/ssh/authorized_keys,它可以儲存所選帳戶的金鑰檔案。可以透過將設定放在Match指令下,使更改僅應用於一組帳戶。大多數系統上的預設金鑰位置通常是~/.ssh/authorized_keys。
Match Group sftpusers
AuthorizedKeysFile /etc/ssh/authorized_keys/%u
然後,那裡的許可權將允許讀取金鑰,但不能寫入金鑰
$ ls -dhln /etc/ssh/
drwxr-x--x 3 0 0 4.0K Mar 30 22:16 /etc/ssh/authorized_keys/
$ ls -dhln /etc/ssh/*.pub
-rw-r--r-- 1 0 0 173 Mar 23 13:34 /etc/ssh/fred
-rw-r--r-- 1 0 0 93 Mar 23 13:34 /etc/ssh/user1
-rw-r--r-- 1 0 0 565 Mar 23 13:34 /etc/ssh/user2
. . .
金鑰甚至可以位於子目錄中,但許可權和所有權的相同限制適用。
對於chroot的SFTP,該方法與將金鑰檔案置於帳戶無法訪問的範圍內的相同
Match Group sftpusers
ChrootDirectory /home
ForceCommand internal-sftp -d %u
AuthorizedKeysFile /etc/ssh/authorized_keys/%u
當然,Match指令不是必需的。可以透過將指令放在伺服器配置檔案的主要部分中,使設定應用於所有帳戶。
將公鑰標記為已撤銷
[edit | edit source]金鑰可以被撤銷。已撤銷的金鑰可以儲存在/etc/ssh/revoked_keys中,這是一個在sshd_config(5)中使用RevokedKeys指令指定的 檔案,以便sshd(8)阻止嘗試使用它們登入。如果嘗試使用被撤銷的金鑰,客戶端側不會發出任何警告或錯誤。身份驗證將簡單地繼續進行到下一個金鑰或方法。
被撤銷的金鑰檔案應包含一個已撤銷的公鑰列表,每行一個,這些金鑰不再可用於連線到伺服器。金鑰不能包含任何額外的內容,例如登入選項,否則它將被忽略。如果在登入嘗試期間嘗試使用被撤銷的金鑰之一,伺服器將簡單地忽略它並繼續進行下一個身份驗證方法。將記錄嘗試的日誌條目,包括金鑰的指紋。有關此內容的更多資訊,請參見有關記錄的部分。
RevokedKeys /etc/ssh/revoked_keys
RevokedKeys配置指令在sshd_config(5)中預設情況下未設定。如果要使用它,則必須顯式設定它。這又是一種情況,可能更適合透過使用證書來完成,因為證書可以設定任何秒、分鐘、小時、天或周的有效期組合,而金鑰則無限期有效。
金鑰撤銷列表
[edit | edit source]金鑰撤銷列表(KRL)是表示已撤銷金鑰和證書的緊湊二進位制形式。為了使用 KRL,伺服器的配置檔案必須使用RevokedKeys指令指向一個有效的列表。KRL 本身是用ssh-keygen(1)生成的,可以從頭開始建立或就地編輯。這裡,建立了一個新的 KRL,其中包含一個公鑰
$ ssh-keygen -kf /etc/ssh/revoked_keys -z 1 ~/.ssh/old_key_rsa.pub
這裡,透過新增-u選項來更新現有 KRL
$ ssh-keygen -ukf /etc/ssh/revoked_keys -z 2 ~/.ssh/old_key_dsa.pub
KRL 就位後,就可以測試特定金鑰或證書是否在撤銷列表中。
$ ssh-keygen -Qf /etc/ssh/revoked_keys ~/.ssh/old_key_rsa.pub
僅公鑰和證書將被載入到 KRL 中。損壞或損壞的金鑰將不會被載入,如果嘗試載入,將產生錯誤訊息。與常規RevokedKeys列表一樣,目標為 KRL 的公鑰不能包含任何額外的內容,例如登入選項,否則在嘗試將其載入到 KRL 或在 KRL 中搜索它時會產生錯誤。
透過指紋驗證主機金鑰
[edit | edit source]以上示例都是關於使用金鑰對客戶端進行伺服器身份驗證。金鑰使用的另一個上下文是伺服器向客戶端標識自身,這會在每個非多路複用會話開始時自動發生。為了進行這種標識,客戶端會從伺服器獲取一個公鑰,通常是在第一次聯絡之前或之前,它可以隨後使用該公鑰來確保它再次連線到同一個伺服器,而不是冒充者。在客戶端上儲存這些獲取的主機金鑰的預設位置是/etc/ssh/ssh_known_hosts(如果由系統管理員管理),或者~/.ssh/known_hosts(如果由客戶端自己的帳戶管理)。內容的格式是包含主機地址及其匹配公鑰的一行。該檔案在sshd(8)手冊頁的“SSH_KNOWN_HOSTS FILE FORMAT”部分中進行了詳細描述。
首次連線到遠端主機時,應驗證伺服器的主機金鑰,以確保客戶端連線到的是正確的機器,而不是冒名頂替者或其他任何東西。通常,此驗證是透過比較伺服器主機金鑰的指紋來完成的,而不是嘗試比較整個金鑰本身。預設情況下,如果客戶端在known_hosts登錄檔中未找到金鑰,則客戶端將顯示指紋。
$ ssh -l fred server.example.org
The authenticity of host 'server.example.org (192.0.32.10)' can't be established.
ECDSA key fingerprint is SHA256:LPFiMYrrCYQVsVUPzjOHv+ZjyxCHlVYJMBVFerVCP7k.
Are you sure you want to continue connecting (yes/no)?
這可以與透過非頻寬方式接收到的指紋進行比較,例如透過郵件、電子郵件、簡訊、快遞等。具體來說,該示例將金鑰的指紋表示為 base64 編碼的 SHA256 校驗和。這是預設樣式。指紋也可以顯示為十六進位制的 MD5 雜湊,方法是將客戶端的FingerprintHash配置指令作為執行時引數傳遞,或者在ssh_config(5)中進行設定。
$ ssh -o FingerprintHash=md5 host.example.org
The authenticity of host 'host.example.org (192.0.32.203)' can't be established.
RSA key fingerprint is MD5:10:4a:ec:d2:f1:38:f7:ea:0a:a0:0f:17:57:ea:a6:16.
Are you sure you want to continue connecting (yes/no)?
但新版本中的預設值是 base64 中的 SHA256,它具有較低的碰撞機率。
在 OpenSSH 6.7 及更早版本中,客戶端將指紋顯示為十六進位制 MD5 校驗和,而不是當前使用的 base64 編碼的 SHA256 校驗和
$ ssh -l fred server.example.org
The authenticity of host 'server.example.org (192.0.32.10)' can't be established.
RSA key fingerprint is 4a:11:ef:d3:f2:48:f8:ea:1a:a2:0d:17:57:ea:a6:16.
Are you sure you want to continue connecting (yes/no)?
比較金鑰的另一種方法是使用 ASCII 藝術視覺化主機金鑰。有關詳細資訊,請參閱下文。
儘管通常在 SSH 客戶端首次嘗試連線時會顯示主機金鑰以供檢視,但也可以使用 ssh-keyscan(1) 隨時按需獲取金鑰。
$ ssh-keyscan host.example.org
# host.example.org SSH-2.0-OpenSSH_8.2
host.example.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLC2PpBnFrbXh2YoK030Y5JdglqCWfozNiSMjsbWQt1QS09TcINqWK1aLOsNLByBE2WBymtLJEppiUVOFFPze+I=
# host.example.org SSH-2.0-OpenSSH_8.2
host.example.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9iViojCZkcpdLju7/3+OaxKs/11TAU4SuvIPTvVYvQO32o4KOdw54fQmd8f4qUWU59EUks9VQNdqf1uT1LXZN+3zXU51mCwzMzIsJuEH0nXECtUrlpEOMlhqYh5UVkOvm0pqx1jbBV0QaTyDBOhvZsNmzp2o8ZKRSLCt9kMsEgzJmexM0Ho7v3/zHeHSD7elP7TKOJOATwqi4f6R5nNWaR6v/oNdGDtFYJnQfKUn2pdD30VtOKgUl2Wz9xDNMKrIkiM8Vsg8ly35WEuFQ1xLKjVlWSS6Frl5wLqmU1oIgowwWv+3kJS2/CRlopECy726oBgKzNoYfDOBAAbahSK8R
# host.example.org SSH-2.0-OpenSSH_8.2
host.example.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDDOmBOknpyJ61Qnaeq2s+pHOH6rdMn09iREz2A/yO2m
獲取金鑰後,可以使用 ssh-keygen(1) 顯示其指紋。這可以透過管道直接完成。
$ ssh-keyscan host.example.org | ssh-keygen -lf -
# host.example.org SSH-2.0-OpenSSH_8.2
# host.example.org SSH-2.0-OpenSSH_8.2
# host.example.org SSH-2.0-OpenSSH_8.2
256 SHA256:sxh5i6KjXZd8c34mVTBfWk6/q5cC6BzR6Qxep5nBMVo host.example.org (ECDSA)
3072 SHA256:hlPei3IXhkZmo+GBLamiiIaWbeGZMqeTXg15R42yCC0 host.example.org (RSA)
256 SHA256:ZmS+IoHh31CmQZ4NJjv3z58Pfa0zMaOgxu8yAcpuwuw host.example.org (ED25519)
如果伺服器上可用的公鑰型別超過一種,則 ssh-keyscan(1) 將獲取每個型別。如果透過stdin或檔案提供的金鑰超過一個,則ssh-keygen(1) 將按順序處理它們。在 OpenSSH 7.2 之前,手動指紋識別是一個兩步過程,金鑰被讀入檔案,然後對其進行指紋識別處理。
$ ssh-keyscan -t ed25519 host.example.org > key.pub
# host.example.org SSH-2.0-OpenSSH_6.8
$ ssh-keygen -lf key.pub
256 SHA256:ZmS+IoHh31CmQZ4NJjv3z58Pfa0zMaOgxu8yAcpuwuw host.example.org (ED25519)
請注意,ssh-keyscan(1) 的一些輸出被髮送到stderr而不是stdout。
可以在具有 awk(1)、sed(1) 和 xxd(1) 的系統上使用 awk(1)、sed(1) 和 xxd(1) 手動生成雜湊或指紋。
$ awk '{print $2}' key.pub | base64 -d | md5sum -b | sed 's/../&:/g; s/: .*$//'
$ awk '{print $2}' key.pub | base64 -d | sha256sum -b | sed 's/ .*$//' | xxd -r -p | base64
如果主機名以明文形式儲存,而不是以雜湊形式儲存,則可以從檔案中找到所有具有與known_hosts中的金鑰不同或新的金鑰的主機。
$ ssh-keyscan -t rsa,ecdsa -f ssh_hosts | \
sort -u - ~/.ssh/known_hosts | \
diff ~/.ssh/known_hosts -
實用程式 ssh-keyscan(1) 不解析 ssh_config(5)。這部分是為了保持程式碼庫簡單。有很多配置選項很難實現,包括但不限於ProxyJump、ProxyCommand、Match、BindInterface和CanonicalizeHostname[8] 。可以透過將實用程式包裝在簡短的 shell 函式中來解析客戶端配置檔案中的主機名
my-ssh-keyscan() {
for host in "$@" ; do
ssh-keyscan $(ssh -G "$host" | awk '/^hostname/ {print $2}')
done
}
該 shell 函式使用 ssh(1) 的-G選項使用 ssh_config(5) 解析每個主機名,然後檢查結果主機名以獲取 SSH 金鑰。
可以與 SHA256 base64 指紋一起顯示金鑰的 ASCII 藝術表示形式
$ ssh-keygen -lvf key
256 SHA256:BClQBFAGuz55+tgHM1aazI8FUo8eJiwmMcqg2U3UgWU www.example.org (ED25519)
+--[ED25519 256]--+
|o+=*++Eo |
|+o .+.o. |
|B=.oo. . |
|*B.=.o . |
|= B * S |
|. .@ . |
| +..B |
| *. o |
| o.o. |
+----[SHA256]-----+
在 OpenSSH 6.7 及更早版本中,指紋以 MD5 十六進位制形式顯示。
$ ssh-keygen -lvf key
2048 37:af:05:99:e7:fb:86:6c:98:ee:14:a6:30:06:bc:f0 www.example.net (RSA)
+--[ RSA 2048]----+
| o |
| o . |
| o o |
| o + |
| . . S |
| E .. |
| .o.* .. |
| .*=.+o |
| ..==+. |
+-----------------+
可以透過比較 base64 編碼的 SHA256 指紋,驗證客戶端或伺服器上的金鑰是否與已知的良好金鑰匹配。
有時需要比較兩個不確定的金鑰檔案,以檢查它們是否屬於同一金鑰對。但是,公鑰或多或少是可丟棄的。因此,在客戶端機器上的此類情況下,簡單的做法是重新命名或刪除舊的、有問題的公鑰,並用從現有私鑰生成的新的公鑰替換它。
$ ssh-keygen -y -f ~/.ssh/my_key_rsa
但是,如果必須真正比較這兩個部分,則使用 ssh-keygen(1) 分兩步進行。首先,從已知的私鑰重新生成一個新的公鑰,並將其用於生成指紋到stdout。接下來,生成未知公鑰的指紋以進行比較。在此示例中,比較了私鑰my_key_a_rsa和公鑰my_key_b_rsa.pub
$ ssh-keygen -y -f my_key_a_rsa | ssh-keygen -l -f -
$ ssh-keygen -l -f my_key_b_rsa.pub
結果是每個金鑰的 base64 編碼的 SHA256 校驗和,其中一個指紋顯示在另一個指紋下方,便於直觀比較。舊版本不支援從stdin讀取,因此此時將需要中間檔案。更舊的版本只會顯示每個金鑰的 MD5 校驗和。無論哪種方式,使用 shell 指令碼進行自動化都足夠簡單,但超出了本書的範圍。
首次連線時,必須對伺服器的主機金鑰進行可靠的驗證。可能需要聯絡系統管理員,他們可以非頻寬方式提供它,以便預先知道指紋並準備驗證第一次連線。
以下是如何讀取伺服器的 RSA 金鑰並將其指紋顯示為 SHA256 base64 的示例
$ ssh-keygen -lf /etc/ssh/ssh_host_rsa_key.pub
3072 SHA256:hlPei3IXhkZmo+GBLamiiIaWbeGZMqeTXg15R42yCC0 root@server.example.net (RSA)
這裡讀取了相應的 ECDSA 金鑰,但顯示為 MD5 十六進位制雜湊
$ ssh-keygen -E md5 -lf /etc/ssh/ssh_host_ecdsa_key.pub
256 MD5:ed:d2:34:b4:93:fd:0e:eb:08:ee:b3:c4:b3:4f:28:e4 root@server.example.net (ECDSA)
在 6.8 之前,指紋表示為 MD5 十六進位制雜湊
$ ssh-keygen -lf /etc/ssh/ssh_host_rsa_key.pub
2048 MD5:e4:a0:f4:19:46:d7:a4:cc:be:ea:9b:65:a7:62:db:2c root@server.example.net (RSA)
也可以使用 ssh-keyscan(1) 從活動 SSH 伺服器獲取金鑰。但是,仍然需要非頻寬方式驗證指紋。
如果伺服器的金鑰與客戶端在系統或本地帳戶的authorized_keys檔案中找到的記錄不匹配,則客戶端將發出警告,並附帶可疑金鑰的指紋。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that a host key has just been changed. The fingerprint for the RSA key sent by the remote host is SHA256:GkoIDP/d0I6KA9IQyOB9iqL+Rzpxx9LhlSJPCEfjVQ4. Please contact your system administrator. Add correct host key in /home/fred/.ssh/known_hosts to get rid of this message. Offending RSA key in /home/fred/.ssh/known_hosts:19 remove with: ssh-keygen -f "/home/fred/.ssh/known_hosts" -R "server.example.com" RSA host key for server.example.com has changed and you have requested strict checking. Host key verification failed.
警告有三個常見原因。
一個原因是伺服器的金鑰被替換,通常是因為伺服器的作業系統被重新安裝而沒有備份舊金鑰。另一個原因可能是系統管理員已淘汰過時的或已洩露的金鑰。但是,可以更好地計劃這種情況,如果時間允許,可以將新的金鑰新增到伺服器並讓客戶端使用UpdateHostKeys選項,以便在舊金鑰匹配的情況下接受新金鑰。第三種情況是連線到錯誤的機器,例如,當遠端系統由於動態地址分配而更改 IP 地址時。
在所有三種情況下,金鑰已更改,只有一件事要做:聯絡系統管理員並驗證金鑰。詢問 OpenSSH-server 是否最近重新安裝,或者機器是否從舊備份恢復?請記住,在某些情況下,系統管理員可能是您自己。
相當罕見但足夠嚴重以至於應該確定排除的情況是,錯誤的機器是中間人攻擊的一部分。
在所有四種情況下,可以透過任何可能驗證訊息完整性和來源的方法獲取真實的金鑰指紋,例如透過 PGP 簽名的電子郵件。如果可以進行物理訪問,則使用控制檯獲取正確的指紋。獲取真實的金鑰指紋後,返回到收到錯誤的客戶端機器,從~/.ssh/known_hosts中刪除舊金鑰
$ ssh-keygen -R server.example.org
然後嘗試登入,但首先比較金鑰指紋,如果並且只有如果金鑰指紋與您非頻寬方式收到的指紋匹配,則繼續操作。如果金鑰指紋匹配,則繼續登入過程,金鑰將被自動新增。如果金鑰指紋不匹配,立即停止並弄清楚您正在連線到的內容。最好用真正的電話,而不是電腦電話,與遠端機器的系統管理員或網路管理員聯絡。
可以透過使用模式匹配,或者簡單地列出同一金鑰的多個系統,讓多個主機名或 IP 地址在known_hosts檔案中使用相同的金鑰。這可以在/etc/ssh/ssh_known_hosts中的全域性金鑰列表以及每個帳戶的~/.ssh/known_hosts檔案中每個帳戶特定的本地金鑰列表中完成。實驗室、計算叢集和類似的機器池可以使用這種方式使用金鑰。這裡是一個由三個特定主機共享的金鑰,透過名稱識別
server1,server2,server3 ssh-rsa AAAAB097y0yiblo97gvl...jhvlhjgluibp7y807t08mmniKjug...==
或者可以使用萬用字元在/etc/ssh/ssh_known_hosts或~/.ssh/known_hosts中指定一個範圍。
172.19.40.* ssh-rsa AAAAB097y0yiblo97gvl...jhvlhjgluibp7y807t08mmniKjug...==
相反,對於同一個地址的多個金鑰,需要在 /etc/ssh/ssh_known_hosts 或 ~/.ssh/known_hosts 中為每個金鑰建立多個條目。
server1 ssh-rsa AAAAB097y0yiblo97gvljh...vlhjgluibp7y807t08mmniKjug...== server1 ssh-rsa AAAAB0liuouibl kuhlhlu...qerf1dcw16twc61c6cw1ryer4t...== server1 ssh-rsa AAAAB568ijh68uhg63wedx...aq14rdfcvbhu865rfgbvcfrt65...==
因此,為了讓伺服器池共享金鑰池,必須手動將每個伺服器-金鑰組合新增到 known_hosts 檔案中。
server1 ssh-rsa AAAAB097y0yiblo97gvljh...07t8mmniKjug...== server1 ssh-rsa AAAAB0liuouibl kuhlhlu...qerfw1ryer4t...== server1 ssh-rsa AAAAB568ijh68uhg63wedx...aq14rvcfrt65...== server2 ssh-rsa AAAAB097y0yiblo97gvljh...07t8mmniKjug...== server2 ssh-rsa AAAAB0liuouibl kuhlhlu...qerfw1ryer4t...== server2 ssh-rsa AAAAB568ijh68uhg63wedx...aq14rvcfrt65...==
雖然升級到證書可能比手動更新大量金鑰更合適。
可以使用 HostKeyAlias 手動指向正確的金鑰,可以作為 ssh_config(5) 的一部分,也可以作為執行時引數。在這裡,機器 Foobar 的金鑰用於連線到主機 192.168.11.15。
$ ssh -o StrictHostKeyChecking=accept-new \
-o HostKeyAlias=foobar \
192.168.11.15
這在以下情況下非常有用:DHCP 未配置為嘗試長時間為相同機器保留相同地址,或者使用某些 stdio 轉發方法透過中間主機傳遞。
從 OpenSSH 6.8[9] 及更高版本開始,OpenSSH 就包含了一個協議擴充套件,可以將弱的公鑰從 known_hosts 中輪換出來。使用它,伺服器能夠向客戶端通知其所有主機金鑰,並在至少有一個已知受信任金鑰的情況下,使用新的金鑰更新 known_hosts。這種方法仍然需要私鑰對伺服器可用 [10],以便完成證明。在 ssh_config(5) 中,指令 UpdateHostKeys 指定客戶端是否應該在身份驗證完成後接受伺服器的附加主機金鑰更新,並將它們新增到 known_hosts 中。在伺服器刪除過時的金鑰之前,可以為一段時間內提供多個相同型別的金鑰,從而允許自動選項用於輪換金鑰,以及從較弱的演算法升級到更強的演算法。有關金鑰管理標準,請參見 RFC 4819:安全 Shell 公鑰子系統。
- ↑ "安全 Shell (SSH) 身份驗證協議". IETF. 2006. 檢索於 2015-05-06.
- ↑ Steve Friedl (2006-02-22). "SSH 代理轉發圖解". Unixwiz.net. 檢索於 2013-04-27.
- ↑ Daniel Robbins (2002-02-01). "共同主題:OpenSSH 金鑰管理,第 3 部分". IBM. 檢索於 2013-04-27.
- ↑ Vincent Bernat (2020-04-05). "更安全的 SSH 代理轉發". 檢索於 2020-10-04.
- ↑ "管理多個 SSH 代理". 維基媒體. 檢索於 2020-04-07.
- ↑ Damien Miller (2021-12-16). "SSH 代理限制". OpenSSH. 檢索於 2022-03-06.
- ↑ "OpenSSH 中的基礎 U2F/FIDO 支援". OpenBSD-Tech 郵件列表. 2019-11-14. 檢索於 2021-03-24.
- ↑ Miller, Damien (2023-03-01). "為什麼 ssh-keyscan 不使用 .ssh/config?". mindrot.org. https://lists.mindrot.org/pipermail/openssh-unix-dev/2023-March/040605.html. 檢索於 2023-03-01.
- ↑ Damien Miller (2015-02-01). "OpenSSH 6.8+ 中的金鑰輪換". DJM's 個人部落格. 檢索於 2016-03-05.
- ↑ Damien Miller (2015-02-17). "主機金鑰輪換,再談". DJM's 個人部落格. 檢索於 2016-03-05.