news 2026/7/4 18:05:12

Python驱动SecureCRT实现Jumpserver MFA自动化登录实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python驱动SecureCRT实现Jumpserver MFA自动化登录实战

1. 项目概述与核心痛点

每次登录Jumpserver堡垒机,都要经历“输入账号密码 -> 掏出手机 -> 打开身份验证器App -> 查看并输入6位动态验证码”这一套标准流程。对于需要频繁登录的运维、开发人员来说,一天重复十几次,不仅打断了工作流,更是一种精神上的消耗。尤其是在处理紧急故障时,多一次手动操作就多一分焦虑。这个项目,就是为了解决这个看似微小却极其影响效率的痛点:利用Python脚本驱动SecureCRT,实现Jumpserver MFA(多因素认证)的全程自动化登录

你可能已经尝试过SecureCRT自带的“录制/回放脚本”功能,但面对需要动态交互的MFA验证码,它往往无能为力。而本方案的核心思路,是将SecureCRT作为一个可被外部程序控制的“终端模拟器”,通过其强大的VBScript/Python脚本接口,结合外部的Python逻辑,模拟出一个“会思考”的用户,自动完成从连接建立到输入验证码的全过程。这不仅仅是“自动输入”,更是“智能等待与响应”。

适合阅读本文的读者包括:日常使用Jumpserver和SecureCRT的运维工程师、开发人员、SRE,以及对自动化脚本和RPA(机器人流程自动化)感兴趣的技术爱好者。即使你Python基础一般,只要跟着步骤走,也能成功复现。整个方案不涉及任何对Jumpserver或MFA机制的破解或绕过,完全在合规的框架内,模拟人工操作,安全可靠。

2. 整体方案设计与技术选型解析

2.1 为什么是Python + SecureCRT?

首先需要明确,我们的目标是自动化一个图形化桌面应用(SecureCRT)与一个Web服务(Jumpserver登录页面)的交互过程。市面上有几种常见思路:

  1. 纯浏览器自动化(如Selenium):直接控制浏览器打开Jumpserver登录页并操作。问题在于,很多企业环境通过堡垒机访问服务器时,走的不是标准的Web登录,而是先通过客户端(如SecureCRT)建立隧道或直连,登录流程可能内嵌在客户端中。此外,浏览器的自动化可能被企业安全策略拦截。
  2. 纯客户端脚本(SecureCRT VBScript):SecureCRT原生支持VBScript和JScript。但对于需要复杂逻辑判断(如解析屏幕输出、等待特定字符出现、处理动态验证码)的场景,VBScript能力较弱,尤其是与外部系统(如获取TOTP码)交互不便。
  3. Python + SecureCRT API:这是本方案选择的路径。SecureCRT提供了完善的脚本对象模型(对象模型),可以通过Python(或VBScript)进行几乎所有的控制。Python作为胶水语言,既能方便地调用各种库(如pyotp生成TOTP码),又能编写清晰的逻辑控制流,还能通过subprocess等模块与系统其他部分交互,灵活性最高。

方案优势

  • 非侵入性:不修改Jumpserver、不修改SecureCRT,仅在其提供的脚本接口上操作。
  • 模拟人工:所有操作序列(连接、等待、输入)都与人工操作一致,安全策略兼容性好。
  • 集中管理:可以将服务器连接信息、MFA种子密钥统一管理在一个加密的配置文件中,避免散落各处。
  • 可扩展性强:Python脚本很容易扩展,例如加入失败重试、多账号切换、登录状态通知(如发送邮件或Teams消息)等功能。

2.2 核心组件与工作流程

整个自动化登录系统由以下几个核心部分组成,它们协同工作的流程如下图所示(概念描述):

  1. 连接配置文件:SecureCRT中保存了目标服务器的连接信息(协议、主机名、端口、用户名等)。脚本将启动这个已配置好的会话。
  2. Python主控脚本:这是大脑。它负责:
    • 启动或附着到SecureCRT进程。
    • 连接到指定的会话。
    • 监控终端屏幕输出,使用正则表达式匹配登录提示符(如username:password:MFA code:)。
    • 在适当时机,从配置源获取凭证并发送按键事件。
    • 调用TOTP生成器获取当前动态码。
  3. 凭证与配置管理器:一个安全的存储(如经过加密的JSON或YAML文件),用于保存:
    • Jumpserver登录用户名、密码(或密码的加密形态)。
    • 对应账号的MFA TOTP种子密钥(Base32格式)。
    • 目标SecureCRT会话的名称或路径。
  4. TOTP生成器:使用Python的pyotp库,根据种子密钥和当前时间,生成符合RFC 6238标准的6位动态验证码。

工作流程时序

  1. 用户执行Python脚本,并传入目标会话标识。
  2. 脚本启动SecureCRT并打开指定会话,建立连接。
  3. 脚本进入“监控-响应”循环: a.等待用户名提示-> 从配置读取并输入用户名,发送回车。 b.等待密码提示-> 从配置读取并输入密码,发送回车。 c.等待MFA验证码提示-> 调用pyotp生成当前码,输入,发送回车。
  4. 脚本检测到登录成功的标志(如出现特定命令提示符$#),退出循环,报告成功。脚本可以继续运行或退出,此时SecureCRT会话已保持登录状态供用户使用。

注意:密码和MFA种子密钥属于最高敏感信息。绝对不要以明文形式硬编码在脚本中。后续我们会详细讨论如何相对安全地管理这些机密。

3. 环境准备与核心工具详解

3.1 SecureCRT脚本环境搭建

SecureCRt支持两种脚本运行方式:内置脚本和外部脚本。我们选择外部Python脚本,控制更灵活。

  1. 启用脚本支持:确保你的SecureCRT版本支持脚本(一般专业版都支持)。在SecureCRT菜单栏点击Script->Run,如果能弹出对话框,说明支持。

  2. Python环境配置:SecureCRT默认可能绑定了一个自带的Python环境。为了使用我们熟悉的库(如pyotp),最好配置它使用我们自己的Python解释器。

    • 打开SecureCRT,点击Options->Global Options
    • 在左侧找到General->Default Session->Edit Default Settings
    • 在弹出窗口中,找到Connection->Logon Scripts
    • 在这里可以看到配置脚本的路径。但更关键的是,SecureCRT会查找系统环境变量和注册表来确定Python路径。最稳妥的方法是,确保你用来执行脚本的系统Python(比如通过python命令启动的那个)已经安装了必要的库。
    • 你也可以在脚本的开头通过指定Python解释器的绝对路径来消除歧义,但通常不是必须的。
  3. 理解SecureCRT对象模型:这是脚本能控制SecureCRT的关键。几个最核心的对象:

    • crt:根对象,所有操作的起点。
    • crt.Screen:代表当前会话的屏幕,可以读取内容、发送字符串、等待字符串。
    • crt.Dialog:用于创建简单的消息框、输入框,进行人机交互。
    • crt.Session:代表当前会话对象,可以获取会话配置信息。

    一个最简单的测试脚本test_connect.py,可以验证环境:

    # 文件名:test_connect.py import sys def main(): # 获取当前活动的标签页(会话) tab = crt.GetScriptTab() # 获取屏幕对象 screen = tab.Screen # 向当前会话发送一条命令并回车 screen.Send(“echo ‘Hello from SecureCRT Script!’\r”) # 等待回显出现 screen.WaitForString(“Hello from SecureCRT Script!”) # 弹窗提示 crt.Dialog.MessageBox(“脚本执行成功!”, “提示”) if __name__ == “__main__”: main()

    在SecureCRT中打开一个已连接的会话,然后Script->Run,选择这个文件,如果能看到命令执行和弹窗,说明基础环境OK。

3.2 Python依赖库安装

我们需要两个核心Python库:

  1. pyotp:用于生成基于时间的一次性密码(TOTP)。这是Google Authenticator、Microsoft Authenticator等应用使用的标准算法。
  2. keyring(可选但强烈推荐):用于安全地存储密码和种子密钥,利用操作系统提供的凭据管理工具(如Windows的Credential Manager、macOS的Keychain、Linux的Secret Service)。

打开你的命令行(确保是SecureCRT将会使用的那个Python环境),执行安装命令:

pip install pyotp keyring

pyotp库原理简述:TOTP是基于HMAC的一次性密码算法。它需要一个共享的密钥(种子密钥)和当前时间戳(通常以30秒为一个时间窗口)。服务器和你的脚本使用相同的密钥和相同的时间算法,就能独立生成相同的6位数字。种子密钥通常是一个Base32编码的字符串(由字母A-Z和数字2-7组成),在Jumpserver中启用MFA时会提供给你,务必妥善保存。

3.3 安全存储方案设计:如何管理你的密钥

这是本项目最重要的部分之一。明文存储等于没有安全

方案一:使用keyring库(推荐)keyring库将秘密存储在操作系统的安全存储中,脚本运行时再取出。这样秘密不会出现在脚本文件、配置文件或环境变量中。

import keyring import getpass # 设置密码(第一次运行时执行) service_name = “jumpserver_auto_login” username = “your_username” password = getpass.getpass(“请输入密码: “) keyring.set_password(service_name, username, password) # 获取密码(在脚本中使用) retrieved_password = keyring.get_password(service_name, username)

对于MFA种子密钥,也可以用同样的方式存储,使用不同的username标识,例如username_mfa_seed

方案二:加密的配置文件如果觉得keyring依赖特定系统,可以使用对称加密(如AES)来加密一个配置文件。脚本运行时,需要提供一个解密口令(可通过getpass输入,不保存)。

# 简化示例,使用cryptography库 from cryptography.fernet import Fernet import json import os # 生成并保存密钥(仅一次) key = Fernet.generate_key() cipher_suite = Fernet(key) # 将key保存在一个安全的地方,并告知脚本使用者 config = { “username”: “admin”, “password_encrypted”: cipher_suite.encrypt(b“my_password”).decode(), “mfa_seed_encrypted”: cipher_suite.encrypt(b“JBSWY3DPEHPK3PXP”).decode() } with open(“config.enc”, “w”) as f: json.dump(config, f) # 在脚本中读取 with open(“config.enc”, “r”) as f: config = json.load(f) user_input_key = getpass.getpass(“请输入解密密钥: “).encode() cipher_suite = Fernet(user_input_key) password = cipher_suite.decrypt(config[“password_encrypted”].encode()).decode() mfa_seed = cipher_suite.decrypt(config[“mfa_seed_encrypted”].encode()).decode()

方案三:环境变量(适用于CI/CD等无交互环境)将密码和种子密钥设置为环境变量,脚本从中读取。这要求你的系统环境本身是安全的。

# 在终端中设置(临时) export JUMPSERVER_PASSWORD=‘your_password’ export JUMPSERVER_MFA_SEED=‘JBSWY3DPEHPK3PXP’
import os password = os.environ.get(‘JUMPSERVER_PASSWORD’) mfa_seed = os.environ.get(‘JUMPSERVER_MFA_SEED’)

实操心得:对于个人日常使用,keyring方案是最佳平衡点,既安全又方便。在团队共享场景下,可以考虑加密配置文件,将解密密钥通过安全渠道分发给成员。永远避免将秘密提交到版本控制系统(如Git)中,记得将config.enc*.key等文件加入.gitignore

4. 脚本核心代码实现与逐行解析

下面我们将构建完整的自动化登录脚本。我们将它命名为jumpserver_auto_login.py。这个脚本被设计为既可以由SecureCRT直接调用(通过Script -> Run),也可以作为一个独立的Python脚本运行(它会自动启动或连接到SecureCRT)。

4.1 脚本框架与参数解析

首先,构建脚本的基本框架,处理输入参数。我们允许通过命令行参数指定要连接的SecureCRT会话名称。

#!/usr/bin/env python3 # -*- coding: utf-8 -*- “”” Jumpserver MFA 自动登录脚本 for SecureCRT 用法: 1. 在SecureCRT内运行:Script -> Run,选择此文件。 2. 命令行运行:python jumpserver_auto_login.py [会话名称] “”” import sys import os import re import time import pyotp import keyring import argparse from typing import Optional, Tuple # SecureCRT脚本环境检查 try: import pythoncom import win32com.client # 如果导入成功,说明可能是在外部运行,需要连接SecureCRT的COM接口 IN_SECURECRT = False except ImportError: # 如果导入失败,可能是在SecureCRT内置Python环境中运行 # 此时`crt`对象是全局可用的 IN_SECURECRT = ‘crt’ in globals() def get_credentials_from_keyring(username: str) -> Tuple[Optional[str], Optional[str]]: “”” 从系统密钥库获取密码和MFA种子。 服务名固定为‘jumpserver_auto’,用户名作为标识。 “”” SERVICE_NAME = “jumpserver_auto” password = keyring.get_password(SERVICE_NAME, username) mfa_seed = keyring.get_password(SERVICE_NAME, f“{username}_mfa_seed”) if not password: print(f“错误:未找到用户 ‘{username}’ 的密码,请先使用 keyring.set_password() 设置。”) if not mfa_seed: print(f“错误:未找到用户 ‘{username}’ 的MFA种子密钥,请先设置。”) return password, mfa_seed def generate_current_totp(mfa_seed: str) -> str: “””根据种子密钥生成当前的6位TOTP码。“”” totp = pyotp.TOTP(mfa_seed) return totp.now() def main_external(session_name: str, username: str): “””在SecureCRT外部运行的主函数。“”” # 这部分代码需要连接SecureCRT的COM接口,相对复杂 # 作为示例,我们这里打印信息并退出。实际实现需要用到win32com。 print(f“外部模式:准备连接会话 ‘{session_name}’,用户 ‘{username}’。”) print(“注意:外部COM控制模式代码较长,此处省略。建议在SecureCRT内部运行脚本。”) sys.exit(1) def main_internal(session_name: str, username: str): “””在SecureCRT内部运行的主函数。“”” # 获取凭证 password, mfa_seed = get_credentials_from_keyring(username) if not all([password, mfa_seed]): crt.Dialog.MessageBox(“获取凭证失败,请检查密钥库设置。”, “错误”) return # 获取当前脚本标签页,通常就是我们运行脚本时所在的会话。 # 但为了更通用,我们可以尝试连接到指定名称的会话。 # SecureCRT API没有直接通过名称获取会话的简单方法。 # 更常见的做法是:在需要自动登录的会话中,直接运行此脚本。 # 因此,我们假设脚本就在目标会话中运行。 tab = crt.GetScriptTab() screen = tab.Screen # 定义等待和发送函数 def wait_and_send(wait_pattern, send_text, timeout=10): “””等待屏幕上出现特定模式,然后发送文本。“”” try: # WaitForString 会阻塞直到字符串出现或超时 screen.WaitForString(wait_pattern, timeout) # 短暂的额外等待,确保光标就位 time.sleep(0.5) screen.Send(send_text + “\r”) time.sleep(0.5) # 等待服务器响应 return True except Exception as e: crt.Dialog.MessageBox(f“等待 ‘{wait_pattern}’ 超时或失败: {e}”, “错误”) return False # 开始自动化登录流程 print(“[INFO] 开始Jumpserver自动登录流程...”) # 1. 等待用户名提示符。常见的提示符有 “username:”, “login:”, “用户:” # 使用正则表达式匹配更灵活 screen.Send(“\r”) # 先发送一个回车,激活可能的待输入状态 time.sleep(1) # 我们采用更主动的方式:直接尝试发送用户名,如果当前提示就是用户名,则成功。 # 如果不是,WaitForString会等待。这里用一个组合策略。 # 首先,尝试匹配常见的提示符 username_prompts = [“username:”, “login:”, “用户:”] found = False for prompt in username_prompts: # Screen.ReadString 可以读取当前屏幕内容 current_screen = screen.ReadString(screen.CurrentRow, screen.CurrentColumn, screen.CurrentRow+20, screen.CurrentColumn+80, 0) if re.search(prompt, current_screen, re.IGNORECASE): found = True # 光标可能已经在提示符后,直接发送用户名 screen.Send(username + “\r”) time.sleep(1) break if not found: # 如果没有找到明确提示,使用WaitForString等待一个可能出现的提示 # 这里假设第一个提示是用户名 if not wait_and_send(“username:”, username): return # 2. 等待密码提示符 if not wait_and_send(“password:”, password): return # 3. 等待MFA验证码提示符 # 提示符可能是 “MFA code:”, “Verification code:”, “动态码:”, “二次验证码:” # 在输入密码后,可能需要稍长的等待服务器处理 time.sleep(2) # 生成当前的TOTP码 current_totp = generate_current_totp(mfa_seed) print(f“[INFO] 生成的TOTP码: {current_totp}“) # 尝试匹配MFA提示 mfa_prompts = [“MFA”, “verification”, “动态码”, “二次验证”, “code:”] mfa_found = False for prompt in mfa_prompts: current_screen = screen.ReadString(screen.CurrentRow, screen.CurrentColumn, screen.CurrentRow+10, screen.CurrentColumn+80, 0) if re.search(prompt, current_screen, re.IGNORECASE): mfa_found = True screen.Send(current_totp + “\r”) break if not mfa_found: # 如果没找到,尝试通用等待 if not wait_and_send(“code:”, current_totp, timeout=5): crt.Dialog.MessageBox(“未检测到MFA输入提示,可能登录流程有变或已失败。”, “警告”) return # 4. 等待登录成功标志(例如出现命令行提示符 $, #, > 或特定的欢迎信息) print(“[INFO] 等待登录成功...”) try: # 等待一个常见的提示符,比如 $, #, >,或者包含用户名@主机名的字符串 screen.WaitForStrings([“$”, “#”, “>”, username + “@”], 15) print(“[SUCCESS] 登录成功!”) crt.Dialog.MessageBox(“自动登录成功!”, “提示”, options=0x40) # 0x40 是信息图标 except Exception as e: print(f“[WARNING] 可能未在预期时间内看到成功提示: {e}“) # 即使没等到明确提示,也可能已经登录,这里不视为失败 if __name__ == “__main__”: # 参数解析 parser = argparse.ArgumentParser(description=‘Jumpserver MFA自动登录’) parser.add_argument(‘session_name’, nargs=‘?’, help=‘SecureCRT会话名称(可选)’) parser.add_argument(‘-u’, ‘—username’, required=True, help=‘Jumpserver登录用户名’) args = parser.parse_args() if IN_SECURECRT: # 在SecureCRT内部运行 main_internal(args.session_name, args.username) else: # 在SecureCRT外部运行 main_external(args.session_name, args.username)

4.2 关键函数与逻辑深度解析

  1. get_credentials_from_keyring函数

    • 为什么用keyring如前所述,安全。它利用操作系统级别的安全存储,比文件加密对普通用户更友好。
    • 服务名与用户名:我们将服务名固定为”jumpserver_auto”。用户名作为检索凭据的键。对于MFA种子,我们使用f”{username}_mfa_seed”作为键,与密码区分开。这意味着你需要为每个Jumpserver用户运行两次keyring.set_password来存储密码和种子。
  2. generate_current_totp函数

    • pyotp.TOTP(mfa_seed):使用种子密钥创建一个TOTP对象。种子密钥必须是Base32编码的字符串。
    • totp.now():根据当前时间(精确到秒)计算并返回6位数字字符串。pyotp库会自动处理30秒时间窗口的逻辑。
  3. wait_and_send函数(内部辅助函数)

    • 这是自动化交互的核心。screen.WaitForString(pattern, timeout)会阻塞脚本执行,直到屏幕上出现指定的pattern字符串,或者超过timeout秒。
    • 超时设置:根据网络和服务器响应速度合理设置。登录提示通常很快(2-5秒),但MFA验证后的系统加载可能较慢(10-15秒)。
    • screen.Send():向当前会话发送字符串。务必在字符串末尾加上”\r”,这代表回车键(Enter),相当于用户按下回车。只发送文本而不发送回车,命令不会执行。
    • time.sleep():短暂的等待非常必要。它模拟了用户的反应时间,并给服务器和终端处理输入留出时间。去掉这些等待,可能导致输入节奏过快,造成乱序或丢失。
  4. 登录流程控制逻辑

    • 主动探测与被动等待结合:脚本首先尝试读取当前屏幕(screen.ReadString)来检查是否已经存在某个提示符。如果找到了,就直接输入。这提高了脚本的健壮性,避免在提示符早已出现时傻等。
    • 使用正则表达式忽略大小写re.IGNORECASE标志让匹配更灵活,适应不同服务器可能使用”Username:””username:”的情况。
    • 多提示符匹配:定义了username_promptsmfa_prompts列表。服务器提示符的文本可能变化,这个列表可以方便地扩展。实际使用中,你需要根据你的Jumpserver页面或SSH登录提示的具体文本来调整这些字符串。
    • 成功判定:使用screen.WaitForStrings等待多个可能的成功标志($,#,>, 用户名@主机名)。只要出现其中一个,就认为登录成功。这是一个保守但实用的策略。

4.3 如何适配你的Jumpserver登录流程

这是脚本能否成功运行的关键。你需要仔细观察你手动登录时的完整过程:

  1. 打开SecureCRT会话,连接后,屏幕最先出现什么?

    • 是直接显示username:吗?
    • 还是先显示一些横幅(banner)信息,然后才出现提示?
    • 如果是后者,需要在脚本最开始增加screen.WaitForString(“横幅中的某个特征词”),确保跳过横幅。
  2. 每一步的提示符到底是什么?

    • 打开一个终端,手动登录一次,精确记录每一步系统输出的提示文字。
    • 例如,你的Jumpserver SSH网关提示可能是:
      Welcome to Jumpserver SSH Gateway Please enter your username:
    • 那么你的username_prompts就应该包含”username:”
    • 密码提示可能是”Password:”,MFA提示可能是”MFA authentication code:”
  3. 登录成功后的最终界面是什么?

    • 是普通的$#提示符吗?
    • 还是跳转到了某个内部菜单?如果是菜单,你可能需要额外发送一个命令(比如”1\r”)来选择默认主机。

实操心得:编写这类自动化脚本,“录制-回放”是一个很好的起点。你可以先手动操作一遍,用纸笔或录屏记下所有的屏幕输出和你的键盘输入。然后根据这个“剧本”来编写WaitForStringSend的顺序。第一次运行时,建议在关键步骤后加入crt.Dialog.MessageBox进行调试,确认脚本执行到了哪一步,看到了什么屏幕内容。

5. 脚本的部署、使用与优化

5.1 一键运行:配置SecureCRT按钮或快捷键

每次都要点开菜单运行脚本太麻烦。SecureCRT允许你将脚本绑定到按钮栏或快捷键。

  1. 创建按钮

    • 在SecureCRT窗口的按钮栏空白处右键,选择Customize->Toolbars
    • Commands选项卡下,左侧选择Scripts,你会看到你的脚本文件(如果放在默认脚本目录,或者你运行过)。如果没有,点击Add添加。
    • 将你的脚本命令拖拽到上方的按钮栏中。
    • 右键新按钮,选择Customize,可以修改图标和显示文本。
  2. 设置快捷键

    • 点击Options->Global Options->General->Default Session->Edit Default Settings
    • 找到Terminal->Mapped Keys
    • 点击Map a Key,按下你想设置的快捷键(例如Ctrl+Alt+L)。
    • Send字段选择Script,然后点击Properties,选择你的脚本文件。
    • 这样,在任何会话中按下这个快捷键,就会执行自动登录脚本。

5.2 处理复杂情况与增强健壮性

基础的脚本可能无法覆盖所有情况,我们需要让它更聪明。

  1. 网络波动与连接重试

    def connect_with_retry(tab, max_retries=3): for i in range(max_retries): try: # 尝试连接(如果会话配置为自动连接,这步可能不需要) # 或者,检测当前连接状态 if tab.Session.Connected: return True else: tab.Session.Connect() time.sleep(5) # 等待连接建立 if tab.Session.Connected: return True except Exception as e: print(f“连接尝试 {i+1} 失败: {e}“) time.sleep(2) return False
  2. 验证码过期处理: TOTP码30秒一变。如果脚本在29秒时生成并发送,但网络延迟导致服务器在1秒后收到,此时码可能就失效了。我们可以增加一个简单的重试逻辑。

    def send_totp_with_retry(screen, mfa_seed, prompt_pattern, max_retries=2): for attempt in range(max_retries): current_code = generate_current_totp(mfa_seed) print(f“尝试 {attempt+1}: 发送TOTP码 {current_code}“) screen.Send(current_code + “\r”) time.sleep(2) # 等待服务器响应 # 检查是否出现了错误提示,如 “Invalid code” (无效代码) screen_content = screen.ReadString(screen.CurrentRow-5, 1, screen.CurrentRow+5, 80, 0) if re.search(r“invalid|错误|失败”, screen_content, re.IGNORECASE): print(“检测到验证码错误,等待下一个时间窗口...”) # 计算到下一个30秒窗口还剩多少秒 time_to_next = 30 - (int(time.time()) % 30) wait_time = time_to_next + 2 # 多等2秒确保新码生效 print(f“等待 {wait_time} 秒后重试...”) time.sleep(wait_time) continue # 重试循环 else: # 没有错误信息,假设成功 return True return False

    main_internal函数中,将发送MFA码的部分替换为调用此函数。

  3. 日志记录:将脚本的运行情况(时间、会话、成功/失败)记录到文件,便于排查问题。

    import logging logging.basicConfig( level=logging.INFO, format=‘%(asctime)s - %(levelname)s - %(message)s’, handlers=[ logging.FileHandler(‘jumpserver_auto_login.log’, encoding=‘utf-8’), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) # 在代码中用 logger.info(‘…’) 代替 print(‘…’)

5.3 将脚本打包为可执行文件(.exe)

如果你想让没有Python环境的同事也能使用,或者想隐藏源代码,可以使用PyInstaller打包。

  1. 安装PyInstaller

    pip install pyinstaller
  2. 打包命令

    pyinstaller —onefile —icon=myicon.ico —name “JumpserverAutoLogin” jumpserver_auto_login.py
    • —onefile:打包成单个exe文件。
    • —icon:指定exe的图标(可选)。
    • —name:指定输出exe的名称。
  3. 注意事项

    • 打包后的exe会比较大(因为包含了Python解释器和依赖库)。
    • keyring后端问题:在打包时,keyring使用的系统后端(如Windows的win32ctypes)可能需要额外处理。有时需要手动指定隐藏的导入。
    • 测试:务必在目标机器上测试打包好的exe,确保它能正确访问系统的密钥存储(如Windows凭据管理器)。

6. 常见问题排查与实战技巧

即使脚本写得再完善,在实际环境中也可能遇到各种问题。下面是一个常见问题排查清单。

问题现象可能原因排查步骤与解决方案
脚本运行后无任何反应,SecureCRT会话也没输入。1. SecureCRT脚本执行权限问题。
2. Python路径错误。
3. 脚本语法错误。
1. 在SecureCRT中,Options->Global Options->General->Configuration Paths,检查脚本路径是否正确。
2. 在SecureCRT内直接运行一个简单的print(“hello”)脚本测试环境。
3. 查看SecureCRT的Window->Script Window,这里会有脚本运行的错误输出。
能输入用户名,但卡在密码提示。1. 密码提示符文本不匹配。
2. 屏幕读取区域不对,没检测到提示。
3. 用户名输入后服务器响应慢。
1. 在wait_and_send(‘password:’, password)前加一行crt.Dialog.MessageBox(screen.ReadString(…)),弹窗显示当前屏幕内容,确认提示符到底是什么。
2. 增加time.sleep(2)在发送用户名后,给服务器更多处理时间。
MFA验证码总是错误。1. 系统时间不同步。
2. MFA种子密钥错误。
3. 验证码在传输过程中过期。
1.这是最常见原因!检查运行脚本的电脑系统时间是否准确,确保与网络时间同步(NTP)。
2. 确认从Jumpserver备份的种子密钥是否正确,没有多余空格或换行。用pyotp.TOTP(seed).now()生成一个码,与你手机验证器上的对比是否一致。
3. 实现上文提到的send_totp_with_retry函数。
登录成功后脚本还卡住,或提前结束。1. 成功提示符匹配失败。
2. 登录后进入了非标准shell(如受限shell)。
1. 调整screen.WaitForStrings中的成功标志列表。登录后手动查看屏幕上的提示符是什么。
2. 如果登录后是菜单,需要在脚本最后增加选择菜单项的Send命令。
在外部调用脚本(如计划任务)不工作。1. SecureCRT COM接口权限问题。
2. 会话未正确启动或找到。
1. 外部控制非常复杂,涉及COM初始化、进程查找等。对于大多数用户,强烈建议只在SecureCRT内部运行脚本。如果必须外部调用,考虑使用AutoHotkey或PyAutoGUI等UI自动化工具模拟按键,但稳定性较差。
keyring找不到密码。1. 服务名或用户名不对。
2. 密钥库中确实没有存储。
3. 跨平台问题(如在Linux上用了Windows的存储后端)。
1. 用Python交互环境执行import keyring; print(keyring.get_password(‘jumpserver_auto’, ‘your_username’))测试。
2. 确认存储时使用的service_nameusername与读取时完全一致。
3. 查看keyring的文档,了解当前使用的后端。

独家避坑技巧

  • “慢一点,更快”:在自动化脚本中,time.sleep是你的好朋友。尤其是在网络环境复杂或服务器负载高时,在关键操作(如发送回车后)等待0.5到2秒,能极大提高脚本的稳定性。贪快往往导致失败。
  • 使用Screen.WaitForStrings代替多个WaitForStringWaitForStrings可以同时等待多个字符串中的任意一个出现,比顺序写多个WaitForString更高效,也能更好地处理不确定的提示顺序。
  • 正则表达式是利器:对于变化较多的提示文本,使用re.search(r”password\s*:”, screen_content, re.IGNORECASE)比精确匹配”password:”更可靠。它可以匹配”Password: “”password :”等多种形式。
  • 准备一个“调试模式”:在脚本开头设置一个DEBUG = True的变量。当DEBUG为真时,将每一步要发送的密码和验证码用*号代替显示在日志中,并增加更多的状态弹窗。正常使用时关闭调试。这能帮你快速定位问题阶段。
  • 处理好异常和超时:每一个WaitForStringSend操作都应该有try…except包裹,并给出有意义的错误信息。不要让脚本无声无息地失败。

这个方案将你从重复的MFA输入中解放出来,把时间留给更有价值的工作。它背后的思路——通过脚本控制GUI应用,实现流程自动化——可以举一反三,应用到其他需要与终端、桌面软件交互的自动化场景中。核心永远是:仔细观察手动流程,将其精确地翻译成“等待-响应”的代码逻辑,并预留足够的容错空间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/4 18:05:05

XSS攻防实战:从基础过滤到纵深防御体系的构建

1. 项目概述&#xff1a;从“能防”到“防得住”的XSS攻防实战聊到Web安全&#xff0c;XSS&#xff08;跨站脚本攻击&#xff09;绝对是个绕不开的“老朋友”。很多开发者&#xff0c;尤其是刚入行的朋友&#xff0c;可能觉得XSS防御很简单&#xff0c;不就是把用户输入里的<…

作者头像 李华
网站建设 2026/7/4 18:04:32

Si4732与PIC18F87J10构建高保真收音系统设计

1. 为什么选择Si4732与PIC18F87J10构建高保真收音系统在数字音频处理领域&#xff0c;收音机芯片与微控制器的组合方案直接影响最终音质表现。Si4732作为Silicon Labs推出的高性能数字调谐收音芯片&#xff0c;搭配Microchip的PIC18F87J10单片机&#xff0c;这套组合在车载音响…

作者头像 李华
网站建设 2026/7/4 18:03:28

AI编程助手Antigravity:从智能编码到自动化测试的一体化开发体验

1. 项目概述&#xff1a;当AI成为你的编程伙伴与测试专家最近在开发者圈子里&#xff0c;Google Antigravity 这个名字的热度持续攀升。作为一个长期混迹在一线的开发者&#xff0c;我最初也以为这不过是又一个“AI代码补全”工具&#xff0c;但深度使用几周后&#xff0c;我发…

作者头像 李华
网站建设 2026/7/4 18:00:12

胡言乱语计算机一

操作系统是连接硬件和应用软件之间的纽带。至少目前是这样的。而操作系统这门课也是计算机专业的必修课之一。无奈当时混沌。并没有真正的上好这一门课&#xff0c;之所以叫胡言乱语。是因为这里面的水对我来说实在是太深了。任何一个小的问题背后都是一个深渊。所以第一篇&…

作者头像 李华
网站建设 2026/7/4 17:58:36

强化学习入门:从猫抓挠到Q-learning实战

1. 这不是科幻&#xff0c;是猫和Scratching Post教我的第一课&#xff1a;RL到底在学什么&#xff1f;你有没有试过教一只猫别抓沙发&#xff1f;我养过三只猫&#xff0c;每只都用爪子认真“评估”过我家所有家具的承重极限和织物韧性。最绝的是第二只——它能精准避开我新买…

作者头像 李华
网站建设 2026/7/4 17:58:22

多维聚合实战:超越GROUP BY的结构化空间建模方法论

1. 项目概述&#xff1a;多维聚合中的数据操作&#xff0c;远不止GROUP BY那么简单“Part 20: Data Manipulation in Multi-Dimensional Aggregation”这个标题乍看像是一门数据库课程的普通章节编号&#xff0c;但如果你在真实业务场景中处理过销售漏斗分析、用户行为路径归因…

作者头像 李华