OpenSSH/Cookbook/公鑰認證
如果操作得當,認證金鑰可以提高效率。額外的好處是,密碼短語和私鑰永遠不會離開客戶端[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 檔案格式”部分給出。如果金鑰沒有標記,則可能難以匹配,這可能是您想要或不想要的。
可以在執行時指定金鑰,但為了節省一遍又一遍地重新輸入相同的路徑,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
在此示例中,首先嚐試較短的名稱,但當然可以改為使用不太模稜兩可的快捷方式。配置檔案按照首次匹配的方式解析。因此,最具體的規則放在開頭,最通用的規則放在結尾。
使用加密主目錄時,金鑰必須儲存在未加密的目錄中。這意味著儲存在實際主目錄之外的某個地方,這意味著 sshd(8) 需要進行適當的配置才能在該特殊位置找到金鑰。
以下是一種解決訪問問題的方法。每個使用者在 /etc/ssh/keys/ 下都有一個子目錄,他們可以使用該子目錄儲存其 authorized_keys 檔案。這在伺服器的配置檔案 /etc/ssh/sshd_config 中設定
AuthorizedKeysFile /etc/ssh/keys/%u/authorized_keys
為金鑰設定特殊位置為金鑰的管理方式提供了更多可能性,並且如果它們用空格分隔,則可以指定多個金鑰檔案位置。使用者不必對 authorized_keys 檔案擁有寫入許可權。只需讀取許可權即可登入。但是,如果使用者被允許新增、刪除或更改其金鑰,則他們需要對檔案具有寫入許可權才能執行此操作。
加密主目錄的一個症狀是,基於金鑰的身份驗證僅在您已經登入到同一個帳戶時有效,但在嘗試首次連線並首次登入時失敗。
有時還需要在身份驗證後立即從 /etc/ssh/sshrc 新增指令碼或呼叫程式以解密主目錄。
允許無密碼登入的一種方法是按照上述步驟操作,但在建立金鑰時,在提示輸入密碼時,只需不輸入密碼即可。請注意,使用沒有密碼的金鑰非常危險,因此金鑰檔案應該受到很好的保護並進行跟蹤。這包括它們只能用作下面描述的單用途金鑰。及時的金鑰輪換變得尤為重要。一般來說,最好不要建立沒有密碼的金鑰。更好的解決方案是擁有密碼並結合使用身份驗證代理和單用途金鑰。如今,大多數桌面環境都會自動啟動 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 使用代理的基於金鑰的身份驗證]。
雖然使用者應該為他們的金鑰設定強密碼,但無法強制或驗證這一點。實際上,由於私鑰及其密碼從未離開客戶端機器,伺服器無法對此有任何影響。相反,可以同時要求金鑰和密碼。從 OpenSSH 6.2 開始,伺服器可以使用 AuthenticationMethods 指令要求使用多個身份驗證方法進行登入。
AuthenticationMethods publickey,password
此示例來自 http://man.openbsd.org/sshd_config.5 sshd_config(5)] 要求使用者首先使用金鑰進行身份驗證,只有在金鑰成功後才會查詢密碼。因此,使用該配置,如果不先使用有效的金鑰進行身份驗證,就無法訪問系統密碼提示符。更改引數的順序會更改身份驗證方法的順序。
從 OpenSSH 6.8 開始,伺服器現在會記住哪些公鑰已被用於身份驗證,並拒絕接受以前使用的金鑰。這允許設定要求使用者使用兩個不同的公鑰進行身份驗證,例如一個在檔案系統中,另一個在硬體令牌中。
AuthenticationMethods publickey,publickey
AuthenticationMethods 指令(無論是用於金鑰還是密碼),也可以在伺服器上的 Match 指令下設定,以僅應用於某些組或情況。
同樣從 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** 指令決定允許用於身份驗證的金鑰型別。
使用代理的基於金鑰的身份驗證
[edit | edit source]當使用身份驗證代理,例如 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)
金鑰會一直保留在代理中,直到代理停止執行,除非另有說明。可以在啟動代理本身時或使用 ssh-add(1) 實際載入金鑰時使用 **-t** 選項設定超時。在這兩種情況下,**-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** 中,從而為指定的宿主連線自動新增它們。
代理轉發
[edit | edit source]代理轉發是透過一個或多箇中間主機的一種方式。但是,**-J** 選項用於 **ProxyJump** 將是一個更安全的選項。請參閱跳轉主機部分中的 透過閘道器或兩個閘道器,瞭解有關該選項的資訊。使用代理轉發,中間機器會在客戶端和最終目的地之間來回轉發挑戰和響應。這存在一些風險,但也無需在這些中間機器上使用密碼或儲存金鑰。
代理轉發的主要優勢在於,私鑰本身不需要在任何遠端機器上,從而阻止了對私鑰的不必要的訪問。[3] 另一個優勢是,使用者已透過身份驗證的實際代理不會移動到任何地方,因此不易被分析。
代理的一個風險是,如果許可權允許,它們可以被重複使用以進行尾隨登入。金鑰不能以這種方式複製,但在許可權不正確時,身份驗證是可能的。請注意,停用代理轉發不會提高安全性,除非使用者也被拒絕 shell 訪問許可權,因為他們始終可以安裝自己的轉發器。
可以透過在將金鑰新增到代理時新增 **-c** 選項來確認金鑰的每次使用,從而減輕代理轉發的風險。這需要 SSH_ASKPASS 變數設定並可用於代理程序,但將在每次遠端系統使用金鑰時在執行代理的主機上生成提示。因此,如果要透過一個或多箇中間主機,通常最好讓 SSH 客戶端使用帶有 **-W** 或 **-J** 的標準輸入轉發。
在客戶端方面,代理轉發預設情況下是停用的,因此如果要使用它,必須顯式啟用它。將以下行新增到 ssh_config(5) 中,以啟用特定伺服器的代理轉發
Host gateway.example.org
ForwardAgent yes
在伺服器端,預設配置檔案允許身份驗證代理轉發,因此要使用它,只需要在客戶端端配置即可。但是,再次強調,最好考慮使用 **ProxyJump** 替代方案。
舊樣式,相對安全的 SSH 代理轉發
[edit | edit source]透過一個或多箇中間主機的最佳方法是使用 **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) 中的“標記”部分,瞭解有關此類縮寫的更多資訊。
使用這些配置設定,身份驗證代理必須在啟動 SSH 客戶端之前已啟動並執行,並指向指定的套接字,以便該配置能夠正常工作。此外,它應該將套接字放置在任何其他帳戶都無法訪問的目錄中。 ssh-agent(1) 必須使用 **-a** 選項來命名套接字
$ ssh-agent -a /run/user/${UID}/ssh-cloud-01.socket
該代理配置可以手動啟動,也可以透過指令碼或服務管理器啟動。但是,為了保護隱私和安全性,應避免代理轉發。**ProxyJump** 配置指令是最佳替代方案,在較舊的系統上,使用 netcat 的 **ProxyCommand** 進行主機遍歷是更優選擇。再次強調,請參閱有關 代理和跳轉主機 的部分,瞭解這些方法的使用方式。
從 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。
在金鑰的情況下,必須認真維護 known_hosts 列表[6],可能需要藉助 UpdateHostkeys 和 CanonicalizeHostname 客戶端配置指令。使用證書需要代理只需要知道證書頒發機構 (CA) 即可。
再次,請參閱跳躍主機部分的 透過閘道器或兩個閘道器,瞭解一種無需轉發 SSH 代理即可透過一個或多箇中間機器的方法。
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 時,指定的金鑰將被新增到代理中,並保持可用。
雖然獨立金鑰已經存在很長時間了,但從 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]。因此,沒有隨附的硬體令牌,硬體支援的私鑰檔案是無用的。這也意味著這些金鑰檔案無法跨硬體令牌移植,例如當擁有多個備用或備份令牌時,即使由同一帳戶使用。因此,當使用多個硬體令牌時,必須為每個令牌生成不同的金鑰對。
可以在令牌本身記憶體儲私鑰,但目前無法直接從令牌內部使用,必須先將其儲存為檔案。此外,金鑰只能在建立時使用 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
由於輸出檔名是固定的,任何預先存在的具有該名稱的檔案都可能被覆蓋,但首先會有警告。但是,不建議將金鑰儲存在硬體令牌中,因為與單獨儲存相比,這樣做可以提供更多保護。
定製的單用途金鑰可以消除許多管理活動的遠端 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 選項。
最簡單的方法是編寫一個簡短的 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。這樣,它們可以被限制為僅訪問檔案系統中指定的區域。例如,下面是rsync(1)的一個特定使用情況,請注意“傳送命令”行。
$ 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
預設情況下,sshd_config(5)中沒有設定RevokedKeys配置指令。如果要使用它,必須顯式設定它。這是另一個情況,可能更適合透過使用證書來滿足,因為可以為證書設定任何秒、分鐘、小時、天或周的組合的有效期間隔,而金鑰的有效期無限。
金鑰撤銷列表
[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 檔案格式”部分中有詳細說明。
第一次連線到遠端主機時,應該驗證伺服器的主機金鑰,以確保客戶端連線到正確的機器,而不是冒名頂替者或其他任何東西。通常,這種驗證是透過比較伺服器主機金鑰的指紋來完成的,而不是嘗試比較金鑰本身。預設情況下,如果金鑰在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 校驗和。這是預設樣式。指紋也可以透過將客戶端的FingerprintHash配置指令作為執行時引數傳遞或在ssh_config(5)中設定它來以十六進位制顯示為 MD5 雜湊。
$ 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 藝術視覺主機金鑰。請參閱下面有關此內容的進一步介紹。
下載金鑰
[edit | edit source]即使通常會顯示主機金鑰以供 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 '{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 金鑰。
可以顯示金鑰的 ASCII 藝術表示形式以及 SHA256 base64 指紋。
$ 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] 及更高版本開始,就包含了一個用於將弱公開金鑰從 known_hosts 中移除的協議擴充套件。有了它,伺服器能夠向客戶端通告所有其主機金鑰,並在至少有一個已知的可信金鑰的情況下,使用新金鑰更新 known_hosts。此方法仍然要求伺服器提供私鑰[10],以便完成證明。在 ssh_config(5) 中,指令 UpdateHostKeys 指定了在身份驗證完成並將其新增到 known_hosts 之後,客戶端是否應該接受來自伺服器的額外主機金鑰更新。伺服器可以在一段時間內為同一型別的金鑰提供多個金鑰,然後從提供的金鑰中刪除已過時的金鑰,從而允許自動旋轉金鑰,以及從較弱的演算法升級到更強的演算法。另見 RFC 4819:安全外殼公開金鑰子系統 關於金鑰管理標準。
參考資料
[edit | edit source]- ↑ "安全外殼(SSH)身份驗證協議". IETF. 2006. Retrieved 2015-05-06.
- ↑ Steve Friedl (2006-02-22). "SSH 代理轉發圖解指南". Unixwiz.net. Retrieved 2013-04-27.
- ↑ Daniel Robbins (2002-02-01). "常見執行緒:OpenSSH 金鑰管理,第 3 部分". IBM. Retrieved 2013-04-27.
- ↑ Vincent Bernat (2020-04-05). "更安全的 SSH 代理轉發". Retrieved 2020-10-04.
- ↑ "管理多個 SSH 代理". Wikimedia. Retrieved 2020-04-07.
- ↑ Damien Miller (2021-12-16). "SSH 代理限制". OpenSSH. Retrieved 2022-03-06.
- ↑ "OpenSSH U2F/FIDO 支援在基礎中". OpenBSD-Tech 郵件列表. 2019-11-14. Retrieved 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. Retrieved 2023-03-01.
- ↑ Damien Miller (2015-02-01). "OpenSSH 6.8+ 中的金鑰輪換". DJM 的個人部落格. Retrieved 2016-03-05.
- ↑ Damien Miller (2015-02-17). "主機金鑰輪換,再談". DJM 的個人部落格. Retrieved 2016-03-05.