SSH金鑰登入流程分析

jaychen發表於2017-12-07

本文首發於 https://jaychen.cc

作者 jaychen

SSH金鑰登入流程分析

寫一篇短文,介紹 ssh 金鑰登入遠端伺服器流程和注意事項。

登入流程

金鑰登入比密碼登入安全,主要是因為他使用了非對稱加密,登入過程中需要用到金鑰對。整個登入流程如下:

  1. 遠端伺服器持有公鑰,當有使用者進行登入,伺服器就會隨機生成一串字串,然後傳送給正在進行登入的使用者。
  2. 使用者收到遠端伺服器發來的字串,使用與遠端伺服器公鑰配對的私鑰對字串進行加密,再傳送給遠端伺服器。
  3. 伺服器使用公鑰對使用者發來的加密字串進行解密,得到的解密字串如果與第一步中傳送給客戶端的隨機字串一樣,那麼判斷為登入成功。

整個登入的流程就是這麼簡單,但是在實際使用 ssh 登入中還會碰到一些小細節,這裡演示一遍 ssh 遠端登入來展示下這些細節問題。

生成金鑰對

使用 ssh-keygen 就可以直接生成登入需要的金鑰對。ssh-keygen 是 Linux 下的命令,不新增任何引數就可以生成金鑰對。

➜  ~ ssh-keygen
Generating public/private rsa key pair.

Enter file in which to save the key (/home/jaychen/.ssh/id_rsa):   #1
Enter passphrase (empty for no passphrase):                        #2
Enter same passphrase again:                                       #3
複製程式碼

執行 ssh-keygen 會出現如上的提示,在 #1 處這裡提示使用者輸入生成的私鑰的名稱,如果不填,預設私鑰儲存在 /home/jaychen/.ssh/id_rsa 檔案中。這裡要注意兩點:

  • 生成的金鑰,會放在執行 ssh-keygen 命令的使用者的家目錄下的 .ssh 資料夾中。即 $HOME/.ssh/ 目錄下。
  • 生成的公鑰的檔名,通常是私鑰的檔名後面加 .pub 的字尾。

#2 處,提示輸入密碼,注意這裡的密碼是用來保證私鑰的安全的。如果填寫了密碼,那麼在使用金鑰進行登入的時候,會讓你輸入密碼,這樣子保證瞭如果私鑰丟失了不至於被惡意使用。話是這麼說,但是平時使用這裡我都是直接略過。

#3 是重複 #2 輸入的密碼,這裡就不廢話了。

生成金鑰之後,就可以在 /home/jaychen/.ssh/ 下看到兩個檔案了(我這裡會放在 /home/jaychen 下是因為我使用 jaychen 使用者來執行 ssh-keygen 命令)

➜  .ssh ls
total 16K
drwx------ 2 jaychen jaychen 4.0K 12月  7 17:57 .
drwx------ 9 jaychen jaychen 4.0K 12月  7 18:14 ..
-rw------- 1 jaychen jaychen 1.7K 12月  7 17:57 id_rsa.github
-rw-r--r-- 1 jaychen jaychen 390 12月  7 17:57 id_rsa.github.pub

複製程式碼

生成的私鑰還要注意一點:私鑰的許可權應該為 rw-------,如果私鑰的許可權過大,那麼私鑰任何人都可以讀寫就會變得不安全。ssh 登入就會失敗。

首次 ssh 登入

登入遠端伺服器的命令是

ssh 登入使用者@伺服器ip
複製程式碼

這裡開始要注意兩個使用者的概念:

  • 本地執行這條命令的使用者,即當前登入使用者,我這裡演示的使用者名稱稱是 jaychen。
  • 要登入到遠端伺服器的使用者。

在開始登入之前,我們要首先要把生成公鑰上傳到伺服器。

公鑰的內容要儲存到要登入的使用者的家目錄下的 .ssh/authorized_keys 檔案中。假設你之後要使用 root 使用者登入遠端伺服器,那麼公鑰的內容應該是儲存在 /root/.ssh/authorized_keys中。注意 authorized_keys 檔案是可以儲存多個公鑰資訊的,每個公鑰以換行分開。

上傳完畢之後,執行

ssh root@遠端伺服器 ip
複製程式碼

這個時候,如上面說的,遠端伺服器會傳送一段隨機字串回來,這個時候需要使用私鑰對字串進行加密。而這個私鑰會去執行該命令的使用者的家目錄下的 .ssh 目錄讀取私鑰檔案,預設私鑰檔案為 id_rsa 檔案。即 $HOME/.ssh/id_rsa 檔案。假設在生成金鑰的時候對私鑰進行了加密,那麼這個時候就需要輸入密碼。

上面的流程使用者登入的時候是不會感知的,ssh 在背後完成了所有的校驗操作,如果金鑰匹配的話,那麼使用者就可以直接登入到遠端伺服器,但是如果是首次登入的話,會出現類似下面的提示:

➜  .ssh ssh root@192.168.1.1
The authenticity of host '192.168.1.1 (192.168.1.1)' can't be established.
ECDSA key fingerprint is SHA256:61U/SJ4n/QdR7oKT2gaHNuGxhx98saqMfzJnzA1XFZg.
Are you sure you want to continue connecting (yes/no)?
複製程式碼

這句話的意思是,遠端伺服器的真實身份無法校驗,只知道公鑰指紋(公鑰的 MD5 值)為 61U/SJ4n/QdR7oKT2gaHNuGxhx98saqMfzJnzA1XFZg,是否真的要建立連線。出現上面的提示是因為避免存在中間人攻擊

中間人攻擊

中間人攻擊的前提是,你第一次登入一臺遠端伺服器,你除了使用者名稱、使用者名稱對應的公鑰私鑰以及伺服器 ip 之外,對遠端伺服器絲毫不瞭解的情況下。假設你 ssh 遠端登入 192.168.1.1 的遠端主機,在連線過程中被第三者攔截,第三者假冒自己為 192.168.1.1 的主機,那麼你就會直接連線到其他人的伺服器上。這就是中間人攻擊。

為了避免中間人攻擊,ssh 在首次登入的時候會返回公鑰指紋,使用者需要自己手動去比對你要登入的遠端伺服器的公鑰的公鑰指紋和 ssh 返回的公鑰指紋是否一樣

經過比較公鑰指紋,確認該伺服器就是你要登入的伺服器,輸入 yes 之後就可以成功登入。整個登入流程結束。

known_hosts 檔案

第一次登入之後,在本機的 $HOME/.ssh/ 目錄下就會生成一個 known_hosts 的檔案,內容類似下面

➜  .ssh cat known_hosts

192.168.1.1 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOPKYWolOYTDevvBR6GV0rFcI0z/DHZizN5l/ajApsgx+UcOOh51liuyBRRCIyF+BR56Le0lP0Pn6nzvLjbqMqg=
複製程式碼

這個檔案記錄了遠端主機 ip 和遠端主機對應的公鑰指紋,那麼在下次登入的時候,遠端主機傳送過來的公鑰指紋,直接和 known_hosts 檔案中對應 ip 的公鑰指紋比較即可。

config 配置

很多時候,我們開發可能需要連線多臺遠端伺服器,並且需要配置 git 伺服器的私鑰。那麼這麼多的伺服器不能共用一套私鑰,不同的伺服器應該使用不同的私鑰。但是我們從上面的連線流程可以看到,ssh 預設是去讀取 $HOME/.ssh/id_rsa 檔案作為私鑰登入的。如果想要不同的伺服器使用不同的私鑰進行登入,那麼需要在 .ssh 目錄下編寫 config 檔案來進行配置。

config 的配置很簡單,只要指明哪個使用者登入哪臺遠端伺服器需要使用哪個私鑰即可。下面給出一個配置示例。

Host github.com
	User jaychen
	IdentityFile ~/.ssh/id_rsa.github
Host 192.168.1.1
	User ubuntu
	IdentityFile ~/.ssh/id_rsa.xxx
複製程式碼

上面 config 檔案欄位含義如下:

  • Host 指明瞭遠端主機的 ip,除了使用 ip 地址,也可以直接使用網址。
  • User 指的是登入遠端主機的使用者。
  • IdentityFile 指明使用哪個私鑰檔案。

編寫好 config 檔案之後,需要把 config 檔案的許可權改為 rw-r--r--。如果許可權過大,ssh 會禁止登入。

相關文章