Telnet 和 SSH 協議是 Internet 遠程登錄服務的標準協議和主要方式,它們為用戶提供了在本地機器上完成遠程機器上工作的能力.用戶使用 Telnet 或者 SSH 軟體連接遠程伺服器,在 Telnet 或者 SSH 軟體中輸入的命令就會在伺服器上運行.Telnet 協議本質上是不安全的,因為它使用明文傳送數據、用戶賬號和口令,很容易受到中間人攻擊方式的攻擊;而 SSH 協議則是比較可靠、專為遠程登錄會話和其他網路服務提供安全性的協議.通過 SSH 可以對所有傳輸的數據進行加密,也能夠防止 DNS 欺騙和 IP 欺騙.本文主要針對 SSH 協議進行闡述,使用的 SSH 軟體是 OpenSSH,它是開放源代碼的免費的 SSH 替代軟體包. 用戶使用 Telnet 或者 SSH 登錄不同的操作系統,會得到不同的返回信息.用戶可以編寫程序根據運程登錄的返回信息進行判斷和處理,從而實現遠程伺服器系統的自動化登錄.本文將先介紹實現遠程自動化登錄用到的技術,再詳細介紹如何實現 Unix/Linux 系統間遠程登錄自動化. 遠程系統自動化登錄機制簡介 所謂自動化遠程登錄,是指在用戶不干預的情況下,不需要手動輸入密碼,就能登錄到遠程系統.目前遠程登錄工具有兩種安全認證方式. 基於用戶口令的安全認證 當使用登錄賬戶登錄時,根據提示輸入口令,SSH 就會用安全密碼認證協議,將加密傳送給 SSHD 伺服器.認證成功后,就可以登錄到 SSHD 伺服器. 針對這種安全認證方式,可以讓程序自動輸入用戶名和密碼,實現自動化登錄.目前可以用 expect, C 或 Perl 來實現.如果採用 C, 需要很熟悉 TCP/IP 協議,實現起來比較複雜;如果用 expect, 由於 expect 是基於 Tcl 的,需要熟悉 Tcl 語法;如果用 perl 實現,需要採用 perl 的 expect.pm 這個包.但需要用戶輸入口令,因此這種認證方式存在著安全隱患. 基於 SSH key 交換的安全認證方式 用戶需要先在 SSH 客戶端為登錄賬戶創建一對密匙:私鑰(private key)和公鑰 (public key),然後把公鑰傳送到要登錄的 SSHD 伺服器上.當用戶使用 SSH 客戶端登錄 SSHD 伺服器時,SSH 客戶端就會向 SSHD 伺服器發出用登錄帳戶的密鑰進行安全驗證的請求;SSHD 伺服器收到請求,先在登錄帳號的主目錄下尋找對應的公鑰,並與客戶端發送過來的公鑰進行對比;如果兩個密鑰一致,SSHD 伺服器會用公鑰加密「質詢」(challenge),發送給 SSH 客戶端;SSH 客戶端收到「質詢」之後用客戶端的私鑰解密,再把它發送給 SSHD 伺服器.這樣就完成了安全認證的整個過程. 使用基於 SSH key 交換的認證方式,用戶只要將在 SSH 客戶端生成的公鑰複製到遠程的 SSHD 伺服器.當通過 SSH 客戶端登錄 SSHD 伺服器時,用戶不需要輸入密碼,就可以自動登錄到遠程 SSHD 伺服器.這種方式不僅簡便,
避免了用戶名和密碼的泄露,比第一種方式要安全. 使用 Expect 的自動化登錄 Expect 的基礎知識 Expect 是由 Don Libes 基於 Tcl 語言開發的,並被廣泛應用於互動式操作和自動化測試的場景之中,它尤其適用於需要對多台伺服器執行相同操作的環境中,可以大幅度得提高系統管理人員的工作效率.目前,大部分 Unix/Linux 系統安裝有 expect. 萬一系統中沒有,可以從 http://expect.nist.gov/ 下載相應的包安裝. Expect 作為基於 Tcl 的高級語言,增加了一些特殊的語法.傳統意義上的 Expect 是以 Tcl 擴展包的形式出現的,任何 Tcl 語言編寫的應用程序都可以載入 Expect 功能;此外,Expect 已經以模塊的方式移植到了 Perl 和 Python 語言中,因此用戶同樣可以在 Perl 和 Python 腳本中利用 Expect 強大的交互功能. Send,expect 和 spwan 是 Expect 語言最基本的命令.其中,send 命令會發送字元串給指定進程(process); expect 命令會等待接受該進程返回的結果並且會根據返回的字元串來決定下一步的操作;而 spwan 命令可以發起一個進程的運行. send 命令接收一個字元串做為參數併發送給指定的進程;從 send 「Hello world」 這行代碼中,send 會送出字元串「Hello world」( 不帶引號 ).如果 Expect 早已經開始與某一個程序進行交互,那麼這個字元串將被發送給該程序;而在通常情況下,這個字元串會被送到標準輸出設備. expect 命令則等待一個響應,通常是來自於 Expect 正在與之交互的進程,或者來自於標準輸入設備;它會等待一個指定的字元串或者滿足給定的正則表達式的任何字元串.我們可以創建一個名為 response.exp 的文件,來看 Expect 是如何處理的,其內容如下: #!expect – f expect 「hi\n」 send 「hello there\n」 然後在 shell 下面運行 」expect response.exp」,它會等待來自標準輸入設備的響應,直到用戶輸入 hi 並回車,它才會發送」hello there」到標準輸出設備,並回車.然後結束 expect 腳本的運行.但是,如果用戶沒有輸入 hi 並回車,那麼 expect 會繼續等待」hi\n」;輸入其他的字元並不會影響到 expect 的工作.通常情況下,expect 會一直等會輸入,直到最終超時退出.此外, expect 還支持使用正則表達式來預防 expect 匹配到未預想到的輸入數據. spawn 命令會調用另一個程序.它的第一個參數是要啟動程序的名字;剩餘的參數則會被傳遞給該程序做為參數.比如 spawn ftp linux.ibm.com">ftp.linux.ibm.com 命令會衍生出一個 ftp 進程,並且將 ftp.linux.ibm.com 做為參數傳遞給這個 ftp 進程. 用戶通過 spawn,send 和 expect 這三個基本命令,就可以編寫一段 Expect 程序來實現自動化工作. Expect 腳本實現 本節將利用基於用戶口令的安全認證方式,並使用 Expect 來實現 SSHD 伺服器的自動化登錄過程,並在登錄的會話中實現命令在 SSHD 伺服器端的執行.本文使用的具體實驗環境如下:用戶使用的 SSH 客戶端機器:操作系統均為 RHELS5.3, IP 地址為 192.168.0.3, Expect 版本為 version 5.43.0;遠程的 SSHD 伺服器:操作系統均為 RHELS5.3,IP 地址為 192.168.0.4,用戶名 / 密碼為 root/123456. 清單1.登錄 SSHD 伺服器的自動化腳本 #!/usr/bin/expect # 設置超時時間為 60 秒 set timeout 60 # 設置要登錄的主機 IP 地址 set host 192.168.0.4 # 設置以什麼名字的用戶登錄 set name root # 設置用戶名的登錄密碼 set password 123456 #spawn 一個 ssh 登錄進程 spawn ssh $host -l $name # 等待響應,第一次登錄往往會提示是否永久保存 RSA 到本機的 know hosts 列表中;等到回答后,在提示輸出密碼;之後就直接提示輸入密碼 expect { "(yes/no)?" { send "yes\n" expect "assword:" send "$pasword\n" } "assword:" { send "$password\n" } } expect "#" # 下面測試是否登錄到 $host send "uname\n" expect "Linux" send_user "Now you can do some operation on this terminal\n" # 這裡使用了 interact 命令,使執行完程序后,用戶可以在 $host 終端進行交互操作. Interact 如果要運行該腳本,可以參考如下的操作,假設 expect 腳本的文件名為 t1.expect.另外,在運行該腳本之前,需要將 t1.expect 文件設置成可執行的模式 ; 清單2.運行自動化登錄腳本的操作步驟 [root@redhat ~]chmod a x t1.expect [root@redhat ~]./t1.expect spawn ssh 192.168.0.4 -l root root@192.168.0.4's password: Last login: Fri Jun 12 15:36:01 2009 from 192.168.0.3 Red Hat Enterprise Linux Server release 5.1 (Tikanga) [root@c96m3h4ms01 ~]# uname Linux Now you can do some operation on this terminal [root@c96m3h4ms01 ~]# 基於 SSH 交換 Key 自動化登錄 SSH 證書簡介 SSH 證書使用一對密鑰 : 私鑰(private key)和公鑰 (public key).公鑰(public key)對數據進行加密
只能用於加密,私鑰(private key)只能對所匹配的公鑰(public key)加密過的數據進行解密.私鑰(private key)只保存你獨有的一些秘密信息.SSH 客戶端用其向 SSHD 伺服器證明自已的身份.公鑰是公開的,可以隨便將其放入 SSHD 伺服器上自已的帳號中 , 在認證時,進行私鑰和公鑰協商,如果匹配,那麼身份就得以證明,認證就成功. 目前所有的 OpenSSH 版本都應該既能使用 RSA 密鑰又能使用 DSA 密鑰.RSA 密鑰和 DSA 密鑰的生成命令和使用方法相同,本文僅介紹 RSA. 如果採用 SSH 密鑰認證的方式實現自動化登錄,用戶可以參考下面的章節. 生成密鑰對 ssh-keygen 程序生成的 RSA 密鑰的文件名默認為 id_rsa 和 id_rsa.pub,用戶也可以將其更改為別的名稱.下面」清單 3. 生成密鑰對」的操作過程將採用系統的默認值. 清單3. 生成密鑰對 [root@redhat ~]# ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory '/root/.ssh'. 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. The key fingerprint is: 4b:70:20:de:89:92:a9:fe:21:a4:9b:7c:6b:65:ae:e0 root@redhat [root@redhat ~]# ls -al .ssh total 20 drwx------ 2 root root 4096 May 10 02:51 . drwxr-x--- 11 root root 4096 May 10 02:51 .. -rw------- 1 root root 1675 May 10 02:51 id_rsa -rw-r--r-- 1 root root 397 May 10 02:51 id_rsa.pub 如果 ssh 目錄不存在,程序為自動創建本地 SSH 目錄 ~/.ssh,並將所生成的密鑰分成兩個文件存儲,私鑰為 id_rsa,公鑰為 id_rsa.pub. 配置自動化登錄 在配置自動化登錄的過程中,賬戶對應的公鑰需要被添加到 SSHD 伺服器端的配置文件.在 3.0 版本的 OPENSSH 中,用戶需要修改的文件為 authorized_keys,早於 3.0 的版本則使用 authorized_keys2 文件.將在客戶端生成的 id_rsa.pub 文件內容加入到 authorized_keys 或者 authorized_keys2 文件中即可完成配置工作. 為安全起見,要確保 $HOME/.ssh 目錄的安全,只有所有者才有 權寫入.如果遠程用戶的 SSH 配置文件的許可權設置不當,伺服器可能會拒絕進行認證. 下面是配置自動化登錄的具體過程,其場景如下: Openssh 為 OpenSSH_4.3p2SSH,將以 root 用戶登錄到 SSHD 伺服器(192.168.0.4)上,因此公鑰的內容將存放在 root/.ssh/authorized_keys. 清單4. 終端操作記錄 [root@redhat ~]# scp /root/.ssh/id_rsa.pub root@192.168.0.4:/tmp [root@redhat .ssh]# ssh 192.168.0.4 root@192.168.0.4's password: ****** [root@server ~]# cat /tmp/id_rsa.pub >> /root/.ssh/authorized_keys 如果用戶在生成 rsa 和 id_rsa.pub 的時候沒有輸入密碼,可以直接使用「ssh root@192.168.0.4」登錄,而不用輸入密碼. 而如果用戶在生成 rsa 和 id_rsa.pub 的時候輸入了密碼,需要進行以下兩步操作: 啟用 ssh-agent 認證代理 , 清單5. 終端操作記錄 [root@redhat ~]# ssh-agent $SHELL (2) 使用 ssh-add, 裝入私鑰,並輸入生成 rsa 和 id_rsa.pub 時輸入的密碼 清單6. 終端操作記錄 [root@redhat ~]# ssh-add Enter passphrase for /root/.ssh/id_rsa: Identity added: /root/.ssh/id_rsa (/root/.ssh/id_rsa) Identity added: /root/.ssh/id_dsa (/root/.ssh/id_dsa) [root@redhat ~]# ssh 192.168.0.4 Last login: Sat May 16 11:37:39 2009 from redhat 把私鑰保存在內存中,為認證提供服務,之後以 root 用戶 ssh 登錄時,就不用重複輸入密碼.其生命周期為 ssh-agent 啟動的那個 shell,當用戶退出該 shell 時,需要重新執行 ssh-agent 和 ssh-add. 只要密鑰配置好,以後登錄就是自動化了.因此本部分登錄過程不需要用腳本來實現,實現了前期拷貝 SSH key 到遠程伺服器,並添加到相應的位置,並測試自動登錄是否成功. 腳本實現配置自動化登錄 本節的腳本實現基於 SSH key 交換的安全認證方式,並利用 Expect 來實現自動化登錄.本節所實現的腳本可以運行在以下的實驗環境中:用戶使用的 SSH 客戶端機器:操作系統均為 RHELS5.3, IP 地址為 192.168.0.3, Expect 版本為 version 5.43.0;遠程的 SSHD 伺服器:操作系統均為 RHELS5.3,IP 地址為 192.168.0.4,用戶名 / 密碼為 root/123456. Expect 腳本的內容如下: 清單7.基於 SSH key 交換的自動化登錄腳本 #!/usr/bin/expect # 判斷輸入的參數是否為 3 個,如果不為 3 個,就列印錯誤信息,退出該程序. if { $argc != 3 } { puts stderr "Usage: test1 host-address username host-password\n" exit 1 } # 設置超時時間為 60 秒 set timeout 60 # 將命令行輸入的第一個參數作為將要登錄的 SSHD 伺服器 set host [lindex $argv 0] # 第二個參數是用戶名,賦值給 name, 之後用 $name 格式來使用 set name [lindex $argv 1] # 第三個參數是以 $name 登錄 $host 的口令 set password [lindex $argv 2] ##set timeout 60 ##set password "cluster" ##set name 「root」 ##set host "192.168.0.4" #root 用戶的 rsa key 放在 /root/.ssh 中,其他用戶則放在 /home/$name/.ssh if { $name == "root"} { spawn scp /$name/.ssh/id_rsa.pub $name@$host:/tmp } else { spawn scp /home/$name/.ssh/id_rsa.pub $name@$host:/tmp } # 等待上個命令的響應 expect { "(yes/no)?" { send "yes\n" expect "assword:" send "$pasword\n" } "assword:" { send "$password\n" } } # 輸入密碼后,拷貝成功,出現 100% 字元串,作為預期響應 expect "100%" # 調用 ssh 以 $name 用戶名登錄到 $host 上 spawn ssh $host – l$name # 期待提示出入密碼的響應 expect "assword:" # 接收密碼 send "$password\n" expect ":~#" # 將剛剛拷貝的 rsa key 添加到用戶的 home 目錄下的 ./ssh/authorized_keys if { $name == "root"} { send "cat /tmp/id_rsa.pub >> /root/.ssh/authorized_keys\n" } else { send "cat /tmp/id_rsa.pub >> /home/$name/.ssh/authorized_keys\n" } expect ":~#" # 操作成功后,退回 SSH 客戶端機器 send "exit\n" expect "#" # 下面將測試能否自動登錄,不用輸入密碼 spawn ssh $host – l$name expect { "Welcome" { send_user "Auto login the server successfully!" } "assword:" { send_user "failed to login the server!" } } send "ls\n" expect ":~#" # 退出 $host send "exit\n" # 程序結束 expect eof 清單 7 基於 SSH key 交換的自動化登錄腳本註釋掉了主機名,用戶名和密碼,因此用戶在運行腳本時需要手工輸入主機名,用戶名和密碼.一旦 SSH Key 的交換完成,用戶就可以直接運行 「ssh host-address – l username」實現自動化登錄,而不再需要輸入用戶名密碼.