【问题标题】:DSA key forwarding using Paramiko?使用 Paramiko 进行 DSA 密钥转发?
【发布时间】:2014-05-25 06:53:57
【问题描述】:

我正在使用 Paramiko 在远程服务器上执行 bash 脚本。在其中一些脚本中,存在与其他服务器的 ssh 连接。如果我只使用 bash,不使用 Python,我的 DSA 密钥会被第一个远程服务器上的 bash 脚本转发并用于连接到第二个远程服务器。当我使用 Paramiko 时,情况并非如此。

Bash 示例:

Jean@mydesktop:~ & ssh root@firstserver
root@firstserver:~ # ssh root@secondserver hostname
secondserver.mydomain.org

使用 Paramiko:

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

import paramiko

class SSHSession:


    def __init__(self, server_address, user='root', port=22):
        self.connected = False
        self.server_address = server_address
        self.user           = user
        self.port           = port


    def connect(self, clear_channel=True):
        try:
            if self.server_address == None:
                raise ValueError('No hostname')
        except:
            raise ValueError('No hostname')
        else:
            try:
                self.ssh_client = paramiko.SSHClient()
                self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
                self.ssh_client.connect(self.server_address, username=self.user)
                #self.transport = self.ssh_client.get_transport()
                #self.channel   = self.transport.open_forward_agent_channel()
                self.channel   = self.ssh_client.invoke_shell()
            except:
                self.connected = False
                return False
            else:
                self.connected = True
                return True

    def exec_command(self, command, newline='\r'):
        if not self.connected:
            raise Exception('Not connected')
        else:
            timeout = 31536000 # 365 days in seconds
            self.channel.settimeout(timeout)
            line_buffer    = ''
            channel_buffer = ''
            end_string     = 'CLIENT_EXPECT_CMD_OK'
            print('[SEND   ] >>', command)
            self.channel.send(command + ' ; echo ' + end_string + newline)
            while True:
                channel_buffer = self.channel.recv(1).decode('UTF-8')
                if len(channel_buffer) == 0:
                    raise Exception('connection lost with server: ' + self.server_address)
                    break 
                channel_buffer  = channel_buffer.replace('\r', '')
                if channel_buffer != '\n':
                    line_buffer += channel_buffer
                else:
                    if line_buffer == end_string:
                        break
                    print('[RECEIVE] <<', line_buffer)
                    line_buffer   = ''


    def disconnect(self):
        self.ssh_client.close()


    def __enter__(self):
        self.connect()
        return self


    def __exit__(self, _type, value, traceback):
        self.disconnect()


if __name__ == "__main__":
    server_address = 'firstserver'
    ssh_user       = 'root'
    with SSHSession(server_address) as ssh_session:
        ssh_session.exec_command('hostname')
        ssh_session.exec_command('ssh root@secondserver hostname')

输出是:

[SEND   ] >> hostname
[RECEIVE] << [root@firstserver ~]# hostname ; echo CLIENT_EXPECT_CMD_OK
[RECEIVE] << firstserver.mydomain.fr
[SEND   ] >> ssh root@secondserver hostname
[RECEIVE] << [root@firstserver ~]# ssh root@secondserver hostname ; echo CLIENT_EXPECT_CMD_OK
[RECEIVE] << Permission denied (publickey,gssapi-keyex,gssapi-with-mic).

我试过了:

self.transport = self.ssh_client.get_transport()
self.channel   = self.transport.open_forward_agent_channel()

而不是:

self.channel   = self.ssh_client.invoke_shell()

然后我得到一个错误:

paramiko.ssh_exception.ChannelException: Administratively prohibited

有人知道这是否可能吗?我发现建议就是这样,但我不知道如何做到这一点。

【问题讨论】:

    标签: python python-3.x paramiko


    【解决方案1】:

    好的,现在可以了。我发现了这些有用的帖子:

    Add paramiko ssh agent forwarding (optional) #4100
    open_forward_agent_channel vs. open_session #89

    最终代码为:

    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
    
    import paramiko
    
    class SSHSession:
    
    
        def __init__(self, server_address, user='root', port=22):
            self.connected      = False
            self.server_address = server_address
            self.user           = user
            self.port           = port
    
    
        def connect(self):
            try:
                self.ssh_client = paramiko.SSHClient()
                self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
                self.ssh_client.connect(self.server_address, username=self.user)
                self.transport     = self.ssh_client.get_transport()
                self.agent_channel = self.transport.open_session()
                self.agent_handler = paramiko.agent.AgentRequestHandler(self.agent_channel)
                self.channel       = self.ssh_client.invoke_shell()
            except:
                self.connected = False
            else:
                self.connected = True
            return self.connected
    
        def exec_command(self, command, newline='\r'):
            if not self.connected:
                raise Exception('Not connected')
            else:
                timeout = 31536000 # 365 days in seconds
                self.channel.settimeout(timeout)
                line_buffer    = ''
                channel_buffer = ''
                end_string     = 'CLIENT_EXPECT_CMD_OK'
                print('[SEND   ] >>', command)
                self.channel.send(command + ' ; echo ' + end_string + newline)
                while True:
                    channel_buffer = self.channel.recv(1).decode('UTF-8')
                    if len(channel_buffer) == 0:
                        raise Exception('connection lost with server: ' +     self.server_address)
                        break 
                    channel_buffer  = channel_buffer.replace('\r', '')
                    if channel_buffer != '\n':
                        line_buffer += channel_buffer
                    else:
                        if line_buffer == end_string:
                            break
                        print('[RECEIVE] <<', line_buffer)
                        line_buffer   = ''
    
    
        def disconnect(self):
            self.ssh_client.close()
    
    
        def __enter__(self):
            self.connect()
            return self
    
    
        def __exit__(self, _type, value, traceback):
            self.disconnect()
    
    
    if __name__ == "__main__":
        server_address = 'firstserver'
        ssh_user       = 'root'
        with SSHSession(server_address) as ssh_session:
            ssh_session.exec_command('hostname')
            ssh_session.exec_command('ssh root@secondserver hostname')
    

    输出是:

    [SEND   ] >> hostname
    [RECEIVE] << [root@firstserver ~]# hostname ; echo CLIENT_EXPECT_CMD_OK
    [RECEIVE] << firstserver.mydomain.fr
    [SEND   ] >> ssh root@secondserver hostname
    [RECEIVE] << [root@firstserver ~]# ssh root@secondserver hostname ; echo CLIENT_EXPECT_CMD_OK
    [RECEIVE] << secondserver.mydomain.fr
    

    启用代理转发的代码的重要部分是:

    self.agent_channel = self.transport.open_session()
    self.agent_handler = paramiko.agent.AgentRequestHandler(self.agent_channel)
    

    【讨论】:

      【解决方案2】:

      连接时应设置key_filename

      key_filename (str or list(str)) - 文件名,或文件名列表, 用于尝试身份验证的可选私钥

      【讨论】:

      • 你的意思是这样的? self.ssh_client.connect(self.server_address, username=self.user, key_filename='/home/jean/.ssh/id_dsa')我试过了,行为是一样的。实际上,我想使用我的 Linux 桌面的 ssh 代理,而不必指定密钥。我的脚本必须由多个用户使用,每个用户都有自己的密钥。
      猜你喜欢
      • 1970-01-01
      • 2012-09-03
      • 2013-05-07
      • 1970-01-01
      • 2015-11-12
      • 2012-07-02
      • 2013-12-02
      相关资源
      最近更新 更多