Total Pageviews

Sunday 3 April 2016

在linux桌面系统/mac上,编译expect,让你的ssh tunnel不断线


我们通过Shell可以实现简单的控制流功能,如:循环、判断等。但是对于需要交互的场合则必须通过人工来干预,有时候我们可能会需要实现和交互程序如telnet服务器等进行交互的功能。而EXPect就使用来实现这种功能的工具。

Expect 是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预。Expect的作者Don Libes在1990年开始编写Expect时对Expect做有如下定义:Expect是一个用来实现自动交互功能的软件套件(Expect [is a] software suite for automating interactive tools)。使用它系统管理员的可以创建脚本用来实现对命令或程序提供输入,而这些命令和程序是期望从终端(terminal)得到输入,一般来说这些 输入都需要手工输入进行的。Expect则可以根据程序的提示模拟标准输入提供给程序需要的输入来实现交互程序执行。甚至可以实现实现简单的BBS聊天机 器人。 :)

Expect是不断发展的,随着时间的流逝,其功能越来越强大,已经成为系统管理员的的一个强大助手。Expect需要Tcl编程语言的支持,要在系统上运行Expect必须首先安装Tcl。
     Expect 为用户提供一种机制,使用户能够自动执行一些交互式的任务。例如,通常我们在使用 telnet 的时候,都需要手动输入用户名、密码才能登录。而使用 Expect ,我们就可以实现全自动的 telnet 交互,不需用户干预。 Expect 由 Don Libes 开发 , 基于 TCL 内核 , 它的主页在 http://expect.nist.gov/

ActiveTcl is the industry-standard Tcl distribution, available for Windows, Linux, Mac OS X, Solaris, AIX and HP-UX.

在linux桌面系统上,编译如下:
wget http://freefr.dl.sourceforge.net/project/expect/Expect/5.45/expect5.45.tar.gz
tar zxvf expect5.45.tar.gz
cd expect5.45
./configure
make
sudo make install

这样就编译好了expect.

项目地址:https://sourceforge.net/projects/expect/
expect.sourceforge.net
http://www.nist.gov/el/msid/expect.cfm

用 expect 写了一个脚本,不担可以防止ssh 断线,还可以自动输入密码 。
关于 expect 我摘录一段在 中文维基 上的介绍:
ExpectUnix系统中用来进行自动化控制和测试的软件工具,由Don Libes制作,作为Tcl脚本语言的一个扩展,应用在交互式软件中如telnetftpPasswdfsckrlogintipssh等等。该工具利用Unix伪终端包装其子进程,允许任意程序通过终端接入进行自动化控制
有些 linux 系统里可能没有 expect , 你可以自己安装
在 debian/ubuntu 里 : apt-get install expect
在 opensuse 里:zypper in expect
在 fedora 里: yum install expect
当然还有一些其它发行版都有自己的安装方式。
至于Mac用户,可以通过homebrew安装(需要先安装homebrew,请自行Google)
brew install expect

你可以新建一个文件,比如 fuckgfw.sh , 将如下代码复制进去:

#!/usr/local/bin/expect
set timeout 60

spawn /usr/bin/ssh -g user@yourserver.com -p ssh_port -D 1081 
#这里的 user 为你自己的用户名,yourserver.com 为你自己的服务器域名或ip.
expect {
"password:" {
send "your_password\r"
#这里的your_password改为你的密码
}
}
interact {
timeout 60 { send " "}
}


加上可执行权限 chmod 755 fuckgfw.sh, 然后你就可以在它所在的目录执行 ./fuckgfw.sh 就行了。当然你也可以把它复制到 /usr/local/bin 或 /usr/bin 等目录下,以方便本机所有用户都可以快捷的使用。
注意: your_password的后面,不要忘了加上\r ,否则你仍需输入密码。
注:ssh 加上 -g (即在代码中的 ssh -D  1081  -g ) 可使局域网内其它机器使用我的机器做代理. 以我的情况为例:我在一个局域网内的 ip 为 192.168.1.16 , 当我联上服务器之后,我就可以使用 localhost:1081 做代理,同时我的局域网内的其它 ip 为 192.168.1.* 的机器,就可以用代理 192.168.1.16:1081 来穿墙啦。

哈,真爽。以后在终端登录ssh server所建立的ssh tunnel再也不会断线了。

附录:在linux vps上,创建有ssh登陆权限的账户的方法:
adduser your_username (注意:不要用useradd your_username这样的命令,这样的命令在linux vps上,无法生成/home/your_username/目录,而我们需要这样的目录,方能登陆ssh console)
passed your_username

注意:这种方法适合给自己用。给别人用是不太安全的!!

建立给别人使用的ssh账号的方法,见此文http://briteming.blogspot.com/2016/04/vpsssh.html的粉色字体部分。
------------------------------------------

#!/usr/bin/expect
# Leng Ganghua <hiweed@gmail.com>
# 2010.12.13
###########################################
# 服务器地址(或IP)
set SERVER “echo.supportedns.com
# 服务器 SSH 端口号
set PORT “2233
# SSH 用户名
set USER “demouser
# 密码
set PASSWD “demo
###########################################
set timeout 60
spawn /usr/bin/ssh -N -D 7070 -p $PORT -g $USER@$SERVER
expect {
“*(yes/no)*” { send “yes\r”}
“password:” { send “$PASSWD\r” }
}
interact {
timeout 60 { send " "}
}
把上面红色字体部分修改为你的ssh服务器地址、端口、用户名、密码,注意:上文中蓝色字体部分的-N记住一定要加上呀,不然可能会不能正常使用!

这个脚本的好处是能保存密码还能断线自动重连.

建立ssh账号的方法,见此文http://briteming.blogspot.com/2016/04/vpsssh.html的粉色字体部分。
-------------------------------

用linux脚本实现自动输入密码


使用Linux的程序员对输入密码这个举动一定不陌生,在Linux下对用户有严格的权限限制,干很多事情越过了权限就得输入密码,比如使用超级用户执行命令,又比如ftp、ssh连接远程主机等等,
那么问题来了,在脚本自动化执行的时候需要输入密码怎么办?比如你的脚本里有一条scp语句,总不能在脚本执行到这一句时手动输入密码吧
针对于ssh或scp命令,可能有人会回答是建立信任关系,关于建立ssh信任关系的方法请自行百度Google,只需要两行简单的命令即可搞定, 但这并不是常规的解决方案,如果是ftp连接就没辙了,况且,你不可能为了执行某些命令去给每个你要连接的主机都手动建立ssh信任,这已经偏离了今天主 题的本意,今天要说的是在脚本里自动输入密码,我们可以想象下,更优雅的方式应该是在脚本里自己配置密码,当屏幕交互需要输入时自动输入进去,要达到这样 的效果就需要用到expect

安装

CentOS下安装命令很简单,如下
sudo yum install expect
至于Mac用户,可以通过homebrew安装(需要先安装homebrew,请自行Google)
brew install expect

测试脚本

我们写一个简单的脚本实现scp拷贝文件,在脚本里配置密码,保存为scp.exp如下
#!/usr/bin/expect
set timeout 20

if { [llength $argv] < 2} {
    puts "Usage:"
    puts "$argv0 local_file remote_path"
    exit 1
}

set local_file [lindex $argv 0]
set remote_path [lindex $argv 1]
set passwd your_passwd

set passwderror 0

spawn scp $local_file $remote_path

expect {
    "*assword:*" {
        if { $passwderror == 1 } {
        puts "passwd is error"
        exit 2
        }
        set timeout 1000
        set passwderror 1
        send "$passwd\r"
        exp_continue
    }
    "*es/no)?*" {
        send "yes\r"
        exp_continue
    }
    timeout {
        puts "connect is timeout"
        exit 3
    }
}
注意,第一行很重要,通常我们的脚本里第一行是#!/bin/bash,而这里是你机器上expect程序的路径,说明这段脚本是由expect来解释执行的,而不是由bash解释执行,所以代码的语法和shell脚本也是不一样的,其中set passwd your_passwd设置成你自己的密码,然后执行如下命令
./scp.exp ./local_file user@host:/xx/yy/
执行前确保scp.exp有执行权限,第一个参数为你本地文件,第二个为远程主机的目录,运行脚本如果报错“connect is timeout”,可以把超时设长一点,第二行set timeout 20可以设置超时时间,单位是秒。

还能做什么

细心的同学一定发现了,其实expect提供的是和终端的一种交互机制,输入密码只是其中一种应用形式,只要是在终端阻塞需要输入时,都可以通过 expect脚本完成自动输入,比如前面脚本里配置了两种交互场景,一种是终端提示"password:"时输入密码,还有一种是提 示"yes/no)?"时输入“yes”,如果和远程主机是第一次建立连接,执行scp.exp脚本效果是这样的

所以我们可以根据终端的提示来配置输入命令,这样就能达到了自动化的效果。至于处理其它交互场景,只需要照着上面的脚本依葫芦画瓢就行了.
--------------

自动ssh的expect脚本


#!/usr/bin/expect 
set timeout 5
set cmd_prompt "]#|~]?"
set server [lindex $argv 0] 
set user [lindex $argv 1] 
set passwd [lindex $argv 2] 

spawn ssh -l $user $server 
expect { 
"(yes/no)" { send "yes\r"; exp_continue } 
"password:" { send "$passwd\r" } 
}
#expect "$cmd_prompt" interact
expect "*Last login*" interact