前置条件

  • 本机需要安装 python3 (3.11)
  • 本机支持 expect 命令及脚本

编写生成 google mfa 验证的 python 脚本

需要使用的库是 onetimepass 网址: https://github.com/tadeck/onetimepass

安装命令:

pip install onetimepass

安装好 onetimepass 之后,编写生成mfa的python脚本 code.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import onetimepass as otp # type: ignore

# 注意:如果你的电脑有多个python3版本,请在第1行选择合适的版本,同时确保 onetimepass 是安装在该解释器的环境下
# 否则可能会报找不到该模块的错误

if __name__ == "__main__":
    my_secret = '这里是你的MFA的密码'
    my_token = otp.get_totp(my_secret)
    print(my_token)

之后你可以将 code.py 设置为可执行程序,这样你不需要在命令行添加 python3 的命令。

# 给 code.py 增加执行权限
chmod +x ./code.py

之后,你执行

# 能够输出6位数字的MFA验证码
./code.py

编写 expect 脚本执行登录的动作 server.exp

#!/usr/bin/expect
 
# 登录堡垒机
set timeout 1
set TERMSERV "192.168.0.1"
set USER "JumpServerUser0"
set PASSWORD "JumpServerPassword"
set mfa_code ""
set arg_server ""

set arg_count [llength $argv]

if {$arg_count==1} {
    set arg_server [lindex $argv 0]
}
puts "准备登录到 JumpServer 服务器 ..."
set mfa_code [exec code.py]
puts "mfa_code=$mfa_code,arg_server=$arg_server"
spawn bash
send "export LC_CTYPE=en_US.UTF-8\r"
# 登录跳板机
spawn ssh -l $USER -p22 $TERMSERV

expect {
    # 自动输入密码
    "*assword:*" {
        send "$PASSWORD\r"
        exp_continue
    }
}
expect {
    # 自动输入 OTP Code 即 MFA 密码,由 code.py 生成
    "*OTP Code*" {
        send "$mfa_code\r"
    }
}
if {$arg_count==1} {
    expect {
        # 若进入JumpServer的选单,则自动输入ipv4的最后1位进入相应的服务器
        "Opt>" {
            send "$arg_server\r"
        }
    }
}

interact
# 执行完成后保持交互状态,把控制权交给控制台

最后,将编写好的 server.expcode.py 都放在一起,同时也给 server.exp 授权为可执行:

chmod +x ./server.exp

测试

命令行执行

# 自动登录JumpServer后进入 192.168.0.100的服务器
./server.exp 100

实测成功登录,再也不需要拿着手机看着 Google Authenticator应用生成验证码并输入了。

后记

由于登录到服务器后,通常情况下你需要上传或下载文件,而命令行状态下更多的是使用 rz 进行上传 和 sz 进行下载。 然而,你会发现用了这个 server.exp 脚本登录后无法使用这2个命令了,原因在于其不兼容 expect环境。 需要给 expect 套个 sh 的外壳。

你可以编写 s.sh 放在与 server.exp 相同文件夹下,通过该脚本再调用 server.exp就可以啦 :

#!/bin/sh
# 设置兼容类型为 en_US
export LC_CTYPE=en_US.UTF-8
$HOME/server.exp "$@"

测试

chmod +x ./s.sh
./s.sh 100

登录后就可以愉快的下载与上传文件了。

补充(20240426)

onetimepass 用起来偶尔会有生成otp不正确的情况发生,可以使用 Go 语言的库 gotp https://github.com/xlzd/gotp

参考上面的代码编译一个最简单的 mfa 执行程序即可,用来替换掉 ./code.py

更快、更安全

参考文章