背景
为了安全,多数公司会采用跳板机的方式访问内网服务器,登录需要输入AD密码和谷歌验证码,非常繁琐,这里提供一个基于 expect
脚本的方法,实现一键登录跳板机
注:笔者在 MacOS
和 Ubuntu20.04
环境下测试ok,其他环境大同小异,按需修改即可。
方法
此方式思路是利用 expect
脚本交互式输入预设的AD密码,输入实时获取的谷歌验证码(使用 oathtool
或 python authenticator
等工具)
自动获取谷歌验证码
所需依赖
安装以下工具,各平台安装方法请自行搜索:
expect
oathtool
(推荐)- 或者使用
python
包
使用指南:Ubuntu man1/oathtool.1.html
安装
sudo apt install -y oathtool
使用
- 获取谷歌验证码的
SECRET
字符串 - 执行
oathtool --totp -b ${SECRET}
即可获取到当前时间的动态码
方法二:python authenticator
安装
pip install authenticator
若不成功,请先升级 pip
到最新版本
配置
- 执行
authenticator add $user
(AD账号无邮箱后缀) - 提示
Enter passphrase
这里输入2次AD密码 - 提示
Enter shared secret
需要把谷歌验证码的 SECRET
字符串输入到这里 - 到这里就配置ok了,执行
authenticator generate
,输入AD密码,看到谷歌验证码正常输出就是成功了
能够自动获取谷歌验证码之后就可以使用expect脚本来实现自动登录了
注意点
多数公司的AD密码可能需要每隔一段时间更新一次,在更新AD密码后,使用 authenticator
仍然需要输入旧密码,所以下述脚本内相应的做了一下兼容
expect脚本
功能
1
2
3
4
5
6
7
| $ jump --help
Usage: jump [--user <username>] [--auth <auth_type>] [--type <link_type>] [--zone <jump_zone>] [--help]
--user <username> Specify the username to use (default: $DEFAULT_USER)
--auth <auth_type> Specify the auth type, 'py' for using authenticator (default: oathtool)
--type <link_type> Specify the link type, 'ssh' or 'sftp' (default: ssh)
--zone <jump_zone> Specify the jump server zone, 'bj' or 'sh' (default: sh)
--help Display this help message
|
使用
- 把
users
相关的几个变量填写好PASSWORD
AD密码PASSWORD_GEN
生成密码SECRET
密钥
- 假设保存文件为
~/jump.exp
- 执行脚本即可自动化登录到跳板机
expect ~/jump.exp
- 也可以给脚本加上可执行权限
chmod a+x ~/jump.exp
- 然后直接执行即可
~/jump.exp
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
| #!/usr/bin/expect
# 设置超时
set timeout 1000
set JUMP_ZONE sh
set LINK_TYPE ssh
set DEFAULT_USER userAAA
# 扁平化关联数组,使用键名结合用户名
array set users {
userBBB_PASSWORD "xxxxxxxxxx"
userBBB_PASSWORD_GEN "xxxxxxxxxx"
userBBB_SECRET "xxxxxxxxxx"
userAAA_PASSWORD "xxxxxxxxxx"
userAAA_PASSWORD_GEN "xxxxxxxxxx"
userAAA_SECRET "xxxxxxxxxx"
}
# 初始化变量
set USERNAME $DEFAULT_USER
set use_authenticator 0
# 打印 usage 信息
proc print_usage {} {
puts {Usage: jump [--user <username>] [--auth <auth_type>] [--type <link_type>] [--help]}
puts {--user <username> Specify the username to use (default: $DEFAULT_USER)}
puts {--auth <auth_type> Specify the auth type, 'py' for using authenticator (default: oathtool)}
puts {--type <link_type> Specify the link type, 'ssh' or 'sftp' (default: ssh)}
puts {--zone <jump_zone> Specify the jump server zone, 'bj' or 'sh' (default: sh)}
puts {--help Display this help message}
exit 0
}
# 解析命令行参数
for {set i 0} {$i < [llength $argv]} {incr i} {
if {[lindex $argv $i] eq "--user"} {
if {[expr {$i + 1}] < [llength $argv]} {
set USERNAME [lindex $argv [expr $i+1]]
} else {
puts "Error: --user requires an argument"
exit 1
}
} elseif {[lindex $argv $i] eq "--auth"} {
if {[expr {$i + 1}] < [llength $argv]} {
if {[lindex $argv [expr $i+1]] eq "py"} {
set use_authenticator 1
}
} else {
puts "Error: --auth requires an argument"
exit 1
}
} elseif {[lindex $argv $i] eq "--type"} {
if {[expr {$i + 1}] < [llength $argv]} {
set LINK_TYPE [lindex $argv [expr $i+1]]
if {$LINK_TYPE ne "ssh" && $LINK_TYPE ne "sftp"} {
puts "Error: Invalid link type. Use 'ssh' or 'sftp'."
exit 1
}
} else {
puts "Error: --type requires an argument"
exit 1
}
} elseif {[lindex $argv $i] eq "--zone"} {
if {[expr {$i + 1}] < [llength $argv]} {
set JUMP_ZONE [lindex $argv [expr $i+1]]
if {$JUMP_ZONE ne "sh" && $JUMP_ZONE ne "bj"} {
puts "Error: Invalid jump zone. Use 'sh' or 'bj'."
exit 1
}
} else {
puts "Error: --zone requires an argument"
exit 1
}
} elseif {[lindex $argv $i] eq "--help"} {
print_usage
}
}
# 检查用户名是否存在
if {[info exists users(${USERNAME}_PASSWORD)]} {
set PASSWORD $users(${USERNAME}_PASSWORD)
set PASSWORD_GEN $users(${USERNAME}_PASSWORD_GEN)
set SECRET $users(${USERNAME}_SECRET)
} else {
puts "Error: User $USERNAME not found"
exit 1
}
# 获取验证代码
if {$use_authenticator} {
# 使用 authenticator generate 生成验证码
spawn authenticator generate
expect {
"passphrase:" { send "$PASSWORD_GEN\r" }
timeout { puts "Error: Authenticator timeout"; exit 1 }
}
expect {
"seconds" {
set found [regexp {([0-9]{6})} $expect_out(buffer) match verify_code]
if {$found == 1} {
send \x03
} else {
puts "Error: Invalid verification code"
exit 1
}
}
timeout { puts "Error: Failed to retrieve verification code"; exit 1 }
}
} else {
# 使用 oathtool 生成验证码
set verify_code [exec oathtool --totp -b $SECRET]
if {$verify_code eq ""} {
puts "Error: Failed to generate verification code"
exit 1
}
}
# 启动连接并处理交互
spawn ${LINK_TYPE} ${USERNAME}@jump-${JUMP_ZONE}.sensetime.com
expect {
"yes/no" { send "yes\r"; exp_continue }
"Verification code:" { send "$verify_code\r"; exp_continue }
"assword:" { send "$PASSWORD\r"; exp_continue }
"MFA auth" { send "$verify_code\r"; exp_continue }
"Opt>" { }
"sftp>" {
send "ls\r"
expect "sftp>"
}
timeout { puts "Error: Connection timeout"; exit 1 }
eof { puts "Error: Unexpected EOF"; exit 1 }
}
# 进入交互模式
interact
|