起因

之前同事给过一个自动登录跳板机的脚本,但是公司的跳板机升级之后,脚本就失效了,尝试修改过但也没能成功。

最近在学习rust,正好看到有人用python和go实现了这个功能,就用rust来实现了一版(当然主要是用别人的库)。

代码

use std::env;

use google_authenticator::GoogleAuthenticator;

fn main() {
    let args: Vec<String> = env::args().collect();
    let secret = args[1].clone();
    let auth = GoogleAuthenticator::new();
    let otp = auth.get_code(&secret, 0).unwrap();
    println!("{}", otp);
}
[package]
name = "ootp-code"
version = "0.1.0"
edition = "2021"

[dependencies]
google-authenticator = "0.4"

[profile.dev]
# This isn't required for development builds, but makes development
# build behavior match release builds. To enable unwinding panics
# during development, simply remove this line.
panic = "abort" # Abort on panic

[profile.release]
opt-level = "z"     # Optimize for size.
lto = true        # Enable Link Time Optimization
codegen-units = 1 # Reduce number of codegen units to increase optimizations.
panic = "abort"   # Abort on panic
strip = true      # Automatically strip symbols from the binary.

shell脚本 server.exp

#!/usr/bin/expect
 
# 定义基础变量
# 设置默认超时时间为3秒,给予足够的响应时间
set timeout 3        
# 堡垒机地址                  
set TERMSERV "堡垒机地址"
 # MFA验证码,初始为空                  
set mfa_code ""   
# 目标服务器参数,初始为空                    
set arg_server ""                      

# 参数检查和处理
# 获取命令行参数数量
set arg_count [llength $argv]          

# 检查参数数量并处理
if {$arg_count == 2} {
    # 第一个参数:MFA密钥
    set secret [lindex $argv 0]        
    # 第二个参数:目标服务器
    set arg_server [lindex $argv 1]    
} else {
    puts "Usage: $argv0 <secret> <server>"
    exit 1
}

# 开始登录流程
puts "准备登录到 JumpServer 服务器 ..."
# 使用 ootp-code 工具生成 MFA 验证码
set mfa_code [exec ./ootp-code $secret]
puts "mfa_code=$mfa_code,arg_server=$arg_server"

# 启动新的bash会话并设置字符编码
spawn bash
expect -timeout 2 "$"
send "export LC_CTYPE=en_US.UTF-8\r"

# 连接堡垒机
#在原脚本基础上做了一定的修改,因为我习惯在.ssh/config中配置好跳板机的别名、密钥等信息
spawn ssh $TERMSERV 

# 自动处理MFA验证
expect {
    -timeout 2
    "*OTP Code*" {                    
        send "$mfa_code\r"            
    }
}

# 如果提供了服务器参数,自动选择服务器
if {$arg_count==2} {
    expect {
        -timeout 2
        "Opt>" {                      
            send "$arg_server\r"       
        }
    }
}

# 自动切换到root用户(针对测试环境)
expect {
    -timeout 2
    "*java_dev@tjvm*" {              
        send "sudo su -\r"            
    }
}

# 将控制权交给用户
# 允许用户交互操作
interact

server.exp脚本来源https://yujinping.top/post/linux/how_to_auto_login_jump_server_with_google_mfa_code/,做了一定程度的修改,增加了timeout的一点休眠,原脚本偶尔会出现第一次无法正常登录的情况,增加了测试环境自动切换root用户的expect

最终用rust打包出来的二进制文件只有303KB,比文章中的python和go的二进制文件小很多。

使用

./server.exp 跳板机密钥 服务器IP

踩坑记录

rust有很多otp的库,最开始用了ootp等库,但是都没能成功都能算出code但过不了跳板机验证,最后用了google-authenticator这个库才成功。