shell腳本實現無密碼交互的SSH自動登陸
ssh連接遠程主機時候詢問密碼,跟su、sudo命令的默認行為一樣,是不從stdin讀入數據的,據稱是為安全考慮,但是有時候在腳本當中確實需要無人守值的登陸。
搜索一下不難找到類似的例子,使用expect來完成密碼應答:
#!/bin/bash
auto_login_ssh () {
expect -c "set timeout -1;
spawn -noecho ssh -o StrictHostKeyChecking=no $2 ${@:3};
expect *assword:*;
send -- $1\r;
interact;";
}
auto_login_ssh passwd user@host
StrictHostKeyChecking=no參數讓ssh默認添加新主機的公鑰指紋,也就不會出現出現是否繼續yes/no的提示了。
expect很不錯,上述代碼基本可以達到要求了,能夠當翻牆用的ssh -D自動登陸,執行遠程命令等等,但是如果作為一個完全非交互的遠程工具,應該說還一差,不能返回整個連接執行過程是否成功。
使用expect后,程序的exit status是expect的,而不是ssh的,所以如果遇上連接不到的主機、密碼錯誤等情況,expect也一樣是正常退出,$?為0,所以需要對expect的代碼稍加修改;
#!/bin/bash
auto_smart_ssh () {
expect -c "set timeout -1;
spawn ssh -o StrictHostKeyChecking=no $2 ${@:3};
expect {
*assword:* {send -- $1\r;
expect {
*denied* {exit 2;}
eof
}
}
eof {exit 1;}
}
"
return $?
}
auto_smart_ssh passwd user@host ls /var
echo -e "\n---Exit Status: $?"
這段expect的Tcl代碼主要作用是,如果在輸入密碼后遇到Permission denied,肯定是腳本提供的帳號有問題,就直接讓expect按狀態2退出;而如果主機不可達No route to host, timed out, Connection refused等情況,ssh會直接退出,expect收到eof,讓其按狀態1退出。而因為這個設計本來就用於執行遠程命令后退出,不需要用戶交互,所以第9行的eof則是讓expect等待ssh退出,而不是不是進行interact了。
有這樣的處理,使用autosmartssh的腳本就可以根據返回值判斷執行過程的是否成功,而進行相應處理了。
openssh里面另外一個很好用的遠程文件傳輸工具scp,也以如法炮制:
auto_scp () {
expect -c "set timeout -1;
spawn scp -o StrictHostKeyChecking=no ${@:2};
expect {
*assword:* {send -- $1\r;
expect {
*denied* {exit 1;}
eof
}
}
eof {exit 1;}
}
"
return $?
}
auto_scp pass ~/myfile user@host:~/path/to/myfile
echo $?
后話:
如果僅僅是日常使用,為了避免經常輸入主機密碼的麻煩,最理想的方法是生產本機的公/私密鑰對,把指紋直接復制到遠程主機上,較新的openssh提供了ssh-copy-id工具:
ssh-keygen
ssh-copy-id user@host1
ssh-copy-id user@host2
ssh-copy-id user@host3
運行ssh-keygen時會問幾個問題,全部回車默認就是我們要的效果了,分別把密鑰分發到遠程主機后,以后執行ssh user@host,還是scp,都是直接完成了。
如果需要刪除遠程機器上對應本機本賬戶的密鑰,登陸到該賬戶,打開~/.ssh/authorized_keys文件,搜索你的用戶名,刪除那行,保存,即可。
當然也可以自動化:
auto_ssh_copy_id () {
expect -c "set timeout -1;
spawn ssh-copy-id $2;
expect {
*(yes/no)* {send -- yes\r;exp_continue;}
*assword:* {send -- $1\r;exp_continue;}
eof {exit 0;}
}";
}
搜索一下不難找到類似的例子,使用expect來完成密碼應答:
#!/bin/bash
auto_login_ssh () {
expect -c "set timeout -1;
spawn -noecho ssh -o StrictHostKeyChecking=no $2 ${@:3};
expect *assword:*;
send -- $1\r;
interact;";
}
auto_login_ssh passwd user@host
StrictHostKeyChecking=no參數讓ssh默認添加新主機的公鑰指紋,也就不會出現出現是否繼續yes/no的提示了。
expect很不錯,上述代碼基本可以達到要求了,能夠當翻牆用的ssh -D自動登陸,執行遠程命令等等,但是如果作為一個完全非交互的遠程工具,應該說還一差,不能返回整個連接執行過程是否成功。
使用expect后,程序的exit status是expect的,而不是ssh的,所以如果遇上連接不到的主機、密碼錯誤等情況,expect也一樣是正常退出,$?為0,所以需要對expect的代碼稍加修改;
#!/bin/bash
auto_smart_ssh () {
expect -c "set timeout -1;
spawn ssh -o StrictHostKeyChecking=no $2 ${@:3};
expect {
*assword:* {send -- $1\r;
expect {
*denied* {exit 2;}
eof
}
}
eof {exit 1;}
}
"
return $?
}
auto_smart_ssh passwd user@host ls /var
echo -e "\n---Exit Status: $?"
這段expect的Tcl代碼主要作用是,如果在輸入密碼后遇到Permission denied,肯定是腳本提供的帳號有問題,就直接讓expect按狀態2退出;而如果主機不可達No route to host, timed out, Connection refused等情況,ssh會直接退出,expect收到eof,讓其按狀態1退出。而因為這個設計本來就用於執行遠程命令后退出,不需要用戶交互,所以第9行的eof則是讓expect等待ssh退出,而不是不是進行interact了。
有這樣的處理,使用autosmartssh的腳本就可以根據返回值判斷執行過程的是否成功,而進行相應處理了。
openssh里面另外一個很好用的遠程文件傳輸工具scp,也以如法炮制:
auto_scp () {
expect -c "set timeout -1;
spawn scp -o StrictHostKeyChecking=no ${@:2};
expect {
*assword:* {send -- $1\r;
expect {
*denied* {exit 1;}
eof
}
}
eof {exit 1;}
}
"
return $?
}
auto_scp pass ~/myfile user@host:~/path/to/myfile
echo $?
后話:
如果僅僅是日常使用,為了避免經常輸入主機密碼的麻煩,最理想的方法是生產本機的公/私密鑰對,把指紋直接復制到遠程主機上,較新的openssh提供了ssh-copy-id工具:
ssh-keygen
ssh-copy-id user@host1
ssh-copy-id user@host2
ssh-copy-id user@host3
運行ssh-keygen時會問幾個問題,全部回車默認就是我們要的效果了,分別把密鑰分發到遠程主機后,以后執行ssh user@host,還是scp,都是直接完成了。
如果需要刪除遠程機器上對應本機本賬戶的密鑰,登陸到該賬戶,打開~/.ssh/authorized_keys文件,搜索你的用戶名,刪除那行,保存,即可。
當然也可以自動化:
auto_ssh_copy_id () {
expect -c "set timeout -1;
spawn ssh-copy-id $2;
expect {
*(yes/no)* {send -- yes\r;exp_continue;}
*assword:* {send -- $1\r;exp_continue;}
eof {exit 0;}
}";
}
