深入瞭解SSH

學習:深入瞭解SSH

ssh

有商業和開源版本,其中openssh是開源中最流行的。

ssh歷史

1995 年 7 月, 芬蘭學者Tatu Ylonen 以免費軟件的形式將一套保護信息傳輸的程序(也就是 SSH )發佈出去。程序很快流行,到年底已經有兩萬用戶,遍佈五十國家。所以在年底時,他創立了 SSH 通信安全公司來繼續開發和銷售 SSH,所以它變成了專有軟件。在 1999 年,瑞典程序員基於 SSH 最後一個開源的版本 1.2.12 開發了 OSSH,之後 OpenBSD 開發者在 OSSH 的基礎上進行大量修改,形成了 OpenSSH,它是目前唯一一種最流行的 SSH 實現,成爲了所有操作系統的默認組件。

用法

生成密鑰

ssh-keygen

用生成、管理和轉換身份驗證密鑰。

參數說明:

  • -b bits 指定要創建的祕鑰中的位數,默認 2048 位,值越大,密碼越複雜
  • -C comment 註釋,在 id_rsa.pub 中末尾
  • -t rsa/dsa等 指定要創建的祕鑰類型,默認爲 RSA
  • -f filename 指定公私鑰的名稱,會在 $HOME/.ssh 目錄下生產私鑰 filename 和公鑰 filename.pub
  • -N password 指定使用祕鑰的密碼,使得多人使用同一臺機器時更安全

常用命令:

# 生成公私鑰,默認文件爲 ~/.ssh/id_rsa
ssh-keygen -t rsa -b 4096 -C "[email protected]"

id_rsa:私鑰
id_rsa.pub:公鑰

image-20231115133703251

管理私鑰

ssh-agent和ssh-add

用於管理私鑰,一般情況下會使用ssh-keygen生成的(沒有密碼的)私鑰作爲默認私鑰,此時沒有必要使用ssh-agent,當出現以下兩種情況時則需要使用:

  • 使用不同的私鑰連接不同的主機時,需要手動指定對應的私鑰。(ssh-agent能幫助選擇對應的私鑰進行認證)
  • 當私鑰設置了密碼時,又需要頻繁的使用私鑰進行認證。(ssh-agent能幫助免去重複輸入密碼)
# 啓動代理
eval `ssh-agent`
# 關閉代理
ssh-agent -k
# 在 ~/.bashrc 中加入以下來實現登陸自動啓動 ssh-agent,退出自動 kill 掉程序
eval $(ssh-agent -s) > /dev/null
trap 'test -n "$SSH_AGENT_PID" && eval `/usr/bin/ssh-agent -k` > /dev/null' 0

# 查看代理中的私鑰
ssh-add -l
# 查看代理中私鑰對應的公鑰
ssh-add -L
# 移除指定的私鑰
ssh-add -d /path/of/key/key_name
# 移除所有的私鑰
ssh-add -D

發送公鑰

ssh-copt-id

在登陸時,將公鑰放在(綁定)要登陸的服務器上。(雲服務器大都默認關閉密鑰登陸 PasswordAuthentication no

# 發送公鑰的兩種方式(等價)
ssh-copy-id -i ~/.ssh/id_rsa.pub user@host
ssh user@host 'mkdir -p .ssh && cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub

登陸

登陸配置

SSH 登陸服務器需要知道服務器的主機地址(主機名或主機 IP 地址),用戶名和密碼,有時還要指定端口號(默認 22 ),一般使用的登陸命令如下:

# 登陸目標服務器( 172.17.132.120 )
ssh -p 58422 [email protected]
# 通過跳板機登陸目標服務器( 172.17.132.120 )
ssh -p 58422 [email protected] ssh [email protected]
# 端口映射
ssh -p 58422 [email protected] -fNL 5433:172.17.132.120:5432 -N

通過設置$$HOME/.ssh/config$ ,可以使用別名直接登陸。

# 登陸目標服務器( 172.17.132.120 )
ssh target
# 通過跳板機登陸目標服務器( 172.17.132.120 )
ssh jump_target
# 端口映射
## 登陸時通過 LocalForward 配置
ssh jump_target
## 使用-L來實現本地端口映射
ssh -C -N -g -L 5433:127.0.0.1:5432 jump_target


# 通用配置,所有配置都使用
Host *
    AddKeysToAgent yes      # 將私鑰添加到ssh-agent中
    UseKeychain yes         # 保存密碼到agent中
    ServerAliveInterval 10  # 連接心跳間隔10s
    ServerAliveCountMax 3   # 重連次數爲3
# target配置
Host target
    HostName 172.17.132.120
    User user
    Port 58422
    IdentityFile ~/.ssh/id_rsa
# 跳板機配置
Host jumper
    HostName jumper.example.com
    User user
    Port 58422
    IdentityFile ~/.ssh/id_rsa
Host jump_target
    HostName 172.17.132.120
    User user
    Port 22
    IdentityFile ~/.ssh/id_rsa
    ProxyCommand ssh user@jumper -W %h:%p 2>/dev/null
    LocalForward 5433 localhost:5432    # 本地5433映射到jump_target的5432

一般在做遠程開發時,VS Code 的 Remote 插件會讀取本地的配置文件 $HOME/.ssh/config,所以可以在本地匹配好,直接登陸。

首次登陸

一般在 $HOME/.ssh 目錄下除了公私鑰文件,config 配置文件,authorized_keys 認證文件外,還有一個 known_hosts 文件, 這個文件記錄了遠程主機 ip 和遠程主機對應的公鑰指紋

image-20231115135713253

image-20231115140119642

我們在第一次登陸(密碼或祕鑰登陸)服務器時,會有如下的提示界面:

### SSH 首次登陸的提示
The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established.
ECDSA key fingerprint is SHA256:HosOqhcUmbB7QG81yCuDPkvxTgot+vpple+czXPrEug.
ECDSA key fingerprint is MD5:fd:d7:e1:2c:42:4e:b4:2d:a3:21:4d:d1:c4:74:64:2d.
Are you sure you want to continue connecting (yes/no)?

此時 known_hosts 並沒有 127.0.0.1 這臺機器的指紋信息,所以顯示這個提示來讓我們確認這個指紋是否是目標機器的 ECDSA 算法的指紋。 當我們輸入 yes 確認後,在下次登錄的時候,遠程主機發送過來的公鑰指紋,直接和 known_hosts 文件中對應 ip 的公鑰指紋比較即可。

# 本機查看服務器 172.17.132.120 的所有公鑰(要與服務器上 /etc/ssh 下面的公鑰 *.pub 一致)
ssh-keyscan -p 22 172.17.132.120

# 查看服務器公鑰 ecdsa 的指紋 -E md5/sha256 指紋 hash 算法
ssh-keygen -E md5 -lf /etc/ssh/ssh_host_ecdsa_key.pub
## 256 MD5:84:3d:9c:6e:75:f2:6b:b2:0b:40:aa:d6:29:2f:b4:40 no comment (ECDSA)
## 256 SHA256:ZoGnph63gnKLC9wQYrHYVU8ROTf6+K9LKAjn+jrXB2o no comment (ECDSA)

# 從客戶端查看服務器公鑰 ecdsa 的指紋(初次登陸時要驗證的指紋)
ssh-keyscan -t ecdsa -p 22 172.17.132.120 |ssh-keygen -lf -

# 公鑰轉換成特定指紋 hash 算法的指紋
awk '{print $2}' /etc/ssh/ssh_host_ecdsa_key.pub | base64 -d|openssl sha256 -binary |base64

known_hosts 這個文件是客戶端驗證服務端身份的重要依據。每次客戶端向服務端發起連接請求時,不僅服務端要驗證客戶端的合法性,客戶端也需要驗證服務端的身份。

客戶端就是通過 known_hosts 中的公鑰指紋來驗證服務器是否發生了變化,它在一定程度上能避免中間人攻擊,除了第一次登陸,因爲那時 known_hosts 中還沒有服務器的身份信息,所以對於首次提示的登陸指紋信息還是需要和服務器比對,最安全保險的做法是第一次登陸就使用祕鑰登陸。

登錄流程

  1. 版本號協商階段
  2. 密鑰和算法協商階段
  3. 認證階段
  4. 會話請求階段
  5. 會話交互階段

登陸方式分爲密碼登陸和密鑰登錄。

  • 密碼登陸
  1. 客戶端使用密鑰和算法協商階段生成的會話密鑰加密賬號和密碼(口令)、認證方式,將結果發送給服務器。
  2. 服務端使用獲得的會話密鑰解密報文,得到賬號和口令。
  3. 服務端對這個賬號和口令進行判斷,如果失敗,向客戶端發送認證失敗報文,其中包含了可以再次認證的方法列表。
  4. 客戶端從認證方法列表中選擇一種方法進行再次認證。
  5. 這個過程反覆進行,直到認證成功或者認證次數達到上限,服務端關閉本次TCP連接。

image-20231115142332073

  • 密鑰登錄
  1. 客戶端使用密鑰和算法協商階段生成的會話密鑰加密賬號、認證方法、id_rsa.pub,將結果發送給服務端。
  2. 服務端使用會話密鑰解密報文,得到賬號、id_rsa.pub。服務端在 $HOME/.ssh/authorized_keys 中找對應的公鑰,如果沒有找到,發送失敗消息給客戶端;如果找到,比較客戶發送過來的這個公鑰和找到的公鑰,如果內容相同,服務端生成一個隨機的字符串,簡稱“質詢”,然後使用找到的公鑰加密這個質詢,然後使用會話密鑰再次加密
  3. 服務端把這個雙重加密的數據發送給客戶端。
  4. 客戶端使用會話密鑰解密報文,然後使用 id_rsa 再次解密數據,得到質詢。
  5. 客戶端使用會話密鑰加密質詢,發送給服務端。
  6. 服務端使用會話密鑰解密報文,得到質詢,判斷是不是自己生成的那個質詢,如果不相同,發送失敗消息給客戶端,如果相同,認證通過。

免密安全傳輸

scp/rsync/sftp 都可以基於 SSH 來進行免密安全傳輸。

# 從本地同步 src.tar.gz 文件到遠程服務器 jump_target 的目錄 /path/to/des/
scp src.tar.gz jump_target:/path/to/des/
rsync -avz src.tar.gz jump_target:/path/to/des/
sftp登錄 & lcd src.tar.gz /path/to/des

# 從遠程服務器 jump_target 的文件 /path/to/src.tar.gz 到本地
scp jump_target:/path/to/src.tar.gz .
rsync -avz jump_target:/path/to/src.tar.gz .
sftp登錄 & put /path/to/des/src.tar.gz
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章