實作 SSH 金鑰登入:公鑰私鑰原理、ssh-keygen 產生金鑰,到連上 DigitalOcean 伺服器
上一篇講完雜湊與 Salt 之後,這一節終於要把這些知識實際用在 SSH 金鑰上:從公鑰私鑰的加密原理講起,實際產生一組金鑰,加進 DigitalOcean,最後真正登入伺服器。
SSH 背後的公開金鑰加密原理
SSH 採用的是公開金鑰加密(public key encryption,也稱公開金鑰密碼學)。一組 SSH 金鑰由兩個部分組成,稱為 SSH 金鑰對(key pair):
- 公鑰(public key):可以任意分享出去,貼在網路上、貼在社群媒體都沒關係
- 私鑰(private key):只有自己知道,絕不能外流
公鑰的作用是用來加密資料或訊息,可以把它想像成一種雜湊函式(或加了 Salt 的雜湊函式):任何人都可以用這把公鑰加密一段訊息傳給你,但只有擁有對應私鑰的人才能把它解密。這代表即使把訊息公開喊出來,除了持有私鑰的那個人,沒有人能看懂內容,這是相對於帳號密碼而言強大得多的驗證方式。
把這個概念套用在密碼上:如果密碼能用只有自己知道的私鑰運算,其他人即使看到加密後的內容,也完全無法還原出原始密碼,這正是 SSH 之所以遠比帳號密碼、甚至比生物辨識更強的原因。SSH 全名是 secure socket shell(安全外殼協定),代表的就是一種安全連線伺服器的方式。
實際連線時,私鑰永遠留在自己的電腦上,絕不外傳;即使真的需要備份,也頂多存放在安全的 USB 上,一般不建議這麼做,通常做法是需要新的裝置時,直接產生一組新的 SSH 金鑰,而不是把原本的私鑰複製來複製去。伺服器端同樣擁有自己的一組公鑰與私鑰,雙方傳送的每一則訊息都用對方的公鑰加密,也就是說,即使有人在通訊過程中攔截封包,看到的也只是一堆無意義的亂碼,因為沒有任何一方的私鑰在傳輸過程中外流。
產生 SSH 金鑰
依照慣例,SSH 金鑰放在家目錄(home directory)底下的 .ssh 目錄裡;檔名開頭的 . 代表這是隱藏檔案,可以用 ls -la 的 -a 參數查看。系統預設的私鑰檔名通常是 id_rsa,公鑰檔名則一定以 .pub 結尾。這個副檔名非常關鍵,只要檔名沒有 .pub,就絕對不能分享出去。
產生金鑰要用 ssh-keygen 這個工具:
bash
ssh-keygen執行時可以自訂金鑰檔名,例如取名為 fsfe(Full Stack Front End 的縮寫),方便辨識這是這門課專用的金鑰。過程中會被要求輸入密碼短語(passphrase):一般建議設定,這樣即使電腦被入侵、金鑰檔案外洩,攻擊者還是需要這組密碼短語才能使用私鑰,多一層保護。ssh-keygen 產生金鑰後,還會顯示一段用 SHA256 算出的指紋(fingerprint)與 randomart 圖形,這張圖本身沒有實際功能,純粹是讓人可以快速用肉眼辨識不同金鑰之間的差異。
金鑰產生後,可以用 cat 查看公鑰內容,會看到一串可以公開分享的文字;如果改看私鑰內容,會發現長度明顯長得多,因為私鑰牽涉到成本較高的非對稱加密(asymmetric cryptography)運算。這也是為什麼並非所有通訊都採用像 SHA256 這樣昂貴的加密方式:如果每秒要處理幾百萬次這種運算,會迅速耗盡運算資源,因此系統會依照使用情境,在對稱加密(symmetric cryptography)與非對稱加密之間做取捨。
SSH 金鑰有一個有趣的特性:即使把 .ssh 目錄裡的檔案清單全部秀出來也沒關係,因為檔名本身不包含任何機密資訊,這正是它遠優於過去那種把密碼直接存成一個叫 password 的檔案的年代。
為什麼要設定密碼短語
密碼短語存在的意義,是為了防範電腦本身被入侵、私鑰被竊取的情況,SSH 金鑰的安全性終究取決於它存放位置的安全性,這點與生物辨識加密不同。生物辨識的風險在於,一旦有心人士能取得你本人的指紋(甚至用脅迫手段),這道防線同樣會被突破,世界上不存在完美的安全機制。不過如果攻擊者已經入侵到能拿到本機私鑰檔案的程度,通常代表已經有更嚴重的問題,設定密碼短語終究只是多一道保險,是一個值得養成的好習慣。
把公鑰加進 DigitalOcean
金鑰產生好之後,下一步是把公鑰內容複製到 DigitalOcean。可以用 cat 搭配檔名裡的關鍵字、透過 grep 篩選出想要的金鑰檔案,快速在一堆檔案裡找到目標,再用 cat 顯示 .pub 檔案內容並手動複製;如果系統有安裝 pbcopy 這類剪貼簿工具也可以用,但為了盡量減少額外安裝的軟體,用滑鼠選取複製一樣可行。
回到瀏覽器,在建立 Droplet 時選擇 SSH key 作為身份驗證方式(而不是密碼),點選新增 SSH Key,把剛剛複製的公鑰貼上,並取一個名稱方便識別。接著可以視需要幫伺服器主機命名:伺服器數量少的時候名稱無關緊要,但如果同時管理幾百台伺服器,有沒有清楚標示這是 Node 伺服器、資料庫伺服器還是負載平衡器,差別會非常大,而且伺服器一旦命名之後要再改名往往很麻煩,命名這件事本身值得認真對待。
建立 Droplet 時偶爾會遇到某個資料中心(例如 SFO1)暫時沒有可用容量的狀況,這時候換一個資料中心(例如 SFO2)重新建立即可,作業系統一樣選擇 Ubuntu LTS、最陽春最便宜的方案,並選擇剛剛新增好的 SSH 金鑰。
登入伺服器
伺服器建立好之後會被分配一個 IP 位址,一開始的登入使用者一律是 root(後續步驟會建立專屬的一般使用者,不會長期使用 root 登入)。連線指令用 ssh,並用 -i 參數指定私鑰檔案路徑(如果私鑰檔名就是預設的 id_rsa,可以省略這個參數):
bash
ssh -i fsfe root@<伺服器IP>第一次連線時,系統會詢問是否要信任這台主機的身分(authenticity),輸入 yes 即可繼續。如果金鑰設定無誤,就會成功登入伺服器,成功用 SSH 金鑰完成了第一次登入。這整套流程剛開始操作起來會覺得步驟繁瑣,但熟悉之後很快就會變成不假思索的固定動作,後續也還有辦法讓這個流程變得更快。
登入之後會發現自己是以 root 身分在操作這台跑著 Ubuntu 的伺服器,而長期用 root 帳號操作其實並不恰當,什麼是 root、為什麼不該一直用它,會在下一節接著說明。
複習
SSH 金鑰對由哪兩個部分組成?
公鑰(可以公開分享出去)與私鑰(只有金鑰擁有者自己知道)。
SSH 金鑰的副檔名有什麼慣例?
私鑰沒有副檔名,公鑰一律以 .pub 結尾。
用什麼指令產生 SSH 金鑰?
ssh-keygen。
SSH 金鑰預設存放在哪裡?
使用者家目錄底下的 .ssh 目錄。
公開金鑰加密為什麼在通訊安全上特別強大?
任何人都可以用公鑰加密訊息,但只有持有對應私鑰的人才能解密,即使通訊過程被攔截,攔截者拿到的也只是無法解讀的加密內容。
小測驗
SSH 金鑰對由哪兩個部分組成?
公鑰與私鑰用什麼指令產生一組 SSH 金鑰對?
ssh-keygen使用者的 SSH 金鑰慣例上存放在哪裡?
家目錄底下的 .ssh 目錄此文章是 FrontendMasters 上的 Full Stack Fundamentals, v3 課程筆記
