Tds18
Tds18

Reputation: 65

Python Paramiko - SSH keyboard-interactive authentication isn't working on remote server

Trying to log using Python 3.6 Paramiko to a remove server that propmts user and pass and expects keyboard input (per my understanding).

I reviewed several options in earlier thread, but can't get it to work in any way.

Using regular PuTTY SSH login, this remote server returns the below, expecting user after prompt, and then prompts for pass.

login as: user:
[email protected]'s password:

My code, mainly taken from Python Paramiko (Client) Multifactor Authentication - however I removed the user's input for username and pw and hardcoded them in the global variables section. Also removed the MFA (OTP) which isn't required

#!/usr/bin/python3.6

import paramiko     #Provides SSH functionality
import getpass      #Allows for secure prompting and collection of the user password
import os           #Used to setup the Paramiko log file
import logging      #Used to setup the Paramiko log file
import socket       #This method requires that we create our own socket

#Global variables are used to store these data because they're sent to the server by a callback
user = 'user'
pw = 'password'
node = '**.**.**.***'
command = '/info'

def inter_handler(title, instructions, prompt_list):

    resp = []  #Initialize the response container

    #Walk the list of prompts that the server sent that we need to answer
    for pr in prompt_list:
        #str() used to to make sure that we're dealing with a string rather than a unicode string
        #strip() used to get rid of any padding spaces sent by the server
        
        if str(pr[0]).strip() == "Username:":
            resp.append(user)
        elif str(pr[0]).strip() == "Password:":
            resp.append(pw)

    return tuple(resp)  #Convert the response list to a tuple and return it

#Main Entry Point
if __name__ == "__main__":
    #Setup Paramiko logging; this is useful for troubleshooting
    paramiko.util.log_to_file(os.path.expanduser('~/paramiko.log'), logging.DEBUG)

    #Create a socket and connect it to port 22 on the host
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((node, 22))

    #Make a Paramiko Transport object using the socket
    ts = paramiko.Transport(sock)

    #Tell Paramiko that the Transport is going to be used as a client
    ts.start_client(timeout=10)

    #Begin authentication; note that the username and callback are passed
    ts.auth_interactive(user, inter_handler)

    #Opening a session creates a channel along the socket to the server
    chan = ts.open_session(timeout=10)

    #Now the channel can be used to execute commands
    chan.exec_command(command)

Python output - $ ./be.py /usr/lib/python3.6/site-packages/paramiko/rsakey.py:119: CryptographyDeprecationWarning: signer and verifier have been deprecated. Please use sign and verify instead.
algorithm=hashes.SHA1(), Traceback (most recent call last): File "./be.py", line 78, in ts.auth_interactive(user, inter_handler) File "/usr/lib/python3.6/site-packages/paramiko/transport.py", line 1389, in auth_interactive return self.auth_handler.wait_for_response(my_event) File "/usr/lib/python3.6/site-packages/paramiko/auth_handler.py", line 208, in wait_for_response raise e paramiko.ssh_exception.AuthenticationException: Authentication failed.

Paramiko logger output -

DEB [20220902-11:13:37.706] thr=1 paramiko.transport: starting thread (client mode): 0xa6496a58 DEB [20220902-11:13:37.706] thr=1
paramiko.transport: Local version/idstring: SSH-2.0-paramiko_2.1.1 DEB [20220902-11:13:37.707] thr=1 paramiko.transport: Remote version/idstring: SSH-2.0-Alteon INF [20220902-11:13:37.708] thr=1
paramiko.transport: Connected (version 2.0, client Alteon) DEB [20220902-11:13:37.737] thr=1 paramiko.transport: kex algos:['diffie-hellman-group14-sha1'] server key:['ssh-rsa'] client encrypt:['aes128-ctr', 'aes256-ctr', 'aes192-ctr'] server encrypt:['aes128-ctr', 'aes256-ctr', 'aes192-ctr'] client mac:['hmac-sha1'] server mac:['hmac-sha1'] client compress:['none'] server compress:['none'] client lang:[''] server lang:[''] kex follows?False DEB [20220902-11:13:37.738] thr=1 paramiko.transport: Kex agreed: diffie-hellman-group14-sha1 DEB [20220902-11:13:37.738] thr=1 paramiko.transport: Cipher agreed: aes128-ctr DEB [20220902-11:13:37.738] thr=1 paramiko.transport: MAC agreed: hmac-sha1 DEB [20220902-11:13:37.738] thr=1 paramiko.transport: Compression agreed: none DEB [20220902-11:13:37.883] thr=1
paramiko.transport: kex engine KexGroup14 specified hash_algo DEB [20220902-11:13:37.883] thr=1
paramiko.transport: Switch to new keys ... DEB [20220902-11:13:37.921] thr=1 paramiko.transport: userauth is OK INF [20220902-11:13:37.923] thr=1 paramiko.transport: Authentication (keyboard-interactive) failed.

Can you direct me towards what I'm missing here? I simply want to pass the correct username and password and then send a command and return the output

This is verbose ssh output of connection to the same server. Per the 2nd highlighted section, I take it that it's keyboard interactive?

$ ssh -vvv user@**.**.**.***
OpenSSH_7.4p1, OpenSSL 1.0.2k-fips  26 Jan 2017
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 58: Applying options for *
debug2: resolving "**.**.**.***" port 22
debug2: ssh_connect_direct: needpriv 0
debug1: Connecting to **.**.**.*** [**.**.**.***] port 22.
debug1: Connection established.
debug1: key_load_public: No such file or directory
debug1: identity file /home/user/.ssh/id_rsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/user/.ssh/id_rsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/user/.ssh/id_dsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/user/.ssh/id_dsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/user/.ssh/id_ecdsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/user/.ssh/id_ecdsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/user/.ssh/id_ed25519 type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/user/.ssh/id_ed25519-cert type -1
debug1: Enabling compatibility mode for protocol 2.0
debug1: Local version string SSH-2.0-OpenSSH_7.4
debug1: Remote protocol version 2.0, remote software version Alteon
debug1: no match: Alteon
debug2: fd 3 setting O_NONBLOCK
debug1: Authenticating to **.**.**.***:22 as 'user'
debug3: hostkeys_foreach: reading file "/home/user/.ssh/known_hosts"
debug3: record_hostkey: found key type RSA in file /home/user/.ssh/known_hosts:1
debug3: load_hostkeys: loaded 1 keys from **.**.**.***
debug3: order_hostkeyalgs: prefer hostkeyalgs: [email protected],rsa-sha2-512,rsa-sha2-256,ssh-rsa
debug3: send packet: type 20
debug1: SSH2_MSG_KEXINIT sent
debug3: receive packet: type 20
debug1: SSH2_MSG_KEXINIT received
debug2: local client KEXINIT proposal
debug2: KEX algorithms: 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,ext-info-c
debug2: host key algorithms: [email protected],rsa-sha2-512,rsa-sha2-256,ssh-rsa,[email protected],[email protected],[email protected],[email protected],[email protected],ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,ssh-dss
debug2: ciphers ctos: [email protected],aes128-ctr,aes192-ctr,aes256-ctr,[email protected],[email protected],aes128-cbc,aes192-cbc,aes256-cbc
debug2: ciphers stoc: [email protected],aes128-ctr,aes192-ctr,aes256-ctr,[email protected],[email protected],aes128-cbc,aes192-cbc,aes256-cbc
debug2: MACs ctos: [email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: MACs stoc: [email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: compression ctos: none,[email protected],zlib
debug2: compression stoc: none,[email protected],zlib
debug2: languages ctos:
debug2: languages stoc:
debug2: first_kex_follows 0
debug2: reserved 0
debug2: peer server KEXINIT proposal
debug2: KEX algorithms: diffie-hellman-group14-sha1
debug2: host key algorithms: ssh-rsa
debug2: ciphers ctos: aes128-ctr,aes256-ctr,aes192-ctr
debug2: ciphers stoc: aes128-ctr,aes256-ctr,aes192-ctr
debug2: MACs ctos: hmac-sha1
debug2: MACs stoc: hmac-sha1
debug2: compression ctos: none
debug2: compression stoc: none
debug2: languages ctos:
debug2: languages stoc:
debug2: first_kex_follows 0
debug2: reserved 0
debug1: kex: algorithm: diffie-hellman-group14-sha1
debug1: kex: host key algorithm: ssh-rsa
debug1: kex: server->client cipher: aes128-ctr MAC: hmac-sha1 compression: none
debug1: kex: client->server cipher: aes128-ctr MAC: hmac-sha1 compression: none
debug1: kex: diffie-hellman-group14-sha1 need=20 dh_need=20
debug1: kex: diffie-hellman-group14-sha1 need=20 dh_need=20
debug1: sending SSH2_MSG_KEXDH_INIT
debug2: bits set: 1013/2048
debug3: send packet: type 30
debug1: expecting SSH2_MSG_KEXDH_REPLY
debug3: receive packet: type 31
debug1: Server host key: ssh-rsa SHA256:cxQ1CQt1YYKNN/OUdFbz2ht5J75ZpG7jWh8+KJx21wg
debug3: hostkeys_foreach: reading file "/home/user/.ssh/known_hosts"
debug3: record_hostkey: found key type RSA in file /home/user/.ssh/known_hosts:1
debug3: load_hostkeys: loaded 1 keys from **.**.**.***
debug1: Host '**.**.**.***' is known and matches the RSA host key.
debug1: Found key in /home/user/.ssh/known_hosts:1
debug2: bits set: 1006/2048
debug3: send packet: type 21
debug2: set_newkeys: mode 1
debug1: rekey after 4294967296 blocks
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug3: receive packet: type 21
debug1: SSH2_MSG_NEWKEYS received
debug2: set_newkeys: mode 0
debug1: rekey after 4294967296 blocks
debug2: key: /home/user/.ssh/id_rsa ((nil))
debug2: key: /home/user/.ssh/id_dsa ((nil))
debug2: key: /home/user/.ssh/id_ecdsa ((nil))
debug2: key: /home/user/.ssh/id_ed25519 ((nil))
debug3: send packet: type 5
debug3: receive packet: type 6
debug2: service_accept: ssh-userauth
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug3: send packet: type 50
debug3: receive packet: type 51

debug1: Authentications that can continue: publickey,password,none,keyboard-interactive
debug3: start over, passed a different list publickey,password,none,keyboard-interactive
debug3: preferred gssapi-keyex,gssapi-with-mic,publickey,keyboard-interactive,password
debug3: authmethod_lookup publickey
debug3: remaining preferred: keyboard-interactive,password

debug3: authmethod_is_enabled publickey
debug1: Next authentication method: publickey
debug1: Trying private key: /home/user/.ssh/id_rsa
debug3: no such identity: /home/user/.ssh/id_rsa: No such file or directory
debug1: Trying private key: /home/user/.ssh/id_dsa
debug3: no such identity: /home/user/.ssh/id_dsa: No such file or directory
debug1: Trying private key: /home/user/.ssh/id_ecdsa
debug3: no such identity: /home/user/.ssh/id_ecdsa: No such file or directory
debug1: Trying private key: /home/user/.ssh/id_ed25519
debug3: no such identity: /home/user/.ssh/id_ed25519: No such file or directory
debug2: we did not send a packet, disable method
debug3: authmethod_lookup keyboard-interactive
debug3: remaining preferred: password
debug3: authmethod_is_enabled keyboard-interactive
debug1: Next authentication method: keyboard-interactive
debug2: userauth_kbdint
debug3: send packet: type 50
debug2: we sent a keyboard-interactive packet, wait for reply
debug3: receive packet: type 51
debug1: Authentications that can continue: publickey,password,none,keyboard-interactive
debug3: userauth_kbdint: disable: no info_req_seen
debug2: we did not send a packet, disable method
debug3: authmethod_lookup password
debug3: remaining preferred:
debug3: authmethod_is_enabled password
debug1: Next authentication method: password
user@**.**.**.***'s password:
debug3: send packet: type 50
debug2: we sent a password packet, wait for reply
debug3: receive packet: type 52
debug1: Authentication succeeded (password).
Authenticated to **.**.**.*** ([**.**.**.***]:22).
debug1: channel 0: new [client-session]
debug3: ssh_session2_open: channel_new: 0
debug2: channel 0: send open
debug3: send packet: type 90
debug1: Entering interactive session.
debug1: pledge: network
debug3: receive packet: type 91
debug2: callback start
debug2: fd 3 setting TCP_NODELAY
debug3: ssh_packet_set_tos: set IP_TOS 0x10
debug2: client_session2_setup: id 0
debug2: channel 0: request pty-req confirm 1
debug3: send packet: type 98
debug1: Sending environment.
debug3: Ignored env XDG_SESSION_ID
debug3: Ignored env HOSTNAME
debug3: Ignored env TERM
debug3: Ignored env SHELL
debug3: Ignored env HISTSIZE
debug3: Ignored env SSH_CLIENT
debug3: Ignored env SSH_TTY
debug3: Ignored env USER
debug3: Ignored env LS_COLORS
debug3: Ignored env MAIL
debug3: Ignored env PATH
debug3: Ignored env PWD
debug1: Sending env LANG = en_US.UTF-8
debug2: channel 0: request env confirm 0
debug3: send packet: type 98
debug3: Ignored env HISTCONTROL
debug3: Ignored env SHLVL
debug3: Ignored env HOME
debug3: Ignored env LOGNAME
debug3: Ignored env SSH_CONNECTION
debug3: Ignored env LESSOPEN
debug3: Ignored env XDG_RUNTIME_DIR
debug3: Ignored env _
debug2: channel 0: request shell confirm 1
debug3: send packet: type 98
debug2: callback done
debug2: channel 0: open confirm rwindow 16384 rmax 16384
debug3: receive packet: type 99
debug2: channel_input_status_confirm: type 99 id 0
debug2: PTY allocation request accepted on channel 0
debug3: receive packet: type 99
debug2: channel_input_status_confirm: type 99 id 0
debug2: shell request accepted on channel 0

After upgrading Python 3.6 to 3.9, and using basic Paramiko SSHClient, I get no output from Python on the run, but how can I get the command's output?

It's a production system that I wouldn't want to do any change as for now until it's verified to work.

I tried setting a variable to the command output line and read from it afterwards, but looks like it's not readable.

It's an operating system built on top of Linux, but can't output _stdout etc because the server doesn't maintain them.

tried -

...
paramiko.util.log_to_file(os.path.expanduser('~/paramiko.log'), logging.DEBUG)
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=node,
            password=pw,
            username=user,
            port=22)
ssh.exec_command(command)
output = ssh.exec_command(command)
print(output)
ssh.close()

But getting python output of -

$ ./so.py
(<paramiko.ChannelFile from <paramiko.Channel 0 (open) window=16384 -> <paramiko.Transport at 0xf05fd700 (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>, <paramiko.ChannelFile from <paramiko.Channel 0 (open) window=16384 -> <paramiko.Transport at 0xf05fd700 (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>, <paramiko.ChannelFile from <paramiko.Channel 0 (open) window=16384 -> <paramiko.Transport at 0xf05fd700 (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>)
Exception ignored in: <function BufferedFile.__del__ at 0x7f71f247a160>
Traceback (most recent call last):
  File "/home/tamirs/.local/lib/python3.9/site-packages/paramiko/file.py", line 66, in __del__
  File "/home/tamirs/.local/lib/python3.9/site-packages/paramiko/channel.py", line 1392, in close
  File "/home/tamirs/.local/lib/python3.9/site-packages/paramiko/channel.py", line 991, in shutdown_write
  File "/home/tamirs/.local/lib/python3.9/site-packages/paramiko/channel.py", line 963, in shutdown
  File "/home/tamirs/.local/lib/python3.9/site-packages/paramiko/channel.py", line 1248, in _send_eof
  File "/home/tamirs/.local/lib/python3.9/site-packages/paramiko/channel.py", line 1212, in _log
  File "/usr/local/lib/python3.9/logging/__init__.py", line 1512, in log
  File "/usr/local/lib/python3.9/logging/__init__.py", line 1589, in _log
  File "/usr/local/lib/python3.9/logging/__init__.py", line 1598, in handle
  File "/usr/local/lib/python3.9/logging/__init__.py", line 806, in filter
  File "/home/tamirs/.local/lib/python3.9/site-packages/paramiko/util.py", line 262, in filter
TypeError: 'NoneType' object is not callable

On a regular Linux distro using the same method I've got the output using the stdout and read().decode(), but this server's OS doesn't maintain them.

How can I get the actual output and not that stream from this server?

Thank you!

Upvotes: 2

Views: 3007

Answers (3)

Tds18
Tds18

Reputation: 65

Answering the question, after getting the authentication to work with your directions, I received an error indicating of 'unknown SCP command'.

I later verified that with a wrong password I stop receiving it and received Authentication failed instead - it lead me to understand that the auth is working and only SCP is supported on my remote server for some reason.

I then found that great thread - Executing command using Paramiko exec_command on device is not working

It indicates that by checking remote execution using ssh user@host command we can determine if the remote server supports it or not, and if not - get it done using ssh.invoke_shell() - which done the job in my case.

Important to set the appropriate time.wait() in the right places or else the commands won't be able to both run on the remote server and get the output in time and eventually connection will be disconnected before the desired actions taken.

Thank you!!

Upvotes: 0

Martin Prikryl
Martin Prikryl

Reputation: 202534

The ssh log looks like a plain password authentication to me. Not keyboard-interactive.

So basic Paramiko SSHClient API should work for you:

ssh = paramiko.SSHClient()
ssh.connect(hostname=node, username=user, password=pw)

You will also have to deal with host key, but not using AutoAddPolicy, as the other answer shows. That's security flaw. For correct solution, see Paramiko "Unknown Server".


You claim that with password authentication, it does not fail with incorrect password. That's likely some peculiarity of your server. It seems it fails only when you actually try to execute the command.

For details, see Executing command using Paramiko exec_command on device is not working.

Upvotes: 1

Vojtěch Chvojka
Vojtěch Chvojka

Reputation: 447

We are using paramiko.SSHClient. Wouldn't that work for you too?

import paramiko

user = 'user'
pw = 'password'
node = '**.**.**.***'
command = '/info'

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=node,
            password=pw,
            username=user,
            port=22)
ssh.exec_command(command)

Upvotes: 0

Related Questions