第1章 ssh命令和SSH服務詳解

 

本文對SSH連接驗證機制進行了非常詳細的分析,還詳細介紹了ssh客戶端工具的各種功能,相信能讓各位對ssh有個全方位較透徹的瞭解,而不是僅僅只會用它來連接遠程主機。

另外,本人翻譯了ssh客戶端命令的man文檔,如本文有不理解的地方,可以參考man文檔手冊:ssh中文手冊

SSH系列文章:

SSH基礎:SSH和SSH服務

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

SSH隧道:端口轉發功能詳解

1.1 非對稱加密基礎知識

對稱加密:加密和解密使用一樣的算法,只要解密時提供與加密時一致的密碼就可以完成解密。例如QQ登錄密碼,銀行卡密碼,只要保證密碼正確就可以。

非對稱加密:通過公鑰(public key)和私鑰(private key)來加密、解密。公鑰加密的內容可以使用私鑰解密,私鑰加密的內容可以使用公鑰解密。一般使用公鑰加密,私鑰解密,但並非絕對如此,例如CA簽署證書時就是使用自己的私鑰加密。在接下來介紹的SSH服務中,雖然一直建議分發公鑰,但也可以分發私鑰。

所以,如果A生成了(私鑰A,公鑰A),B生成了(私鑰B,公鑰B),那麼A和B之間的非對稱加密會話情形包括:

(1).A將自己的公鑰A分發給B,B拿着公鑰A將數據進行加密,並將加密的數據發送給A,A將使用自己的私鑰A解密數據。

(2).A將自己的公鑰A分發給B,並使用自己的私鑰A加密數據,然後B使用公鑰A解密數據。

(3).B將自己的公鑰B分發給A,A拿着公鑰B將數據進行加密,並將加密的數據發送給B,B將使用自己的私鑰B解密數據。

(4).B將自己的公鑰B分發給A,並使用自己的私鑰B加密數據,然後A使用公鑰B解密數據。

雖然理論上支持4種情形,但在SSH的身份驗證階段,SSH只支持服務端保留公鑰,客戶端保留私鑰的方式,所以方式只有兩種:客戶端生成密鑰對,將公鑰分發給服務端;服務端生成密鑰對,將私鑰分發給客戶端。只不過出於安全性和便利性,一般都是客戶端生成密鑰對並分發公鑰。後文將給出這兩種分發方式的示例。

1.2 SSH概要

(1).SSH是傳輸層和應用層上的安全協議,它只能通過加密連接雙方會話的方式來保證連接的安全性。當使用ssh連接成功後,將建立客戶端和服務端之間的會話,該會話是被加密的,之後客戶端和服務端的通信都將通過會話傳輸。

(2).SSH服務的守護進程爲sshd,默認監聽在22端口上。

(3).所有ssh客戶端工具,包括ssh命令,scp,sftp,ssh-copy-id等命令都是藉助於ssh連接來完成任務的。也就是說它們都連接服務端的22端口,只不過連接上之後將待執行的相關命令轉換傳送到遠程主機上,由遠程主機執行。

(4).ssh客戶端命令(ssh、scp、sftp等)讀取兩個配置文件:全局配置文件/etc/ssh/ssh_config和用戶配置文件~/.ssh/config。實際上命令行上也可以傳遞配置選項。它們生效的優先級是:命令行配置選項 > ~/.ssh/config > /etc/ssh/ssh_config。

(5).ssh涉及到兩個驗證:主機驗證和用戶身份驗證。通過主機驗證,再通過該主機上的用戶驗證,就能唯一確定該用戶的身份。一個主機上可以有很多用戶,所以每臺主機的驗證只需一次,但主機上每個用戶都需要單獨進行用戶驗證。

(6).ssh支持多種身份驗證,最常用的是密碼驗證機制和公鑰認證機制,其中公鑰認證機制在某些場景實現雙機互信時幾乎是必須的。雖然常用上述兩種認證機制,但認證時的順序默認是gssapi-with-mic,hostbased,publickey,keyboard-interactive,password。注意其中的主機認證機制hostbased不是主機驗證,由於主機認證用的非常少(它所讀取的認證文件爲/etc/hosts.equiv或/etc/shosts.equiv),所以網絡上比較少見到它的相關介紹。總的來說,通過在ssh配置文件(注意不是sshd配置文件)中使用指令PreferredAuthentications改變認證順序不失爲一種驗證的效率提升方式。

(7).ssh客戶端其實有不少很強大的功能,如端口轉發(隧道模式)、代理認證、連接共享(連接複用)等。

(8).ssh服務端配置文件爲/etc/ssh/sshd_config,注意和客戶端的全局配置文件/etc/ssh/ssh_config區分開來。

(9).很重要卻幾乎被人忽略的一點,ssh登錄時會請求分配一個僞終端。但有些身份認證程序如sudo可以禁止這種類型的終端分配,導致ssh連接失敗。例如使用ssh執行sudo命令時sudo就會驗證是否要分配終端給ssh。

1.3 SSH認證過程分析

假如從客戶端A(172.16.10.5)連接到服務端B(172.16.10.6)上,將包括主機驗證和用戶身份驗證兩個過程,以RSA非對稱加密算法爲例。

[[email protected] ~]# ssh 172.16.10.6

服務端B上首先啓動了sshd服務程序,即開啓了ssh服務,打開了22端口(默認)。

1.3.1 主機驗證過程

當客戶端A要連接B時,首先將進行主機驗證過程,即判斷主機B是否是否曾經連接過。

判斷的方法是讀取~/.ssh/known_hosts文件和/etc/ssh/known_hosts文件,搜索是否有172.16.10.6的主機信息(主機信息稱爲host key,表示主機身份標識)。如果沒有搜索到對應該地址的host key,則詢問是否保存主機B發送過來的host key,如果搜索到了該地址的host key,則將此host key和主機B發送過來的host key做比對,如果完全相同,則表示主機A曾經保存過主機B的host key,無需再保存,直接進入下一個過程——身份驗證,如果不完全相同,則提示是否保存主機B當前使用的host key。

詢問是否保存host key的過程如下所示:

[[email protected] ~]# ssh 172.16.10.6 
The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established.
RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf.
Are you sure you want to continue connecting (yes/no)? yes

或者windows端使用圖形界面ssh客戶端工具時:

在說明身份驗證過程前,先看下known_hosts文件的格式。以~/.ssh/known_hosts爲例。

[[email protected] ~]# cat ~/.ssh/known_hosts
172.16.10.6 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC675dv1w+GDYViXxqlTspUHsQjargFPSnR9nEqCyUgm5/32jXAA3XTJ4LUGcDHBuQ3p3spW/eO5hAP9eeTv5HQzTSlykwsu9He9w3ee+TV0JjBFulfBR0weLE4ut0PurPMbthE7jIn7FVDoLqc6o64WvN8LXssPDr8WcwvARmwE7pYudmhnBIMPV/q8iLMKfquREbhdtGLzJRL9DrnO9NNKB/EeEC56GY2t76p9ThOB6ES6e/87co2HjswLGTWmPpiqY8K/LA0LbVvqRrQ05+vNoNIdEfk4MXRn/IhwAh6j46oGelMxeTaXYC+r2kVELV0EvYV/wMa8QHbFPSM6nLz

該文件中,每行一個host key,行首是主機名,它是搜索host key時的索引,主機名後的內容即是host key部分。以此文件爲例,它表示客戶端A曾經試圖連接過172.16.10.6這個主機B,並保存了主機B的host key,下次連接主機B時,將搜索主機B的host key,並與172.16.10.6傳送過來的host key做比較,如果能匹配上,則表示該host key確實是172.16.10.6當前使用的host key,如果不能匹配上,則表示172.16.10.6修改過host key,或者此文件中的host key被修改過。

那麼主機B當前使用的host key保存在哪呢?在/etc/ssh/ssh_host*文件中,這些文件是服務端(此處即主機B)的sshd服務程序啓動時重建的。以rsa算法爲例,則保存在/etc/ssh/ssh_host_rsa_key和/etc/ssh/ssh_host_rsa_key.pub中,其中公鑰文件/etc/ssh/ssh_host_rsa_key.pub中保存的就是host key。

[[email protected] ~]# cat /etc/ssh/ssh_host_rsa_key.pub   # 在主機B上查看
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC675dv1w+GDYViXxqlTspUHsQjargFPSnR9nEqCyUgm5/32jXAA3XTJ4LUGcDHBuQ3p3spW/eO5hAP9eeTv5HQzTSlykwsu9He9w3ee+TV0JjBFulfBR0weLE4ut0PurPMbthE7jIn7FVDoLqc6o64WvN8LXssPDr8WcwvARmwE7pYudmhnBIMPV/q8iLMKfquREbhdtGLzJRL9DrnO9NNKB/EeEC56GY2t76p9ThOB6ES6e/87co2HjswLGTWmPpiqY8K/LA0LbVvqRrQ05+vNoNIdEfk4MXRn/IhwAh6j46oGelMxeTaXYC+r2kVELV0EvYV/wMa8QHbFPSM6nLz

發現/etc/ssh/ssh_host_rsa_key.pub文件內容和~/.ssh/known_hosts中該主機的host key部分完全一致,只不過~/.ssh/known_hosts中除了host key部分還多了一個主機名,這正是搜索主機時的索引。

綜上所述,在主機驗證階段,服務端持有的是私鑰,客戶端保存的是來自於服務端的公鑰。注意,這和身份驗證階段密鑰的持有方是相反的。

實際上,ssh並非直接比對host key,因爲host key太長了,比對效率較低。所以ssh將host key轉換成host key指紋,然後比對兩邊的host key指紋即可。指紋格式如下:

[[email protected] ~]# ssh 172.16.10.6 
The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established.
RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf.
Are you sure you want to continue connecting (yes/no)? yes

host key的指紋可由ssh-kegen計算得出。例如,下面分別是主機A(172.16.10.5)保存的host key指紋,和主機B(172.16.10.6)當前使用的host key的指紋。可見它們是完全一樣的。

[[email protected] ~]# ssh-keygen -l -f ~/.ssh/known_hosts
2048 f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf 172.16.10.6 (RSA)

[[email protected] ~]# ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key
2048 f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf   (RSA)

其實ssh還支持host key模糊比較,即將host key轉換爲圖形化的指紋。這樣,圖形結果相差大的很容易就比較出來。之所以說是模糊比較,是因爲對於非常近似的圖形化指紋,ssh可能會誤判。圖形化指紋的生成方式如下:只需在上述命令上加一個"-v"選項進入詳細模式即可。

[[email protected] ~]# ssh-keygen -lv -f ~/.ssh/known_hosts
2048 f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf 172.16.10.6 (RSA)
+--[ RSA 2048]----+
|                 |
|                 |
|           .     |
|          o      |
|        S. . +   |
|      . +++ + .  |
|       B.+.= .   |
|      + B.  +.   |
|       o.+.  oE  |
+-----------------+

更詳細的主機認證過程是:先進行密鑰交換(DH算法)生成session key(rfc文檔中稱之爲shared secret),然後從文件中讀取host key,並用host key對session key進行簽名,然後對簽名後的指紋進行判斷。(In SSH, the key exchange is signed with the host key to provide host authentication.來源:https://tools.ietf.org/html/rfc4419)

過程如下圖:

1.3.2 身份驗證過程

主機驗證通過後,將進入身份驗證階段。SSH支持多種身份驗證機制,它們的驗證順序如下:gssapi-with-mic,hostbased,publickey,keyboard-interactive,password,但常見的是密碼認證機制(password)和公鑰認證機制(public key)。當公鑰認證機制未通過時,再進行密碼認證機制的驗證。這些認證順序可以通過ssh配置文件(注意,不是sshd的配置文件)中的指令PreferredAuthentications改變。

如果使用公鑰認證機制,客戶端A需要將自己生成的公鑰(~/.ssh/id_rsa.pub)發送到服務端B的~/.ssh/authorized_keys文件中。當進行公鑰認證時,客戶端將告訴服務端要使用哪個密鑰對,並告訴服務端它已經訪問過密鑰對的私鑰部分~/.ssh/id_rsa(客戶端從自己的私鑰中推導,或者從私鑰同目錄下讀取公鑰,計算公鑰指紋後發送給服務端。所以有些版本的ssh不要求存在公鑰文件,有些版本的ssh則要求私鑰和公鑰同時存在且在同目錄下),然後服務端將檢測密鑰對的公鑰部分,判斷該客戶端是否允許通過認證。如果認證不通過,則進入下一個認證機制,以密碼認證機制爲例。

當使用密碼認證時,將提示輸入要連接的遠程用戶的密碼,輸入正確則驗證通過。

1.3.3 驗證通過

當主機驗證和身份驗證都通過後,分兩種情況:直接登錄或執行ssh命令行中給定某個命令。如:

[[email protected] ~]# ssh 172.16.10.6 
[[email protected] ~]# ssh 172.16.10.6  'echo "haha"'

(1).前者ssh命令行不帶任何命令參數,表示使用遠程主機上的某個用戶(此處爲root用戶)登錄到遠程主機172.16.10.6上,所以遠程主機會爲ssh分配一個僞終端,並進入bash環境。

(2).後者ssh命令行帶有命令參數,表示在遠程主機上執行給定的命令【echo "haha"】。ssh命令行上的遠程命令是通過fork ssh-agent得到的子進程來執行的,當命令執行完畢,子進程消逝,ssh也將退出,建立的會話和連接也都將關閉。(之所以要在這裏明確說明遠程命令的執行過程,是爲了說明後文將介紹的ssh實現端口轉發時的注意事項)

實際上,在ssh連接成功,登錄或執行命令行中命令之前,可以指定要在遠程執行的命令,這些命令放在~/.ssh/rc或/etc/ssh/rc文件中,也就是說,ssh連接建立之後做的第一件事是在遠程主機上執行這兩個文件中的命令。

1.4 各種文件分佈

以主機A連接主機B爲例,主機A爲SSH客戶端,主機B爲SSH服務端。

在服務端即主機B上:

  • /etc/ssh/sshd_config  :ssh服務程序sshd的配置文件。
  • /etc/ssh/ssh_host_*   :服務程序sshd啓動時生成的服務端公鑰和私鑰文件。如ssh_host_rsa_key和ssh_host_rsa_key.pub。
  •                                   :其中.pub文件是主機驗證時的host key,將寫入到客戶端的~/.ssh/known_hosts文件中。
  •                                   :其中私鑰文件嚴格要求權限爲600,若不是則sshd服務可能會拒絕啓動。
  • ~/.ssh/authorized_keys:保存的是基於公鑰認證機制時來自於客戶端的公鑰。在基於公鑰認證機制認證時,服務端將讀取該文件。

在客戶端即主機A上:

  • /etc/ssh/ssh_config    :客戶端的全局配置文件。
  • ~/.ssh/config              :客戶端的用戶配置文件,生效優先級高於全局配置文件。一般該文件默認不存在。該文件對權限有嚴
  •                                   :格要求只對所有者有讀/寫權限,對其他人完全拒絕寫權限。
  • ~/.ssh/known_hosts   :保存主機驗證時服務端主機host key的文件。文件內容來源於服務端的ssh_host_rsa_key.pub文件。
  • /etc/ssh/known_hosts:全局host key保存文件。作用等同於~/.ssh/known_hosts。
  • ~/.ssh/id_rsa              :客戶端生成的私鑰。由ssh-keygen生成。該文件嚴格要求權限,當其他用戶對此文件有可讀權限時,
  •                                   :ssh將直接忽略該文件。
  • ~/.ssh/id_rsa.pub       :私鑰id_rsa的配對公鑰。對權限不敏感。當採用公鑰認證機制時,該文件內容需要複製到服務端的
  •                                   :~/.ssh/authorized_keys文件中。
  • ~/.ssh/rc                     :保存的是命令列表,這些命令在ssh連接到遠程主機成功時將第一時間執行,執行完這些命令之後才
  •                                   :開始登陸或執行ssh命令行中的命令。
  • /etc/ssh/rc                  :作用等同於~/.ssh/rc。

1.5 配置文件簡單介紹

分爲服務端配置文件/etc/ssh/sshd_config和客戶端配置文件/etc/ssh/ssh_config(全局)或~/.ssh/config(用戶)。

雖然服務端和客戶端配置文件默認已配置項雖然非常少非常簡單,但它們可配置項非常多。sshd_config完整配置項參見金步國翻譯的sshd_config中文手冊,ssh_config也可以參考sshd_config的配置,它們大部分配置項所描述的內容是相同的。

1.5.1 sshd_config

簡單介紹下該文件中比較常見的指令。

[[email protected] ~]# cat /etc/ssh/sshd_config
#Port 22 # 服務端SSH端口,可以指定多條表示監聽在多個端口上 #ListenAddress 0.0.0.0 # 監聽的IP地址。0.0.0.0表示監聽所有IP Protocol 2 # 使用SSH 2版本 ##################################### # 私鑰保存位置 # ##################################### # HostKey for protocol version 1 #HostKey /etc/ssh/ssh_host_key # SSH 1保存位置/etc/ssh/ssh_host_key # HostKeys for protocol version 2 #HostKey /etc/ssh/ssh_host_rsa_key # SSH 2保存RSA位置/etc/ssh/ssh_host_rsa _key #HostKey /etc/ssh/ssh_host_dsa_key # SSH 2保存DSA位置/etc/ssh/ssh_host_dsa _key ################################### # 雜項配置 # ################################### #PidFile /var/run/sshd.pid # 服務程序sshd的PID的文件路徑 #ServerKeyBits 1024 # 服務器生成的密鑰長度 #SyslogFacility AUTH # 使用哪個syslog設施記錄ssh日誌。日誌路徑默認爲/var/log/secure #LogLevel INFO # 記錄SSH的日誌級別爲INFO ################################### # 以下項影響認證速度 # ################################### #UseDNS yes # 指定是否將客戶端主機名解析爲IP,以檢查此主機名是否與其IP地址真實對應。默認yes。 # 由此可知該項影響的是主機驗證階段。建議在未配置DNS解析時,將其設置爲no,否則主機驗證階段會很慢 ################################### # 以下是和安全有關的配置 # ################################### #PermitRootLogin yes # 是否允許root用戶登錄 #GSSAPIAuthentication no # 是否開啓GSSAPI身份認證機制,默認爲yes #PubkeyAuthentication yes # 是否開啓基於公鑰認證機制 #AuthorizedKeysFile .ssh/authorized_keys # 基於公鑰認證機制時,來自客戶端的公鑰的存放位置 PasswordAuthentication yes # 是否使用密碼驗證,如果使用密鑰對驗證可以關了它 #PermitEmptyPasswords no # 是否允許空密碼,如果上面的那項是yes,這裏最好設置no #MaxSessions 10 # 最大客戶端連接數量 #LoginGraceTime 2m # 身份驗證階段的超時時間,若在此超時期間內未完成身份驗證將自動斷開 #MaxAuthTries 6 # 指定每個連接最大允許的認證次數。默認值是6。 # 如果失敗認證次數超過該值一半,將被強制斷開,且生成額外日誌消息。 MaxStartups 10 # 最大允許保持多少個未認證的連接。默認值10。 ################################### # 以下可以自行添加到配置文件 # ################################### DenyGroups hellogroup testgroup # 表示hellogroup和testgroup組中的成員不允許使用sshd服務,即拒絕這些用戶連接 DenyUsers hello test # 表示用戶hello和test不能使用sshd服務,即拒絕這些用戶連接 ################################### # 以下一項和遠程端口轉發有關 # ################################### #GatewayPorts no # 設置爲yes表示sshd允許被遠程主機所設置的本地轉發端口綁定在非環回地址上 # 默認值爲no,表示遠程主機設置的本地轉發端口只能綁定在環回地址上,見後文"遠程端口轉發"

一般來說,如非有特殊需求,只需修改下監聽端口和UseDNS爲no以加快主機驗證階段的速度即可。

配置好後直接重啓啓動sshd服務即可。

[[email protected] ~]# service sshd restart

1.5.2 ssh_config

需要說明的是,客戶端配置文件有很多配置項和服務端配置項名稱相同,但它們一個是在連接時採取的配置(客戶端配置文件),一個是sshd啓動時開關性的設置(服務端配置文件)。例如,兩配置文件都有GSSAPIAuthentication項,在客戶端將其設置爲no,表示連接時將直接跳過該身份驗證機制,而在服務端設置爲no則表示sshd啓動時不開啓GSSAPI身份驗證的機制。即使客戶端使用了GSSAPI認證機制,只要服務端沒有開啓,就絕對不可能認證通過。

下面也簡單介紹該文件。

# Host *                              # Host指令是ssh_config中最重要的指令,只有ssh連接的目標主機名能匹配此處給定模式時,
                                      # 下面一系列配置項直到出現下一個Host指令纔對此次連接生效
#   ForwardAgent no
#   ForwardX11 no
#   RhostsRSAAuthentication no
#   RSAAuthentication yes
#   PasswordAuthentication yes     # 是否啓用基於密碼的身份認證機制
#   HostbasedAuthentication no     # 是否啓用基於主機的身份認證機制
#   GSSAPIAuthentication no        # 是否啓用基於GSSAPI的身份認證機制
#   GSSAPIDelegateCredentials no
#   GSSAPIKeyExchange no
#   GSSAPITrustDNS no
#   BatchMode no                   # 如果設置爲"yes",將禁止passphrase/password詢問。比較適用於在那些不需要詢問提供密
                                   # 碼的腳本或批處理任務任務中。默認爲"no"。
#   CheckHostIP yes
#   AddressFamily any
#   ConnectTimeout 0
#   StrictHostKeyChecking ask        # 設置爲"yes",ssh將從不自動添加host key到~/.ssh/known_hosts文件,
                                     # 且拒絕連接那些未知的主機(即未保存host key的主機或host key已改變的主機)。
                                     # 它將強制用戶手動添加host key到~/.ssh/known_hosts中。
                                     # 設置爲ask將詢問是否保存到~/.ssh/known_hosts文件。
                                     # 設置爲no將自動添加到~/.ssh/known_hosts文件。
#   IdentityFile ~/.ssh/identity     # ssh v1版使用的私鑰文件
#   IdentityFile ~/.ssh/id_rsa       # ssh v2使用的rsa算法的私鑰文件
#   IdentityFile ~/.ssh/id_dsa       # ssh v2使用的dsa算法的私鑰文件
#   Port 22                          # 當命令行中不指定端口時,默認連接的遠程主機上的端口
#   Protocol 2,1
#   Cipher 3des                      # 指定ssh v1版本中加密會話時使用的加密協議
#   Ciphers aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc  # 指定ssh v1版本中加密會話時使用的加密協議
#   MACs hmac-md5,hmac-sha1,[email protected],hmac-ripemd160
#   EscapeChar ~
#   Tunnel no
#   TunnelDevice any:any
#   PermitLocalCommand no    # 功能等價於~/.ssh/rc,表示是否允許ssh連接成功後在本地執行LocalCommand指令指定的命令。
#   LocalCommand             # 指定連接成功後要在本地執行的命令列表,當PermitLocalCommand設置爲no時將自動忽略該配置
                             # %d表本地用戶家目錄,%h表示遠程主機名,%l表示本地主機名,%n表示命令行上提供的主機名,
                             # p%表示遠程ssh端口,r%表示遠程用戶名,u%表示本地用戶名。
#   VisualHostKey no         # 是否開啓主機驗證階段時host key的圖形化指紋
Host *
        GSSAPIAuthentication yes

 

如非有特殊需求,ssh客戶端配置文件一般只需修改下GSSAPIAuthentication的值爲no來改善下用戶驗證的速度即可,另外在有非交互需求時,將StrictHostKeyChecking設置爲no以讓主機自動添加host key。

1.6 ssh命令簡單功能

此處先介紹ssh命令的部分功能,其他包括端口轉發的在後文相關內容中解釋,關於連接複用的選項本文不做解釋。

語法:

ssh [options] [[email protected]]hostname [command]
 
參數說明:
-b bind_address :在本地主機上綁定用於ssh連接的地址,當系統有多個ip時才生效。
-E log_file     :將debug日誌寫入到log_file中,而不是默認的標準錯誤輸出stderr。
-F configfile   :指定用戶配置文件,默認爲~/.ssh/config。
-f              :請求ssh在工作在後臺模式。該選項隱含了"-n"選項,所以標準輸入將變爲/dev/null。
-i identity_file:指定公鑰認證時要讀取的私鑰文件。默認爲~/.ssh/id_rsa。
-l login_name   :指定登錄在遠程機器上的用戶名。也可以在全局配置文件中設置。
-N              :顯式指明ssh不執行遠程命令。一般用於端口轉發,見後文端口轉發的示例分析。
-n              :將/dev/null作爲標準輸入stdin,可以防止從標準輸入中讀取內容。ssh在後臺運行時默認該項。
-p port         :指定要連接遠程主機上哪個端口,也可在全局配置文件中指定默認的連接端口。
-q              :靜默模式。大多數警告信息將不輸出。
-T              :禁止爲ssh分配僞終端。
-t              :強制分配僞終端,重複使用該選項"-tt"將進一步強制。
-v              :詳細模式,將輸出debug消息,可用於調試。"-vvv"可更詳細。
-V              :顯示版本號並退出。
-o              :指定額外選項,選項非常多。
[email protected]   :指定ssh以遠程主機hostname上的用戶user連接到的遠程主機上,若省略user部分,則表示使用本地當前用戶。
                :如果在hostname上不存在user用戶,則連接將失敗(將不斷進行身份驗證)。
command         :要在遠程主機上執行的命令。指定該參數時,ssh的行爲將不再是登錄,而是執行命令,命令執行完畢時ssh連接就關閉。

例如,以172.16.10.6主機上的longshuai用戶登錄172.16.10.6。

[[email protected] ~]# ssh [email protected]
The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established.
ECDSA key fingerprint is 18:d1:28:1b:99:3b:db:20:c7:68:0a:f8:9e:43:e8:b4.
Are you sure you want to continue connecting (yes/no)? yes       # 主機驗證
Warning: Permanently added '172.16.10.6' (ECDSA) to the list of known hosts.
[email protected]172.16.10.6's password:                      # 用戶驗證
Last login: Wed Jul  5 12:27:29 2017 from 172.16.10.6

此時已經登錄到了172.16.10.6主機上。

[[email protected] ~]$ hostname -I
172.16.10.6

要退出ssh登錄,使用logout命令或exit命令即可返回到原主機環境。

使用ssh還可以實現主機跳轉,即跳板功能。例如主機B能和A、C通信,但A、C之間不同通信,即A<-->B<-->C<-x->A的情形。如果要從A登陸到C,則可以藉助B這個跳板登錄到C。此處一個簡單示例爲:從172.16.10.5登錄到172.16.10.6,再以此爲基礎登錄到172.16.10.3上。

[[email protected] ~]# ssh 172.16.10.6
The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established.
RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '172.16.10.6' (RSA) to the list of known hosts.
Last login: Wed Jul  5 12:36:51 2017 from 172.16.10.6
[[email protected] ~]# ssh 172.16.10.3
The authenticity of host '172.16.10.3 (172.16.10.3)' can't be established.
ECDSA key fingerprint is 18:d1:28:1b:99:3b:db:20:c7:68:0a:f8:9e:43:e8:b4.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '172.16.10.3' (ECDSA) to the list of known hosts.
[email protected]'s password:
Last login: Thu Jun 29 12:38:56 2017 from 172.16.10.6
[[email protected] ~]# hostname -I
172.16.10.3 172.16.10.4

同樣,在退出時,也是一層一層退出的。

[[email protected] ~]# exit
logout
Connection to 172.16.10.3 closed.

[[email protected] ~]# hostname -I   
172.16.10.6

[[email protected] ~]# exit
logout
Connection to 172.16.10.6 closed.

注意,由於在藉助172.16.10.6當跳板連接到172.16.10.3,所以172.16.10.3的host key是添加到172.16.10.6上而非172.16.10.5上的。

如果在命令行給出了要執行的命令,默認ssh將工作在前臺,如果同時給定了"-f"選項,則ssh工作在後臺。但儘管是工作在後臺,當遠程執行的命令如果有消息返回時,將隨時可能顯示在本地。當遠程命令執行完畢後,ssh連接也將立即關閉。

[[email protected] ~]# ssh 172.16.10.6 'sleep 5'     # 在前臺睡眠5秒鐘

[[email protected] ~]# ssh 172.16.10.6 -f 'sleep 5;echo over'   # 在後臺睡眠5秒,睡眠完成後echo一段信息

由於第二條命令是放在後臺執行的,所以該ssh一建立完成ssh會話就立即返回本地bash環境,但當5秒之後,將在本地突然顯示"over"。

ssh執行遠程命令默認允許從標準輸入中讀取數據然後傳輸到遠程。可以使用"-n"選項,使得標準輸入重定向爲/dev/null。例如:

[[email protected] ~]# echo haha | ssh 172.16.10.6 'cat'
haha

[[email protected] ~]# ssh 172.16.10.6 'cat' </etc/fstab  

再看如下兩條命令:

[[email protected] ~]# tar zc /tmp/* | ssh 172.16.10.6 'cd /tmp;tar xz'

[[email protected] ~]# ssh 172.16.10.6 'tar cz /tmp' | tar xz

第一條命令將/tmp下文件歸檔壓縮,然後傳送到遠程主機上並被解包;第二條命令將遠程主機上的/tmp目錄歸檔壓縮,並傳輸到本地解包。所以它們實現了拷貝的功能。

不妨再分析下面的命令,該命令改編自ssh-copy-id腳本中的主要命令。如果不知道ssh-copy-id命令是幹什麼的,後文有介紹。

[[email protected] ~]# cat ~/.ssh/id_rsa.pub | ssh 172.16.10.6 "umask 077; test -d ~/.ssh || mkdir ~/.ssh ; cat >> ~/.ssh/authorized_keys"

該命令首先建立ssh連接,並在遠程執行"umask 077"臨時修改遠程的umask值,使得遠程創建的目錄權限爲700,然後判斷遠程主機上是否有~/.ssh目錄,如果沒有則創建,最後從標準輸入中讀取本地公鑰文件~/.ssh/id_rsa.pub的內容並將其追加到~/.ssh/authorized_keys文件中。

如果將此命令改爲如下命令,使用ssh的"-n"選項,並將追加重定向改爲覆蓋重定向符號。

[[email protected] ~]# cat ~/.ssh/id_rsa.pub | ssh -n 172.16.10.6 "umask 077; test -d ~/.ssh || mkdir ~/.ssh ; cat > ~/.ssh/authorized_keys"

該命令的結果是清空遠程主機172.16.10.6上的~/.ssh/authorized_keys文件,因爲ssh的"-n"選項強行改變了ssh讀取的標準輸入爲/dev/null。

1.7 scp命令及過程分析

scp是基於ssh的遠程拷貝命令,也支持本地拷貝,甚至支持遠程到遠程的拷貝。

scp由於基於ssh,所以其端口也是使用ssh的端口。其實,scp拷貝的實質是使用ssh連接到遠程,並使用該連接來傳輸數據。下文有scp執行過程的分析。

另外,scp還非常不佔資源,不會提高多少系統負荷,在這一點上,rsync遠不及它。雖然 rsync比scp會快一點,但rsync是增量拷貝,要判斷每個文件是否修改過,在小文件衆多的情況下,判斷次數非常多,導致rsync效率較差,而scp基本不影響系統正常使用。

scp每次都是全量拷貝,在某些情況下,肯定是不及rsync的。

scp [-12BCpqrv] [-l limit] [-o ssh_option] [-P port] [[[email protected]]host1:]src_file ... [[[email protected]]host2:]dest_file

選項說明:
-1:使用ssh v1版本,這是默認使用協議版本
-2:使用ssh v2版本
-C:拷貝時先壓縮,節省帶寬
-l limit:限制拷貝速度,Kbit/s,1Byte=8bit,所以"-l 800"表示的速率是100K/S
-o ssh_option:指定ssh連接時的特殊選項,一般用不上。
-P port:指定目標主機上ssh端口,大寫的字母P,默認是22端口
-p:拷貝時保持源文件的mtime,atime,owner,group,privileges
-r:遞歸拷貝,用於拷貝目錄。注意,scp拷貝遇到鏈接文件時,會拷貝鏈接的源文件內容填充到目標文件中(scp的本質就是填充而非拷貝)
-v:輸出詳細信息,可以用來調試或查看scp的詳細過程,分析scp的機制

src_file是源位置,dest_file是目標位置,即將src_file複製到dest_file,其中src_file可以指定多個。由於源位置和目標位置都可以使用本地路徑和遠程路徑,所以scp能實現本地拷貝到遠程、本地拷貝到本地、遠程拷貝到本地、遠程拷貝到另一個遠程。其中遠程路徑的指定格式爲"[email protected]:/path",可以省略user,也可以省略":/path",省略":/path"時表示拷貝到目的用戶的家目錄下。

注意:scp拷貝是強制覆蓋型拷貝,當有重名文件時,不會進行任何詢問。

例如:

(1).本地拷貝到本地:/etc/fstab-->/tmp/a.txt。

[[email protected] ~]# scp /etc/fstab /tmp/a.txt

(2).本地到遠程:/etc/fstab-->172.16.10.6:/tmp/a.txt。

[[email protected] ~]# scp /etc/fstab 172.16.10.6:/tmp
fstab                                          100%  805     0.8KB/s   00:00

(3).遠程到本地:172.16.10.6:/etc/fstab-->/tmp/a.txt。

[[email protected] ~]# scp 172.16.10.6:/etc/fstab /tmp/a.txt
fstab                                          100%  501     0.5KB/s   00:00

(4).遠程路徑1到遠程路徑2:172.16.10.6:/etc/fstab-->/172.16.10.3:/tmp/a.txt。

[[email protected] ~]# scp 172.16.10.6:/etc/fstab 172.16.10.3:/tmp/a.txt
fstab                                          100%  501     0.5KB/s   00:00   
Connection to 172.16.10.6 closed.

遠程複製到遠程時,可能會因爲sudoers的限制不給ssh分配終端,導致無法輸入yes/密碼或者直接拒絕等而失敗。這時可以加上一層ssh:

ssh -tt 172.16.10.2 "scp 172.16.10.6:/etc/fstab 172.16.10.3:/tmp/a.txt"

其中172.16.10.2是執行ssh命令所在的主機A,也就是在A主機上使用scp將主機B(10.6)上的文件複製到主機C(10.3)上,這裏的"-tt"表示強制分配僞終端給ssh。

另外,還可以使用scp的"-3"選項,改變scp遠程到遠程的默認傳輸模式(默認傳輸模式見下面的機制分析),如果上面的命令失敗的話,也可以使用該選項一試。

scp -3 172.16.10.6:/etc/fstab 172.16.10.3:/tmp/a.txt

1.7.1 scp拷貝機制分析

scp的拷貝實質是建立ssh連接,然後通過此連接來傳輸數據。如果是遠程1拷貝到遠程2,則是將scp命令轉換後發送到遠程1上執行,在遠程1上建立和遠程2的ssh連接,並通過此連接來傳輸數據。

在遠程複製到遠程的過程中,例如在本地(172.16.10.5)執行scp命令將A主機(172.16.10.6)上的/tmp/copy.txt複製到B主機(172.16.10.3)上的/tmp目錄下,如果使用-v選項查看調試信息的話,會發現它的步驟類似是這樣的。

# 以下是從結果中提取的過程
# 首先輸出本地要執行的命令
Executing: /usr/bin/ssh -v -x -oClearAllForwardings yes -t -l root 172.16.10.6 scp -v /tmp/copy.txt [email protected]:/tmp
 
# 從本地連接到A主機
debug1: Connecting to 172.16.10.6 [172.16.10.6] port 22.
debug1: Connection established.
 
# 要求驗證本地和A主機之間的連接
debug1: Next authentication method: password
[email protected]'s password:
 
# 將scp命令行修改後發送到A主機上
debug1: Sending command: scp -v /tmp/copy.txt [email protected]:/tmp
 
# 在A主機上執行scp命令
Executing: program /usr/bin/ssh host 172.16.10.3, user root, command scp -v -t /tmp
 
# 驗證A主機和B主機之間的連接
debug1: Next authentication method: password
[email protected]'s password:
 
# 從A主機上拷貝源文件到最終的B主機上
debug1: Sending command: scp -v -t /tmp
Sending file modes: C0770 24 copy.txt
Sink: C0770 24 copy.txt
copy.txt                                                           100%   24     0.0KB/s  
 
# 關閉本地主機和A主機的連接
Connection to 172.16.10.6 closed.

也就是說,遠程主機A到遠程主機B的複製,實際上是將scp命令行從本地傳輸到主機A上,由A自己去執行scp命令。也就是說,本地主機不會和主機B有任何交互行爲,本地主機就像是一個代理執行者一樣,只是幫助傳送scp命令行以及幫助顯示信息。

其實從本地主機和主機A上的~/.ssh/know_hosts文件中可以看出,本地主機只是添加了主機A的host key,並沒有添加主機B的host key,而在主機A上則添加了主機B的host key。

 

1.8 基於公鑰認證機制實現雙機互信

在身份驗證階段,由於默認情況下基於公鑰認證的機制順序優先於基於密碼認證的機制,所以基於公鑰認證身份,就可以免輸入密碼,即實現雙機互信(實際上只是單方向的信任)。

基於公鑰認證機制的認證過程在前文已經詳細說明過了,如還不清楚,請跳回上文。

1.8.1 實現步驟

以下是實現基於公鑰認證的實現步驟:

(1).在客戶端使用ssh-keygen生成密鑰對,存放路徑按照配置文件的指示,默認是在~/.ssh/目錄下。

[[email protected] ~]# ssh-keygen -t rsa    # -t參數指定算法,可以是rsa或dsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):  # 詢問私鑰保存路徑
Enter passphrase (empty for no passphrase):               # 詢問是否加密私鑰文件
Enter same passphrase again:            
Your identification has been saved in /root/.ssh/id_rsa. 
Your public key has been saved in /root/.ssh/id_rsa.pub. 

如果不想被詢問,則可以使用下面一條命令完成:"-f"指定私鑰文件,"-P"指定passphrase,或者"-N"也一樣。

[[email protected] ~]# ssh-keygen -t rsa -f ~/.ssh/id_rsa -P ''   # 指定加密私鑰文件的密碼爲空密碼,即不加密

[[email protected] ~]# ssh-keygen -t rsa -f ~/.ssh/id_rsa -N ''   # 同上

查看~/.ssh/目錄下私鑰的權限。私鑰文件有嚴格的權限要求,當私鑰文件的非所有者有可讀權限時,將直接忽略該私鑰文件導致公鑰認證失敗。

[[email protected] ~]# ls -l ~/.ssh
total 12
-rw------- 1 root root 1671 Jun 29 00:18 id_rsa      # 私鑰權限必須600,屬主爲自己
-rw-r--r-- 1 root root  406 Jun 29 00:18 id_rsa.pub
-rw-r--r-- 1 root root  393 Jun 29 05:56 known_hosts

(2).將上面生成的公鑰使用ssh-copy-id分發(即複製)到遠程待信任主機上。

ssh-copy-id用法很簡單,只需指定待信任主機及目標用戶即可。如果生成的公鑰文件,路徑不是~/.ssh/id_rsa.pub,則使用"-i"選項指定要分發的公鑰。

ssh-copy-id [-i [identity_file]] [[email protected]]machine

例如,將公鑰分發到172.16.10.6上的root用戶家目錄下:

[[email protected] ~]# ssh-copy-id 172.16.10.6

ssh-copy-id唯一需要注意的是,如果ssh服務端的端口不是22,則需要給ssh-copy-id傳遞端口號,傳遞方式爲 "-p port_num [[email protected]]hostname" (注意加上雙引號),例如"-p 22222 [email protected]"。之所以要如此傳遞,見下面摘自ssh-copy-id中公鑰分發的命令部分。

{ eval "$GET_ID" ; } | ssh $1 "umask 077; test -d ~/.ssh || mkdir ~/.ssh ; cat >> ~/.ssh/authorized_keys && (test -x /sbin/restorecon && /sbin/restorecon ~/.ssh ~/.ssh/authorized_keys >/dev/null 2>&1 || true)" || exit 1

其中"{ eval "$GET_ID" ; }"可理解爲待分發的本地公鑰內容,"(test -x /sbin/restorecon && /sbin/restorecon ~/.ssh ~/.ssh/authorized_keys >/dev/null 2>&1 || true)"和selinux有關,不用管,所以上述命令簡化爲:

cat ~/.ssh/id_rsa.pub | ssh $1 "umask 077; test -d ~/.ssh || mkdir ~/.ssh ; cat >> ~/.ssh/authorized_keys || exit 1

可見,ssh-copy-id的所有參數都是存儲在位置變量$1中傳遞給ssh,所以應該將ssh的端口選項"-p port_num"和[email protected]放在一起傳遞。

通過分析上面的命令,也即知道了ssh-copy-id的作用:在目標主機的指定用戶的家目錄下,檢測是否有~/.ssh目錄,如果沒有,則以700權限創建該目錄,然後將本地的公鑰追加到目標主機指定用戶家目錄下的~/.ssh/authorized_keys文件中。

1.8.2 一鍵shell腳本

就這樣簡單的兩步就實現了基於公鑰的身份認證。當然,不使用ssh-copy-id,也一樣能實現上述過程。更便捷地,可以寫一個簡單的腳本,簡化上述兩個步驟爲1個步驟。

#!/bin/bash
 
###########################################################
#  description: public key authentication in one step     #
#  author     : 駿馬金龍                                   #
#  blog       : http://www.cnblogs.com/f-ck-need-u/       #
###########################################################
 
privkey="$HOME/.ssh/id_rsa"
publickey="$HOME/.ssh/id_rsa.pub"
 
# Usage help
if [ $# -ne 1 ];then
   echo "Usage:$0 [[email protected]]hostname"
   exit 1
fi
 
# test private/publick key exist or not, and the privilege 600 or not
if [ -f "$privkey" -a -f "$publickey" ];then
   privkey_priv=`stat -c %a $privkey`
   if [ "$privkey_priv" -ne 600 ];then
       echo "The privilege of private key ~/.ssh/id_rsa is not 600, exit now."
       exit 1
   fi
else
   echo "private/public key is not exist, it will create it"
   ssh-keygen -t rsa -f $privkey -N ''
   echo "keys created over, it located on $HOME/.ssh/"
fi
 
ssh-copy-id "-o StrictHostKeyChecking=no $1"
 
if [ $? -eq 0 ];then
   echo -e "\e[31m publickey copy over \e[0m"
else
   echo "ssh can't to the remote host"
   exit 1
fi

該腳本將檢查本地密鑰對~/.ssh/{id_rsa,id_rsa.pub}文件是否存在,還檢查私鑰文件的權限是否爲600。如果缺少某個文件,將自動新創建密鑰對文件,最後分發公鑰到目標主機。

1.8.3 公鑰認證之——服務端分發私鑰

對於基於公鑰認證的身份驗證機制,除了上面客戶端分發公鑰到服務端的方法,還可以通過分發服務端私鑰到客戶端來實現。

先理清下公鑰認證的原理:客戶端要連接服務端,並告訴服務端要使用那對密鑰對,然後客戶端訪問自己的私鑰,服務端檢測對應的公鑰來決定該公鑰所對應的客戶端是否允許連接。所以對於基於公鑰認證的身份驗證,無論是客戶端分發公鑰,還是服務端分發私鑰,最終客戶端保存的一定是私鑰,服務端保存的一定是公鑰。

那麼服務端分發私鑰實現公鑰認證是如何實現的呢?步驟如下:假如客戶端爲172.16.10.5,服務端爲172.16.10.6。

(1).在服務端使用ssh-keygen生成密鑰對。

[[email protected] ~]# ssh-keygen -f ~/.ssh/id_rsa -P ''

(2).將上面生成的公鑰追加到自己的authorized_keys文件中。

[[email protected] ~]# ssh-copy-id 172.16.10.6

(3).將私鑰拷貝到客戶端,且路徑和文件名爲~/.ssh/id_rsa。

[[email protected] ~]# scp -p ~/.ssh/id_rsa* 172.16.10.5:/root/.ssh/

注意,第三步中也拷貝了公鑰,原因是客戶端連接服務端時會比較自己的公鑰和私鑰是否配對,如果不配對將直接忽略公鑰認證機制,所以會要求輸入密碼。可以將客戶端的公鑰刪除掉,或者將服務端生成的公鑰覆蓋到客戶端的公鑰上,都能完成公鑰認證。

雖說,服務端分發私鑰的方式很少用,但通過上面的步驟,想必對ssh基於公鑰認證的身份驗證過程有了更深入的理解。

1.9 ssh-keyscan和sshpass輕鬆實現非交互式雙機互信

ssh-keyscan命令可以生成~/.ssh/known_hosts文件中的公鑰信息,所以通過它可以免去主機認證過程中的交互式詢問。

sshpass命令可以自動填充ssh身份驗證過程中的交互式密碼詢問,所以通過它結合ssh-copy-id可以免去輸入密碼的交互式詢問。

例如,[email protected]主機需要和[email protected][email protected]這11臺主機之間配置雙機互信。參考如下過程:

# 1.在[email protected]上生成密鑰
$ ssh-keygen -t rsa -f ~/.ssh/id_rsa -N ''

# 2. 向[email protected]的~/.ssh/known_hosts文件追加各主機的主機認證信息
#    並將公鑰分發給其它節點
for host in 192.168.100.{23..33};do
    ssh-keyscan $host >>~/.ssh/known_hosts 2>/dev/null
    # -p指定的是密碼
    sshpass -p'123456' ssh-copy-id [email protected]$host &>/dev/null
done

1.10 expect實現ssh/scp完全非交互(批量)

expect工具可以在程序發出交互式詢問時按條件傳遞所需的字符串,例如詢問yes/no自動傳遞y或yes,詢問密碼時自動傳遞指定的密碼等,這樣就能讓腳本完全實現非交互。

顯然,ssh等客戶端命令基於密碼認證時總是會詢問密碼,即使是基於公鑰認證,在建立公鑰認證時也要詢問一次密碼。另外,在ssh主機驗證時還會詢問是否保存host key。這一切都可以通過expect自動回答。

關於expect工具,它使用的是tcl語言,雖說應用在expect上的tcl語言並非太複雜,但這並非本文內容,如有興趣,可網上搜索或直接閱讀man文檔。

首先安裝expect工具。

[[email protected] ~]# yum -y install expect

1.10.1 scp自動應答腳本

以下是scp自動問答的腳本。

[[email protected] ~]# cat autoscp.exp
#!/usr/bin/expect
########################################################### # description: scp without interactive # # author : 駿馬金龍 # # blog : http://www.cnblogs.com/f-ck-need-u/ # ########################################################### set timeout 10 set user_hostname [lindex $argv 0] set src_file [lindex $argv 1] set dest_file [lindex $argv 2] set password [lindex $argv 3] spawn scp $src_file $user_hostname:$dest_file expect { "(yes/no)?" { send "yes\n" expect "*assword:" { send "$password\n"} } "*assword:" { send "$password\n" } } expect "100%" expect eof

用法:autoscp.exp [[email protected]]hostname src_file dest_file [password]

該自動回答腳本可以自動完成主機驗證和密碼認證,即使已經是實現公鑰認證的機器也沒問題,因爲公鑰認證機制默認優先於密碼認證,且此腳本的password項是可選的,當然,在沒有實現公鑰認證的情況下,password是必須項,否則expect實現非交互的目的就失去意義了。

以下是幾個示例:

[[email protected] ~]# ./autoscp.exp 172.16.10.6 /etc/fstab /tmp 123456
spawn scp /etc/fstab 172.16.10.6:/tmp
The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established.
RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf.
Are you sure you want to continue connecting (yes/no)? yes          # 主機驗證時詢問是否保存host key,自動回答yes
Warning: Permanently added '172.16.10.6' (RSA) to the list of known hosts.
[email protected]'s password:                                        # 密碼認證過程,自動回答指定的密碼"123456"
fstab                                                        100%  805     0.8KB/s   00:00

也可以指定完成的用戶名和主機名。

[[email protected] ~]# ./autoscp.exp [email protected] /etc/fstab /tmp 123456
spawn scp /etc/fstab [email protected]:/tmp
[email protected]'s password:         
fstab                                 100%  805     0.8KB/s   00:00

1.10.2 ssh-copy-id自動應答腳本

以下是在建立公鑰認證機制時,ssh-copy-id拷貝公鑰到服務端的自動應答腳本。

[[email protected] ~]# cat /tmp/autocopy.exp
#!/usr/bin/expect

###########################################################
#  description: scp without interactive                   #
#  author     : 駿馬金龍                                   #
#  blog       : http://www.cnblogs.com/f-ck-need-u/       #
###########################################################

set timeout 10
set user_hostname [lindex $argv 0]
set password [lindex $argv 1]
spawn ssh-copy-id $user_hostname
    expect {
        "(yes/no)?"
        {
            send "yes\n"
            expect "*assword:" { send "$password\n"}
        }
        "*assword:"
        {
            send "$password\n"
        }
    }
expect eof

用法:autocopy.exp [[email protected]]hostname password

以下是一個示例,

[[email protected] ~]# /tmp/autocopy.exp [email protected] 123456
spawn ssh-copy-id [email protected]
The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established.
RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf.
Are you sure you want to continue connecting (yes/no)? yes      # 主機認證時,自動應答yes
Warning: Permanently added '172.16.10.6' (RSA) to the list of known hosts.
[email protected]'s password:                                    # 密碼認證時自動輸入密碼"123456"
Now try logging into the machine, with "ssh '[email protected]'", and check in:
 
  .ssh/authorized_keys
 
to make sure we haven't added extra keys that you weren't expecting.

如果要實現批量非交互,則可以寫一個shell腳本調用該expect腳本。例如:

[[email protected] ~]# cat /tmp/sci.sh
#!/bin/bash

###########################################################
#  description: scp without interactive                   #
#  author     : 駿馬金龍                                   #
#  blog       : http://www.cnblogs.com/f-ck-need-u/       #
###########################################################
 
passwd=123456               # 指定要傳遞的密碼爲123456
user_host=`awk '{print $3}' ~/.ssh/id_rsa.pub`   # 此變量用於判斷遠程主機中是否已添加本機信息成功
 
for i in [email protected]  
do
        /tmp/autocopy.exp $i $passwd >&/dev/null
        ssh $i "grep "$user_host" ~/.ssh/authorized_keys" >&/dev/null  # 判斷是否添加本機信息成功
        if [ $? -eq 0 ];then
                echo "$i is ok"
        else
                echo "$i is not ok"
        fi
done

用法:/tmp/sci.sh [[email protected]]hostname

其中hostname部分可以使用花括號展開方式枚舉。但有個bug,最好ssh-copy-id的目標不要是腳本所在的本機,可能會強制輸入本機密碼,但批量腳本autocopy.exp則沒有此bug。

例如:

[[email protected] tmp]# /tmp/sci.sh 172.16.10.3 172.16.10.6
172.16.10.3 is ok
172.16.10.6 is ok
[[email protected] tmp]# /tmp/sci.sh 172.16.10.{3,6}
172.16.10.3 is ok
172.16.10.6 is ok
[[email protected] tmp]# /tmp/sci.sh [email protected] 172.16.10.6
[email protected] is ok
172.16.10.6 is ok

1.11 ssh連接速度慢的幾個原因和解決辦法

ssh連接包括兩個階段:主機驗證階段和身份驗證階段。這兩個階段都可能導致連接速度慢。

具體是哪個階段的速度慢,完全可以通過肉眼看出來:

(1).卡着很久才提示保存host key肯定是主機驗證過程慢。

(2).主機驗證完成後卡着很久才提示輸入密碼,肯定是身份驗證過程慢。

其中主機驗證過程慢的原因,可能是網絡連接慢、DNS解析慢等原因。網絡連接慢,ssh對此毫無辦法,而DNS解析慢,ssh是可以解決的,解決方法是將ssh服務端的配置文件中UseDNS設置爲no(默認爲yes)。

而身份驗證慢的原因,則考慮ssh的身份驗證順序:gssapi,host-based,publickey,keyboard-interactive,password。其中gssapi認證順序是比較慢的,所以解決方法一是在ssh客戶端配置文件中將GSSAPI認證機制給關掉,解決方法二是在ssh客戶端配置文件中使用PreferredAuthentications指令修改身份驗證順序。

方法一修改:GSSAPIAuthentication yes

方法二修改:PreferredAuthentications publickey,password,gssapi,host-based,keyboard-interactive

如果感受不到哪個階段導致速度變慢,可以使用ssh或scp等客戶端工具的"-vvv"選項進行調試,看看是卡在哪個地方,不過,想看懂"-vvv"的過程,還是比較考驗耐心的。

1.12 SSH安全隧道(ssh躍牆之術)

 

參考:https://www.cnblogs.com/f-ck-need-u/p/7129122.html

基礎服務類系列文章:http://www.cnblogs.com/f-ck-need-u/p/7048359.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章