当前位置: 首页 > 知识库问答 >
问题:

使用Paramiko在Python中使用2fa的SSH

章海
2023-03-14

远程服务器使用Duo2因素身份验证,并在使用SSH连接时提示您选择身份验证模式:

$ ssh myuser@remoteserver.com
Duo two-factor login for myuser

Enter a passcode or select one of the following options:

 1. Duo Push to +XXX XX-XXX-1111
 2. Phone call to +XXX XX-XXX-1111
 3. SMS passcodes to +XXX XX-XXX-1111

Passcode or option (1-3): 1
Success. Logging you in...

当我在终端中使用ssh时,我只需按1然后输入,得到我的手机的推送,在那里我证明了连接,然后我就登录了。

不幸的是,我无法在Python中做到这一点。下面是我尝试使用的代码:

import paramiko

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

ssh.connect('remoteserver.com', port=22, username='myuser', password='mypassword')
stdin, stdout, stderr = ssh.exec_command('ls -l')
output = stdout.readlines()
print(output)
paramiko.ssh_exception.AuthenticationException: Authentication failed.

如有任何帮助,将不胜感激。

共有1个答案

东方华晖
2023-03-14

在我的一个项目中,我自己终于解决了这个问题,将留下我所使用的代码,它允许进行这项工作。

主要的特点是,paramiko允许在传输和提示列表中这样做,在我的示例中,我的two_factor_types中缺少publickey方法

def _auth(self, username, password, pkey, *args):
    self.password = password
    saved_exception = None
    two_factor = False
    allowed_types = set()
    two_factor_types = {'keyboard-interactive', 'password', 'publickey'}

参考文献:
paramiko/python:键盘交互身份验证
https://github.com/paramiko/paramiko/pull/467
https://github.com/paramiko/paramiko/pull/467/commits/dae916f7bd6723cee95891778baff51ef45532ee
http://docs.paramiko.org/en/stable/api/transport.html

我建议尝试使用auth_interactive_dumb这样的方法

auth_interactive_dumb(username, handler=None, submethods='')

以交互方式向服务器进行身份验证,但比较笨拙。只需将提示和/或说明打印到stdout并发回响应。这对于通过密钥实现部分身份验证,然后用户必须输入2fac令牌的情况很好。

有关更多完整示例,请参阅下面的摘录和链接

class SSHClient(paramiko.SSHClient):
    duo_auth = False

    def handler(self, title, instructions, prompt_list):
        answers = []
        global duo_auth

        if title.startswith('Duo two-factor login'):
            duo_auth = True
            raise SSHException("Expecting one field only.")

        for prompt_, _ in prompt_list:
            prompt = prompt_.strip().lower()
            if prompt.startswith('password'):
                answers.append(self.password)
            elif prompt.startswith('verification'):
                answers.append(self.totp)
            elif prompt.startswith('passcode'):
                answers.append(self.totp)
            else:
                raise ValueError('Unknown prompt: {}'.format(prompt_))
        return answers

    def auth_interactive(self, username, handler):
        if not self.totp:
            raise ValueError('Need a verification code for 2fa.')
        self._transport.auth_interactive(username, handler)

    def _auth(self, username, password, pkey, *args):
        self.password = password
        saved_exception = None
        two_factor = False
        allowed_types = set()
        two_factor_types = {'keyboard-interactive', 'password', 'publickey'}

        agent = paramiko.Agent()
        try:
            agent_keys = agent.get_keys()
            # if len(agent_keys) == 0:
            # return
        except:
            pass

        for key in agent_keys:
            logging.info("Trying ssh-agent key %s" % hexlify(key.get_fingerprint()))
            try:
                self._transport.auth_publickey(username, key)
                logging.info("... success!")
                return
            except paramiko.SSHException as e:
                logging.info("... nope.")
                saved_exception = e

        if pkey is not None:
            logging.info('Trying publickey authentication')
            try:
                allowed_types = set(
                    self._transport.auth_publickey(username, pkey)
                )
                two_factor = allowed_types & two_factor_types
                if not two_factor:
                    return
            except paramiko.SSHException as e:
                saved_exception = e

        if duo_auth or two_factor:
            logging.info('Trying 2fa interactive auth')
            return self.auth_interactive(username, self.handler)

        if password is not None:
            logging.info('Trying password authentication')
            try:
                self._transport.auth_password(username, password)
                return
            except paramiko.SSHException as e:
                saved_exception = e
                allowed_types = set(getattr(e, 'allowed_types', []))
                two_factor = allowed_types & two_factor_types

        assert saved_exception is not None
        raise saved_exception
 类似资料:
  • 问题内容: 我尝试过的 然后 再发送密码导致没有root 然后导致“频道关闭”错误 然后导致不被root 然后写到标准输入并刷新它导致没有root 问题答案: 看看这个例子: 在此处找到具有更多说明的示例:http : //jessenoller.com/2009/02/05/ssh- programming-with-paramiko-completely- different/ 希望能帮助到你

  • 问题内容: 我想编写一个程序(在Windows 7的Python 3.x中),通过ssh在远程shell上执行多个命令。在查看了paramikos的功能之后,我意识到它不适合我的用例(因为在执行命令后通道被关闭了),因为这些命令取决于环境变量(由先前的命令设置),并且不能被合并为一个调用它们,因为它们将在程序中的不同时间执行。 因此,我想在同一通道中执行命令。我研究的下一个选项是使用paramik

  • 本文向大家介绍Python paramiko使用方法代码汇总,包括了Python paramiko使用方法代码汇总的使用技巧和注意事项,需要的朋友参考一下 1、用户名、密码登陆方式 2、免密登陆方式 注意:生成密码的方法 A、进入本地 ssh文件夹 cd .ssh/ B、使用ssh-keygen生产本地公钥和私钥 ssh-keygen xueerhuan@ubuntu:~/.ssh$ ls id_

  • 问题内容: 我正在学习python。我需要使用隧道创建者来从数据库中读取信息并关闭隧道。我使用paramiko,但是我没有使用tonelem示例。请举一个创建隧道的简单代码示例。 提前致谢! 问题答案: 在工作中,我们通常创建ssh隧道转发端口。我们的方法是使用标准命令,使子进程在单独的线程中运行。我找到了这个有用的链接:https : //github.com/paramiko/paramiko

  • 本文向大家介绍Python如何使用paramiko模块连接linux,包括了Python如何使用paramiko模块连接linux的使用技巧和注意事项,需要的朋友参考一下 python程序需要连接linux时,需要使用密码或者秘钥验证以登录os进行命令操作或者文件传输,python中实现此功能的模块为paramiko;下面是该模块的基础用法 下面是通过密码进行linux登录执行命令和文件传输示例

  • 问题内容: 我想使用python自动执行特定任务。 除其他事项外,此任务包括使用ssh连接到远程服务器,以及运行 可能会或可能不会要求用户输入* 的特定程序(称为它)。 经过一些研究并权衡了我的选择之后,我决定使用Python的Paramiko(考虑到以下因素,结果可能是错误的)。 * 让我们从不询问任何输入的简单可能性开始,而只是将一些信息打印到控制台: 编译为:,位于 server_name上