ssh轉發代理:ssh-agent用法詳解

SSH系列文章
SSH基礎:SSH和SSH服務
SSH轉發代理:ssh-agent用法詳解
SSH隧道:端口轉發功能詳解html


使用ssh-agent以前

使用ssh公鑰認證的方式能夠免去ssh客戶端(如ssh命令、xshell等)鏈接遠端主機sshd時須要輸入對方用戶密碼的問題。mysql

但若是執行ssh命令所在的主機上保存了多套祕鑰且將各公鑰分發給了不一樣的遠端主機,這時即便使用了公鑰認證,也依然須要輸入密碼,由於ssh客戶端不知道要讀取哪一個私鑰去和該遠端主機上的公鑰配對。git

看下面這張圖描述的狀況:程序員

上面描述的情形是這樣的:ssh客戶端要管理web server羣,還要管理mysql server羣,ssh客戶端要爲這兩個羣內的主機使用不一樣的密鑰對。例如要鏈接web server羣內的主機,使用~/.ssh/id_rsa_1這一套祕鑰,鏈接mysql server羣內的主機,使用~/.ssh/id_rsa_2這一套祕鑰。github

因而,將id_rsa_1.pub分發給web server羣內的每一個主機,將id_rsa_2.pub分發給mysql server羣內的每一個主機:web

$ ssh-copy-id -i ~/.ssh/id_rsa_1.pub root@webserver1
$ ssh-copy-id -i ~/.ssh/id_rsa_1.pub root@webserver2
$ ssh-copy-id -i ~/.ssh/id_rsa_1.pub root@webserver3
$ ssh-copy-id -i ~/.ssh/id_rsa_2.pub root@mysqlserver1
$ ssh-copy-id -i ~/.ssh/id_rsa_2.pub root@mysqlserver2
$ ssh-copy-id -i ~/.ssh/id_rsa_2.pub root@mysqlserver3

這一切進行的都很歡樂,可是一鏈接,發現仍是要密碼:sql

$ ssh root@webserver1
快輸入 root@webserver's 密碼:???

$ ssh root@mysqlserver1
快輸入 root@mysqlserver's 密碼:???

這是由於ssh客戶端鏈接webserver1的時候,除了默認會讀取的規範私鑰文件id_rsa(或其它祕鑰類型)外,不會自動讀取任何一個文件,同理鏈接mysqlserver1也同樣。shell

正確的鏈接方式是指定鏈接時使用哪一個私鑰去配對:bash

$ ssh -i ~/.ssh/id_rsa_1 root@webserver1
$ ssh -i ~/.ssh/id_rsa_2 root@mysqlserver1

好歡樂,終於連上了。可是真噁心,還要指定鏈接私鑰。併發

不只如此,若是私鑰是加密(passphrase)的,指定私鑰的時候還得輸入這個passphrase密碼。😭

ssh-agent是幹什麼的

程序員很不滿意這樣的鏈接方式,因而創造了一箇中間的私鑰管理者ssh-agent:你不是不知道怎麼配對嗎,我幫你配。並且這個功能對於程序員來講,so easy。

我在ssh身份認證階段中解釋過,ssh認證的過程實際上是客戶端(ssh命令端)讀取本身的私鑰並推導出指紋發送給服務端(sshd端),服務端也使用本身保存的公鑰推導出指紋進行對比,若是指紋相同說明服務端的公鑰和客戶端的私鑰是配對的。以下是公鑰、私鑰的指紋計算方式:

$ ssh-keygen -l -f ~/.ssh/id_rsa_2
2048 2c:e9:70:a8:f5:8d:87:9f:8c:de:cf:cf:14:f4:40:52  root@xuexi.longshuai.com (RSA)
$ ssh-keygen -l -f ~/.ssh/id_rsa_2.pub 
2048 2c:e9:70:a8:f5:8d:87:9f:8c:de:cf:cf:14:f4:40:52  root@xuexi.longshuai.com (RSA)

這個密鑰身份認證的過程是理解ssh-agent的關鍵。

ssh-agent的主功能大概以下圖描述:

使用ssh-agent後,能夠經過ssh-add命令向ssh-agent註冊本機的私鑰,ssh-agent會自動推導出這個私鑰的指紋(其實是ssh-add計算的)保存在本身的小本本里(內存),之後只要ssh鏈接某主機(某用戶),將自動轉發給ssh-agent,ssh-agent將自動從它的小本本里查找私鑰的指紋並將其發送給服務端(sshd端)。如此一來,ssh客戶端就無需再指定使用哪一個私鑰文件去鏈接。

總的看上去,ssh-agent的角色就是幫忙存儲、查找併發送對應的指紋而已,也就是說它是一個鏈接的轉發人,扮演的是一個代理的角色。

ssh並不是必定支持ssh-agent轉發的鏈接。要使用ssh-agent的轉發功能,須要在sshd_config中開啓AllowAgentForwarding選項,在ssh_config中開啓ForwardAgent選項。

使用ssh-agent和ssh-add

先將ssh-agent運行起來:

$ ssh-agent
SSH_AUTH_SOCK=/tmp/ssh-GiORRAqMXEFt/agent.28161; export SSH_AUTH_SOCK;
SSH_AGENT_PID=28162; export SSH_AGENT_PID;
echo Agent pid 28162;

輸出結果中明確說明了導出了幾個環境變量,同時也能夠知道ssh-agent使用Unix Domain套接字的方式監聽在本機上以及其pid爲28162。到時候能夠直接使用kill 28162殺掉這個進程,更直接的方式是使用ssh-agent -k,它會根據當前的環境變量SSH_AGENT_PID來殺進程。

可是很不幸,上面的ssh-agent儘管運行成功了,可是那兩個環境變量並無導出,上面的結果只是顯示給用戶看的。因此更多時候,會使用eval來執行ssh-agent,使得這些環境變量也被導出:

# 先殺掉剛纔的ssh-agent
$ kill 28162

$ eval `ssh-agent`
Agent pid 28173

ssh客戶端上運行ssh-agent後,就可使用ssh-add命令向ssh-agent添加私鑰(若是私鑰使用了passhprase密碼,則要求輸入一次密碼)。

$ ssh-add
$ ssh-add ~/.ssh/id_rsa_1
$ ssh-add ~/.ssh/id_rsa_2

默認會添加~/.ssh/下的全部私鑰類文件(~/.ssh/id_rsa, .ssh/id_dsa, ~/.ssh/id_ecdsa, ~/.ssh/id_ed25519, ~/.ssh/identity),也能夠指定要添加的文件。正如上面給出的示例。

ssh-add命令是查找當前環境變量SSH_AUTH_SOCK的值併發送添加請求給對應套接字的,因此這個套接字環境變量很是重要。

以後再使用公鑰認證就能夠直接鏈接到目標主機:

$ ssh root@webserver1
$ ssh root@webserver2
$ ssh root@webserver3

$ ssh root@mysqlserver1
$ ssh root@mysqlserver2
$ ssh root@mysqlserver3

ssh-agent的痛點和解決方案

ssh-agent的工做是依賴於環境變量SSH_AUTH_SOCKSSH_AGENT_PID的,不一樣用戶,不一樣終端,只要沒有和這兩個環境變量配對的ssh-agent,這個agent進程就不可以使用。要想使用某個agent,就必須在本身的shell中先設置好這兩個環境變量

另外須要注意的是,以eval `ssh-agent`啓動的方式會直接讓ssh-agent工做在後臺,它本身會獨立成本身的進程組,其父進程或終端退出後它仍然會掛靠在pid=1的init/systemd下。而ssh-agent的工做是依賴於環境變量SSH_AUTH_SOCKSSH_AGENT_PID的,shell或終端退出後這兩個環境變量就消失了,使得以前運行的ssh-agent被多餘地保留在後臺。

其實,咱們本身徹底能夠根據已有的ssh-agent推導出這兩個環境變量。若是ssh-agent進程還存在,那麼在/tmp/目錄下必定有對應的套接字文件(除非啓動ssh-agent時自定義了套接字路徑)。

$ tree /tmp/ssh* 
/tmp/ssh-q3tM0FzpCcdU
└── agent.28629
/tmp/ssh-SkKrrkK6qLDq
└── agent.28817

因而環境變量SSH_AUTH_SOCK的值已經獲得了。再根據agent.<ppid>中的ppid值,將其加上1,就是其子進程ssh-agent進程的PID值。例如,想要使用上面agent.28629對應的ssh-agent,那麼設置:

export SSH_AUTH_SOCK=/tmp/ssh-q3tM0FzpCcdU/agent.28629
export SSH_AGENT_PID=28630

咱們徹底能夠本身寫一個腳原本自動化獲取並設置這些環境變量。

不過已經有人用shell腳本提供好了更完善的解決方案:https://github.com/wwalker/ssh-find-agent。

下載好其中的ssh-find-agent.sh腳本,並放到某個目錄下。例如直接放在/etc/profile.d目錄下,而後給執行權限:

$ wget https://raw.githubusercontent.com/wwalker/ssh-find-agent/master/ssh-find-agent.sh -O /etc/profile.d/ssh-find-agent.sh
$ chmod +x /etc/profile.d/ssh-find-agent.sh

之後,只要在新的終端上,或者和ssh-agent進程失聯的shell中執行(若是在舊終端上,須要先source該shell腳本):

$ ssh-find-agent -a

它會自動尋找到第一個ssh-agent進程並配置好相關環境變量:

$ ssh-find-agent -a
$ echo $SSH_AUTH_SOCK
/tmp/ssh-SkKrrkK6qLDq/agent.28817
$ echo $SSH_AGENT_PID
28818

不給任何參數的ssh-find-agent函數會列出當前能找到的ssh-agent進程以及相關環境變量和編號。

$ ssh-find-agent
export SSH_AUTH_SOCK=/tmp/ssh-q3tM0FzpCcdU/agent.28629  #1)         
export SSH_AUTH_SOCK=/tmp/ssh-SkKrrkK6qLDq/agent.28817  #2)

使用ssh-find-agent的-c選項能夠手動選擇使用哪一個ssh-agent進程。

若是不知道當前是否有ssh-agent,可使用下面的方式:

$ ssh-find-agent -a || eval $(ssh-agent) > /dev/null

這樣就會在找不到的時候自動開啓一個ssh-agent。

除了上面的方法,還能夠直接使用另外一種管理工具:keychain。

$ yum install -y keychain

keychain能很好的管理ssh-agent,能夠去查看下如何使用。

管理ssh-agent中的私鑰

ssh-agent命令的選項:

-a bind_address
指定ssh-agent運行時綁定的Unix Domain套接字路徑,默認是`$TMPDIR/ssh-xxx/agent.<ppid>`

-c
-s
指定ssh-agent運行時輸出的內容(那些環境變量)是csh仍是bash格式的語句

-d
調試模式

-k
殺掉`SSH_AGENT_PID`環境變量指定的pid進程

-t life
指定ssh-agent中私鑰(指紋)的有效期。默認單位爲秒,能夠指定m(分鐘)、h(小時)、d(天)、w(周)。若是不指定,則永久有效。該有效期能夠被ssh-add指定的有效期選項覆蓋

ssh-add命令的選項(部分選項):

-D
刪除ssh-agent中全部私鑰(指紋)

-d key_file
刪除指定私鑰

-L
列出agent當前主機上全部公鑰參數,即公鑰文件中的內容

-l
列出agent當前已保存的指紋信息

-t
設置私鑰(指紋)的有效期。默認單位爲秒,能夠指定m(分鐘)、h(小時)、d(天)、w(周)

-x
使用一個密碼將agent鎖起來(lock),鎖起來的agent將再也不提供任何服務

-X
解鎖agent
相關文章
相關標籤/搜索