admin管理员组

文章数量:1298126

I have to use a jump server to login to a restricted server. Below setup in my jump server:

PasswordAuthentication yes 
ChallengeResponseAuthentication yes 
UsePAM yes

Below is my code

import paramiko, traceback
from getpass import getpass
paramikomon.logging.basicConfig(level=paramikomon.DEBUG)


def connect_to_jump(hostname, port, username, password, ):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        transport = paramiko.Transport((hostname, port))
        transport.start_client() 

        def challenge_handler(title, instructions, prompt_list):
            responses = []
            for prompt in prompt_list:
                if "password" in prompt[0].lower():
                    responses.append(password)
                elif "rsa" in prompt[0].lower() :
                    token = getpass(f"{prompt[0].strip()}: ")
                    responses.append(token)
                else:
                    responses.append(getpass(f"{prompt[0].strip()}: "))
            return responses

        transport.auth_interactive(username, handler=challenge_handler)
        ssh._transport = transport
        return ssh
    

    except Exception as e:
        print(f"Error: {e}")
        traceback.print_exc()
        if 'ssh' in locals():
            ssh.close()


def connect_to_target(jump_ssh, target_host, target_port, username, password):
    dest_addr = (target_host, target_port)
    local_addr = ("127.0.0.1", 0)

    try:
        jump_transport = jump_ssh.get_transport()
       
        channel = jump_transport.open_channel("session", dest_addr, local_addr)       
    
        target_ssh = paramiko.SSHClient()
        target_ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        target_ssh.connect(target_host, port=target_port, username=username, password=password, sock=channel)
    
        stdin, stdout, stderr = target_ssh.exec_command("hostname")
        print(stdout.read().decode())
    finally:
        target_ssh.close()
        jump_ssh.close()

And below is my code to call the above functions:

jump_hostname = 'jump'  
jump_port = 22            
target_host = 'target'
target_port = 22
username = get_user_name()
password = keyring.get_password('unix',username)
jump_ssh = connect_to_jump(jump_hostname, jump_port, username, password)
stdin, stdout, stderr = jump_ssh.exec_command('uname', timeout=10)
o = stdout.read().decode()
if "Linux" in o:
    print(rf"connect to {jump_hostname} successfully")
    connect_to_target(jump_ssh, target_host, target_port, username, password)

I have the line connect to jump successfully which means I have connected to the jump server successfully. However, I couldn't connect to the target. My process hang forever.

Can you please share your thoughts for what could be wrong in my code? I can use putty to ssh to my jump server then to the target.

Below are my logs:

DEBUG:paramiko.transport:starting thread (client mode): 0xbbc4af20
DEBUG:paramiko.transport:Local version/idstring: SSH-2.0-paramiko_3.5.0
DEBUG:paramiko.transport:Remote version/idstring: SSH-2.0-OpenSSH_7.4
INFO:paramiko.transport:Connected (version 2.0, client OpenSSH_7.4)
DEBUG:paramiko.transport:=== Key exchange possibilities ===
DEBUG:paramiko.transport:kex algos: curve25519-sha256, [email protected], ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group-exchange-sha256, diffie-hellman-group16-sha512, diffie-hellman-group18-sha512, diffie-hellman-group-exchange-sha1, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1, diffie-hellman-group1-sha1
DEBUG:paramiko.transport:server key: ssh-rsa, rsa-sha2-512, rsa-sha2-256, ecdsa-sha2-nistp256, ssh-ed25519
DEBUG:paramiko.transport:client encrypt: [email protected], aes128-ctr, aes192-ctr, aes256-ctr,
[email protected], [email protected], aes128-cbc, aes192-cbc, aes256-cbc, blowfish-cbc, cast128-cbc, 3des-cbc
DEBUG:paramiko.transport:server encrypt: [email protected], aes128-ctr, aes192-ctr, aes256-ctr,
[email protected], [email protected], aes128-cbc, aes192-cbc, aes256-cbc, blowfish-cbc, cast128-cbc, 3des-cbc
DEBUG:paramiko.transport:client mac: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], hmac-sha2-256, hmac-sha2-512, hmac-sha1
DEBUG:paramiko.transport:server mac:
[email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], hmac-sha2-256, hmac-sha2-512, hmac-sha1
DEBUG:paramiko.transport:client compress: none, [email protected]
DEBUG:paramiko.transport:server compress: none, [email protected]
DEBUG:paramiko.transport:client lang: <none>
DEBUG:paramiko.transport:server lang: <none>
DEBUG:paramiko.transport:kex follows: False
DEBUG:paramiko.transport:=== Key exchange agreements ===
DEBUG:paramiko.transport:Kex: [email protected]
DEBUG:paramiko.transport:HostKey: ssh-ed25519
DEBUG:paramiko.transport:Cipher: aes128-ctr
DEBUG:paramiko.transport:MAC: hmac-sha2-256
DEBUG:paramiko.transport:Compression: none
DEBUG:paramiko.transport:=== End of kex handshake ===
DEBUG:paramiko.transport:kex engine KexCurve25519 specified hash_algo
<built-in function openssl_sha256>
DEBUG:paramiko.transport:Switch to new keys ...
DEBUG:paramiko.transport:Got EXT_INFO: {'server-sig-algs': b'rsa-sha2-256,rsa-sha2-512'}
DEBUG:paramiko.transport:userauth is OK
INFO:paramiko.transport:Authentication (keyboard-interactive) successful!
DEBUG:paramiko.transport:[chan 0] Max packet in: 32768 bytes
DEBUG:paramiko.transport:Received global request "[email protected]"
DEBUG:paramiko.transport:Rejecting "[email protected]" global request from server.
DEBUG:paramiko.transport:[chan 0] Max packet out: 32768 bytes
DEBUG:paramiko.transport:Secsh channel 0 opened.
DEBUG:paramiko.transport:[chan 0] Sesch channel 0 request ok
DEBUG:paramiko.transport:[chan 0] EOF received (0)
DEBUG:paramiko.transport:[chan 1] Max packet in: 32768 bytes
DEBUG:paramiko.transport:[chan 0] EOF sent (0)
**connect to jump successfully**
DEBUG:paramiko.transport:[chan 1] Max packet out: 32768 bytes
DEBUG:paramiko.transport:Secsh channel 1 opened.
DEBUG:paramiko.transport:starting thread (client mode): 0xaa1dfd00

I have to use a jump server to login to a restricted server. Below setup in my jump server:

PasswordAuthentication yes 
ChallengeResponseAuthentication yes 
UsePAM yes

Below is my code

import paramiko, traceback
from getpass import getpass
paramiko.common.logging.basicConfig(level=paramiko.common.DEBUG)


def connect_to_jump(hostname, port, username, password, ):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        transport = paramiko.Transport((hostname, port))
        transport.start_client() 

        def challenge_handler(title, instructions, prompt_list):
            responses = []
            for prompt in prompt_list:
                if "password" in prompt[0].lower():
                    responses.append(password)
                elif "rsa" in prompt[0].lower() :
                    token = getpass(f"{prompt[0].strip()}: ")
                    responses.append(token)
                else:
                    responses.append(getpass(f"{prompt[0].strip()}: "))
            return responses

        transport.auth_interactive(username, handler=challenge_handler)
        ssh._transport = transport
        return ssh
    

    except Exception as e:
        print(f"Error: {e}")
        traceback.print_exc()
        if 'ssh' in locals():
            ssh.close()


def connect_to_target(jump_ssh, target_host, target_port, username, password):
    dest_addr = (target_host, target_port)
    local_addr = ("127.0.0.1", 0)

    try:
        jump_transport = jump_ssh.get_transport()
       
        channel = jump_transport.open_channel("session", dest_addr, local_addr)       
    
        target_ssh = paramiko.SSHClient()
        target_ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        target_ssh.connect(target_host, port=target_port, username=username, password=password, sock=channel)
    
        stdin, stdout, stderr = target_ssh.exec_command("hostname")
        print(stdout.read().decode())
    finally:
        target_ssh.close()
        jump_ssh.close()

And below is my code to call the above functions:

jump_hostname = 'jump'  
jump_port = 22            
target_host = 'target'
target_port = 22
username = get_user_name()
password = keyring.get_password('unix',username)
jump_ssh = connect_to_jump(jump_hostname, jump_port, username, password)
stdin, stdout, stderr = jump_ssh.exec_command('uname', timeout=10)
o = stdout.read().decode()
if "Linux" in o:
    print(rf"connect to {jump_hostname} successfully")
    connect_to_target(jump_ssh, target_host, target_port, username, password)

I have the line connect to jump successfully which means I have connected to the jump server successfully. However, I couldn't connect to the target. My process hang forever.

Can you please share your thoughts for what could be wrong in my code? I can use putty to ssh to my jump server then to the target.

Below are my logs:

DEBUG:paramiko.transport:starting thread (client mode): 0xbbc4af20
DEBUG:paramiko.transport:Local version/idstring: SSH-2.0-paramiko_3.5.0
DEBUG:paramiko.transport:Remote version/idstring: SSH-2.0-OpenSSH_7.4
INFO:paramiko.transport:Connected (version 2.0, client OpenSSH_7.4)
DEBUG:paramiko.transport:=== Key exchange possibilities ===
DEBUG:paramiko.transport:kex algos: curve25519-sha256, [email protected], ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group-exchange-sha256, diffie-hellman-group16-sha512, diffie-hellman-group18-sha512, diffie-hellman-group-exchange-sha1, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1, diffie-hellman-group1-sha1
DEBUG:paramiko.transport:server key: ssh-rsa, rsa-sha2-512, rsa-sha2-256, ecdsa-sha2-nistp256, ssh-ed25519
DEBUG:paramiko.transport:client encrypt: [email protected], aes128-ctr, aes192-ctr, aes256-ctr,
[email protected], [email protected], aes128-cbc, aes192-cbc, aes256-cbc, blowfish-cbc, cast128-cbc, 3des-cbc
DEBUG:paramiko.transport:server encrypt: [email protected], aes128-ctr, aes192-ctr, aes256-ctr,
[email protected], [email protected], aes128-cbc, aes192-cbc, aes256-cbc, blowfish-cbc, cast128-cbc, 3des-cbc
DEBUG:paramiko.transport:client mac: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], hmac-sha2-256, hmac-sha2-512, hmac-sha1
DEBUG:paramiko.transport:server mac:
[email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], hmac-sha2-256, hmac-sha2-512, hmac-sha1
DEBUG:paramiko.transport:client compress: none, [email protected]
DEBUG:paramiko.transport:server compress: none, [email protected]
DEBUG:paramiko.transport:client lang: <none>
DEBUG:paramiko.transport:server lang: <none>
DEBUG:paramiko.transport:kex follows: False
DEBUG:paramiko.transport:=== Key exchange agreements ===
DEBUG:paramiko.transport:Kex: [email protected]
DEBUG:paramiko.transport:HostKey: ssh-ed25519
DEBUG:paramiko.transport:Cipher: aes128-ctr
DEBUG:paramiko.transport:MAC: hmac-sha2-256
DEBUG:paramiko.transport:Compression: none
DEBUG:paramiko.transport:=== End of kex handshake ===
DEBUG:paramiko.transport:kex engine KexCurve25519 specified hash_algo
<built-in function openssl_sha256>
DEBUG:paramiko.transport:Switch to new keys ...
DEBUG:paramiko.transport:Got EXT_INFO: {'server-sig-algs': b'rsa-sha2-256,rsa-sha2-512'}
DEBUG:paramiko.transport:userauth is OK
INFO:paramiko.transport:Authentication (keyboard-interactive) successful!
DEBUG:paramiko.transport:[chan 0] Max packet in: 32768 bytes
DEBUG:paramiko.transport:Received global request "[email protected]"
DEBUG:paramiko.transport:Rejecting "[email protected]" global request from server.
DEBUG:paramiko.transport:[chan 0] Max packet out: 32768 bytes
DEBUG:paramiko.transport:Secsh channel 0 opened.
DEBUG:paramiko.transport:[chan 0] Sesch channel 0 request ok
DEBUG:paramiko.transport:[chan 0] EOF received (0)
DEBUG:paramiko.transport:[chan 1] Max packet in: 32768 bytes
DEBUG:paramiko.transport:[chan 0] EOF sent (0)
**connect to jump successfully**
DEBUG:paramiko.transport:[chan 1] Max packet out: 32768 bytes
DEBUG:paramiko.transport:Secsh channel 1 opened.
DEBUG:paramiko.transport:starting thread (client mode): 0xaa1dfd00
Share Improve this question edited Jan 23 at 4:14 Ginger_Chacha asked Jan 23 at 3:30 Ginger_ChachaGinger_Chacha 3473 silver badges15 bronze badges 7
  • The channel should be direct-tcpip not session: stackoverflow.com/q/35304525/850848 – Martin Prikryl Commented Jan 23 at 8:43
  • If i used direct-tcpip or forwarded-tcpip i got ChannelException: ChannelException(1, 'Administratively prohibited') error. I assume it's related to the jump server or target server setup but I have very limited knowledge. – Ginger_Chacha Commented Jan 23 at 14:09
  • Then it looks like your "jump" server actually does not allow the port forwarding. Did you test the forwarding first using any commandline/GUI SSH client, before trying to implement it? – Martin Prikryl Commented Jan 23 at 14:47
  • yes. I can use putty to ssh to jump server first then the target server. The target server is a restrict one and it accepts the connection from jump only – Ginger_Chacha Commented Jan 23 at 18:52
  • I also tried other solutions. ssh_command = f'ssh {username}@{target_host} uname' stdin, stdout, stderr = jump_ssh.exec_command(ssh_command) stdin.write(f'{password}\n') stdin.flush() I can execute it successfully via Putty. but in this way it doesn't seem input the password properly. I got password error a few times(although i only write to stdinput once) and then it disconnected – Ginger_Chacha Commented Jan 23 at 21:24
 |  Show 2 more comments

1 Answer 1

Reset to default 0

So my final code to achieve the purpose: connect to the jump server(with ChallengeResponseAuthentication enabled) -> connect to the target server -> run command on target server is as below:

import paramiko, traceback, select, logging, time
from getpass import getpass

logging.basicConfig(level=logging.WARN)
logger = logging.getLogger(__name__)

def wait_for_output(
    shell: paramiko.Channel, 
    prompt_pattern: str =  r'[\$#>\]]\s*$', 
    timeout: float = 30, 
    encoding: str = 'utf-8'
) :
   
    output = b''
    start_time = time.time()
    
    
    while time.time() - start_time < timeout:
        try:
            rlist, _, _ = select.select([shell], [], [], max(0.1, timeout - (time.time() - start_time)))
            
            if shell in rlist:
                if shell.recv_ready():
                    chunk = shell.recv(1024)
                    output += chunk
                    
                    # Check prompt if pattern provided
                    if prompt_pattern and re.search(
                        prompt_pattern, 
                        output.decode(encoding, errors='replace'), 
                        re.MULTILINE
                    ):
                        break
                else:
                    break  # No more data
            else:
                break  # Timeout
        
        except (IOError, paramiko.SSHException) as e:
            # Handle potential SSH/IO errors
            print(f"Error during output retrieval: {e}")
            break
    
    try:
        return output.decode(encoding, errors='replace')
    except Exception as e:
        print(f"Decoding error: {e}")
        return output.decode('utf-8', errors='ignore')

def connect_to_jump(hostname, port, username, password, ):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        transport = paramiko.Transport((hostname, port))
        transport.start_client() 

        def challenge_handler(title, instructions, prompt_list):
            responses = []
            for prompt in prompt_list:
                if "password" in prompt[0].lower():
                    responses.append(password)
                elif "rsa" in prompt[0].lower() :
                    token = getpass(f"{prompt[0].strip()}: ")
                    responses.append(token)
                else:
                    responses.append(getpass(f"{prompt[0].strip()}: "))
            return responses

        transport.auth_interactive(username, handler=challenge_handler)
        ssh._transport = transport
        return ssh
    

    except Exception as e:
        print(f"Error: {e}")
        traceback.print_exc()
        if 'ssh' in locals():
            ssh.close()


def connect_to_target(jump_ssh, target_host, target_port, username, password):
    shell = jump_ssh.invoke_shell()
    time.sleep(1)
    shell.send(f"ssh {username}@{target_host} -p {target_port}\n")
    time.sleep(2)
    output = wait_for_output(shell)
    if "password" in output.lower():
        shell.send(f'{password}\n')
        output2 = wait_for_output(shell)
    return shell, output2

    
def run_command(shell, command):
    shell.send(f"{command} \n")
    output = wait_for_output(shell)
    return output   

def run_commands(shell, commands):
    output = []
    for command in commands:
        o1 = run_command(shell, command)
        o2 = run_command(shell, 'echo $?')
        exit_code = re.search(r'\d+', o2)
        if exit_code:
            exit_code = int(exit_code.group())
        else:
            exit_code = -1
        output.append([o1, exit_code])
    return output

then i used it as below:

username = get_user_name()
password = keyring.get_password('lan',username)
try:
    jump_ssh = connect_to_jump(jump_host, jump_port, username, password)
    stdin, stdout, stderr = jump_ssh.exec_command("hostname")
    cmd_output = stdout.read().decode()
    print(rf"connected to {cmd_output}")
    shell, output = connect_to_target(jump_ssh, target_host, target_port, username, password)
    commands = ['hostname', 'cd ~/scripts', '1.sh']
    output = run_commands(shell, commands)
finally:
    if 'shell' in locals():
        shell.close()
    if 'jump_ssh' in locals():
        jump_ssh.close()

It didn't resolve the issue i raised in the subject - i still didn't figure out why the "direct tcp ip" or "forwareded tcp ip" didn't work, or the "session" option in the socket led to the process hang. But the most important thing to me is the above works already :) and now i can focus on the rest of the business logic.

本文标签: pythonparamikotransport hang after sshconnect from jump server to target serverStack Overflow