Skip to content

Instantly share code, notes, and snippets.

@qxj
Last active December 17, 2016 09:37
Show Gist options
  • Save qxj/1a42020d1cccf69bc7192c36efc8b337 to your computer and use it in GitHub Desktop.
Save qxj/1a42020d1cccf69bc7192c36efc8b337 to your computer and use it in GitHub Desktop.
利用expect脚本自动登录跳板机+线上机器
#!/usr/bin/expect
# Name: autoexpect - generate an Expect script from watching a session
#
# Description:
#
# Given a program name, autoexpect will run that program. Otherwise
# autoexpect will start a shell. Interact as desired. When done, exit
# the program or shell. Autoexpect will create a script that reproduces
# your interactions. By default, the script is named script.exp.
# See the man page for more info.
#
# Author: Don Libes, NIST
# Date: June 30 1995
# Version: 1.4b
set filename "script.exp"
set verbose 1
set conservative 0
set promptmode 0
set option_keys ""
proc check_for_following {type} {
if {![llength [uplevel set argv]]} {
puts "autoexpect: [uplevel set flag] requires following $type"
exit 1
}
}
while {[llength $argv]>0} {
set flag [lindex $argv 0]
if {0==[regexp "^-" $flag]} break
set argv [lrange $argv 1 end]
switch -- $flag \
"-c" {
set conservative 1
} "-C" {
check_for_following character
lappend option_keys [lindex $argv 0] ctoggle
set argv [lrange $argv 1 end]
} "-p" {
set promptmode 1
} "-P" {
check_for_following character
lappend option_keys [lindex $argv 0] ptoggle
set argv [lrange $argv 1 end]
} "-Q" {
check_for_following character
lappend option_keys [lindex $argv 0] quote
set argv [lrange $argv 1 end]
} "-f" {
check_for_following filename
set filename [lindex $argv 0]
set argv [lrange $argv 1 end]
} "-quiet" {
set verbose 0
} default {
break
}
}
#############################################################
# Variables Descriptions
#############################################################
# userbuf buffered characters from user
# procbuf buffered characters from process
# lastkey last key pressed by user
# if undefined, last key came from process
# echoing if the process is echoing
#############################################################
# Handle a character that came from user input (i.e., the keyboard)
proc input {c} {
global userbuf lastkey
send -- $c
append userbuf $lastkey
set lastkey $c
}
# Handle a null character from the keyboard
proc input_null {} {
global lastkey userbuf procbuf echoing
send -null
if {$lastkey == ""} {
if {$echoing} {
sendcmd "$userbuf"
}
if {$procbuf != ""} {
expcmd "$procbuf"
}
} else {
sendcmd "$userbuf"
if {$echoing} {
expcmd "$procbuf"
sendcmd "$lastkey"
}
}
cmd "send -null"
set userbuf ""
set procbuf ""
set lastkey ""
set echoing 0
}
# Handle a character that came from the process
proc output {s} {
global lastkey procbuf userbuf echoing
send_user -raw -- $s
if {$lastkey == ""} {
if {!$echoing} {
append procbuf $s
} else {
sendcmd "$userbuf"
expcmd "$procbuf"
set echoing 0
set userbuf ""
set procbuf $s
}
return
}
regexp (.)(.*) $s dummy c tail
if {$c == $lastkey} {
if {$echoing} {
append userbuf $lastkey
set lastkey ""
} else {
if {$procbuf != ""} {
expcmd "$procbuf"
set procbuf ""
}
set echoing 1
}
append procbuf $s
if {[string length $tail]} {
sendcmd "$userbuf$lastkey"
set userbuf ""
set lastkey ""
set echoing 0
}
} else {
if {!$echoing} {
expcmd "$procbuf"
}
sendcmd "$userbuf$lastkey"
set procbuf $s
set userbuf ""
set lastkey ""
set echoing 0
}
}
# rewrite raw strings so that can appear as source code but still reproduce
# themselves.
proc expand {s} {
regsub -all "\\\\" $s "\\\\\\\\" s
regsub -all "\r" $s "\\r" s
regsub -all "\"" $s "\\\"" s
regsub -all "\\\[" $s "\\\[" s
regsub -all "\\\]" $s "\\\]" s
regsub -all "\\\$" $s "\\\$" s
return $s
}
# generate an expect command
proc expcmd {s} {
global promptmode
if {$promptmode} {
regexp ".*\[\r\n]+(.*)" $s dummy s
}
cmd "expect -exact \"[expand $s]\""
}
# generate a send command
proc sendcmd {s} {
global send_style conservative
if {$conservative} {
cmd "sleep .1"
}
cmd "send$send_style -- \"[expand $s]\""
}
# generate any command
proc cmd {s} {
global fd
puts $fd "$s"
}
proc verbose_send_user {s} {
global verbose
if {$verbose} {
send_user -- $s
}
}
proc ctoggle {} {
global conservative send_style
if {$conservative} {
cmd "# conservative mode off - adding no delays"
verbose_send_user "conservative mode off\n"
set conservative 0
set send_style ""
} else {
cmd "# prompt mode on - adding delays"
verbose_send_user "conservative mode on\n"
set conservative 1
set send_style " -s"
}
}
proc ptoggle {} {
global promptmode
if {$promptmode} {
cmd "# prompt mode off - now looking for complete output"
verbose_send_user "prompt mode off\n"
set promptmode 0
} else {
cmd "# prompt mode on - now looking only for prompts"
verbose_send_user "prompt mode on\n"
set promptmode 1
}
}
# quote the next character from the user
proc quote {} {
expect_user -re .
send -- $expect_out(buffer)
}
if {[catch {set fd [open $filename w]} msg]} {
puts $msg
exit
}
exec chmod +x $filename
verbose_send_user "autoexpect started, file is $filename\n"
# calculate a reasonable #! line
set expectpath /usr/local/bin ;# prepare default
foreach dir [split $env(PATH) :] { ;# now look for real location
if {[file executable $dir/expect] && ![file isdirectory $dir/expect]} {
set expectpath $dir
break
}
}
cmd "#![set expectpath]/expect -f
#
# This Expect script was generated by autoexpect on [timestamp -format %c]
# Expect and autoexpect were both written by Don Libes, NIST."
cmd {#
# Note that autoexpect does not guarantee a working script. It
# necessarily has to guess about certain things. Two reasons a script
# might fail are:
#
# 1) timing - A surprising number of programs (rn, ksh, zsh, telnet,
# etc.) and devices discard or ignore keystrokes that arrive "too
# quickly" after prompts. If you find your new script hanging up at
# one spot, try adding a short sleep just before the previous send.
# Setting "force_conservative" to 1 (see below) makes Expect do this
# automatically - pausing briefly before sending each character. This
# pacifies every program I know of. The -c flag makes the script do
# this in the first place. The -C flag allows you to define a
# character to toggle this mode off and on.
set force_conservative 0 ;# set to 1 to force conservative mode even if
;# script wasn't run conservatively originally
if {$force_conservative} {
set send_slow {1 .1}
proc send {ignore arg} {
sleep .1
exp_send -s -- $arg
}
}
#
# 2) differing output - Some programs produce different output each time
# they run. The "date" command is an obvious example. Another is
# ftp, if it produces throughput statistics at the end of a file
# transfer. If this causes a problem, delete these patterns or replace
# them with wildcards. An alternative is to use the -p flag (for
# "prompt") which makes Expect only look for the last line of output
# (i.e., the prompt). The -P flag allows you to define a character to
# toggle this mode off and on.
#
# Read the man page for more info.
#
# -Don
}
cmd "set timeout -1"
if {$conservative} {
set send_style " -s"
cmd "set send_slow {1 .1}"
} else {
set send_style ""
}
if {[llength $argv]>0} {
eval spawn -noecho $argv
cmd "spawn $argv"
} else {
spawn -noecho $env(SHELL)
cmd "spawn \$env(SHELL)"
}
cmd "match_max 100000"
set lastkey ""
set procbuf ""
set userbuf ""
set echoing 0
remove_nulls 0
eval interact $option_keys {
-re . {
input $interact_out(0,string)
} -o -re .+ {
output $interact_out(0,string)
} eof {
cmd "expect eof"
return
}
}
close $fd
verbose_send_user "autoexpect done, file is $filename\n"
#!/usr/bin/expect
#
# Copyright (C) 2016 Julian Qian
#
# @file relay
# @author Julian Qian <[email protected]>
# @created 2016-11-09 12:15:41
#
#
# 使用方法:
# 1) 如果未登录relay,则输入密码(仅需要输入6位动态密码),之后跳转到目标主机;
# 2) 如果复用连接自动登录relay,则直接跳转到目标主机。
#
# 参数说明:
# 如果需要跳转到多个目标主机,可以修改下面代码的switch部分进行配置。
#
set timeout 10
set host [lindex $argv 0]
set relay_host <relay hostname>
set relay_user <relay username>
set relay_pass <relay password>
set target_host ""
set target_pass ""
switch -- $host \
"" {
set target_host ads-ml01
} "1" {
set target_host ads-ml01
} "2" {
set target_host ads-ml02
} default {
set target_host $host
break
}
# debug info
send_user "logining into relay server...\n"
send_user "relay_host: $relay_host\n"
send_user "target_host: $target_host\n"
# relay -> target host
proc go_target { target_host target_pass } {
if {$target_host == ""} {
send_user "no target_host is specified\n"
return
}
send "ssh ${target_host}\n"
expect {
"*assword*" {
send_user "logining into target host...\n"
send -- "${target_pass}\n"
exp_continue
}
"$ " {
send "sudo -u user -i \n"
}
}
expect "$ "
send "cd /opt/xxx\n"
expect "$ "
if {$target_host == "ads-ml01"} {
send "deactivate\n"
expect "$ "
send "source /opt/xxx/virtualenv/bin/activate\n"
expect "$ "
}
interact
}
# local -> relay host
spawn ssh "${relay_user}@${relay_host}"
# spawn ssh "${relay_host}"
expect {
"*rbash*" { go_target $target_host $target_pass }
"*Password*" {
send_user "please enter token (6 digit):"
expect_user -re "(.*)\n"
set veri_code $expect_out(1,string)
send -- "${relay_pass}${veri_code}\n"
expect {
"*rbash*" { go_target $target_host $target_pass }
"*Password*" {
send_user "Wrong token, exit."
exit
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment