Reputation: 6403
I've got the following code:
import paramiko
policy = paramiko.client.WarningPolicy()
client = paramiko.client.SSHClient()
client.set_missing_host_key_policy(policy)
username = '...'
password = '...'
file_path = '...'
pkey = paramiko.RSAKey.from_private_key_file(file_path)
client.connect('...', username=username, password=password, pkey=key)
sftp = client.open_sftp()
From the docs, it seems like it should work. Everything works successfully, but when the code hits client.open_sftp
it bombs with a SSHException: Unable to open channel.
and the transport (from client.get_transport
) is active but not authenticated. I'm also having trouble enabling debug logging for this (I'm trying logging.getLogger('paramiko').setLevel(logging.DEBUG)
without success.)
Any ideas on where I can start to debug this very vague error message?
Upvotes: 5
Views: 7731
Reputation: 1113
Here is an example code that authenticates with "password" first and then "publickey"
import threading
import paramiko
# Define server credentials
hostname = '127.0.0.1'
port = 22
username = 'username'
password = 'password'
private_key_path = '/path/to/id_rsa'
try:
# Initialize transport
transport = paramiko.Transport((hostname, port), disabled_algorithms={'pubkeys': ['rsa-sha2-256', 'rsa-sha2-512']})
transport.connect()
# First authenticate with username and password
transport.auth_password(username=username, password=password)
print("Password authentication successful.")
# Next authenticate with the ssh public key
# Load the private key
private_key = paramiko.RSAKey.from_private_key_file(private_key_path)
publickey_auth_event = threading.Event()
publickey_auth_handler = paramiko.auth_handler.AuthHandler(transport)
transport.auth_handler = publickey_auth_handler
transport.lock.acquire()
publickey_auth_handler.auth_event = publickey_auth_event
publickey_auth_handler.auth_method = "publickey"
publickey_auth_handler.username = username
publickey_auth_handler.private_key = private_key
userauth_message = paramiko.message.Message()
userauth_message.add_string('ssh-userauth')
userauth_message.rewind()
publickey_auth_handler._parse_service_accept(userauth_message)
transport.lock.release()
publickey_auth_handler.wait_for_response(publickey_auth_event)
print("Public key authentication successful.")
# Open an SFTP session
sftp = paramiko.SFTPClient.from_transport(transport)
print("SFTP session established.")
# List files in the remote directory
print("Remote files:", sftp.listdir())
# Close the connection
sftp.close()
transport.close()
except Exception as e:
print(f"Authentication failed: {e}")
Upvotes: 0
Reputation: 2451
I was able to use this solution on paramiko's github found from a still-open issue (at the time of writing this). I'll include the original solution from that github issue, and the slight variant i use in my own code.
From jacky15 in that github issue:
import paramiko
paramiko.util.log_to_file('/path/to/log')
hostname = 'server.name'
port = 12345
username = 'username'
password = 'password'
pkey = paramiko.RSAKey.from_private_key_file('/path/to/key')
transport = paramiko.Transport((hostname, port))
transport.connect()
# auth the public key as usual, auth service now is activated on server
transport.auth_publickey(username=username, key=pkey)
# try to send another userauth request without request auth service
m = paramiko.Message()
m.add_byte(paramiko.common.cMSG_USERAUTH_REQUEST)
m.add_string(username)
m.add_string('ssh-connection')
m.add_string('password')
m.add_boolean(False)
py3_password = paramiko.py3compat.bytestring(password)
m.add_string(py3_password)
transport._send_message(m)
# now it works! : )
sftp_client = paramiko.SFTPClient.from_transport(transport)
from my code:
transport = paramiko.Transport((self.hostname, self.port))
transport.start_client(event=None, timeout=30)
transport.get_remote_server_key()
my_key = paramiko.RSAKey.from_private_key_file(self.hostkey)
transport.auth_publickey(self.username, my_key)
m = paramiko.Message()
m.add_byte(paramiko.common.cMSG_USERAUTH_REQUEST)
m.add_string(self.username)
m.add_string('ssh-connection')
m.add_string('password')
m.add_boolean(False)
py3_password = paramiko.py3compat.bytestring(self.password)
m.add_string(py3_password)
transport._send_message(m)
self.connection = paramiko.SFTPClient.from_transport(transport)
Upvotes: 0
Reputation: 202078
The Paramiko high-level API, SSHClient
can handle common two-factor authentication on its own. For example for key and password authentication, use:
ssh = paramiko.SSHClient()
ssh.connect(
"example.com", username="username", password="password",
key_filename="/path/to/key")
So the complicated code in the answer by @osekmedia is usually not needed.
I know of only two scenarios, where it can "help":
SSHClient
by default verifies the host key. You may mistake failure to verify the host key with failure of the two-factor authentication. They are not related. It's just that the low-level Transport
API, that the @osekmedia's code uses, does not verify the host key, what avoids your actual problem. But that's a security flaw. For a correct solution, see Paramiko "Unknown Server".
You might think that you are using password authentication, while you actually use keyboard-interactive authentication. Normally Paramiko can handle keyboard-interactive authentication, even if you mistakenly ask for password authentication. But with some obscure servers, this does not work, see Password authentication in Python Paramiko fails, but same credentials work in SSH/SFTP client. In such case, the following code should do:
username = "username"
transport = paramiko.Transport('example.com')
transport.connect(username=username)
key = paramiko.RSAKey.from_private_key_file("/path/to/key")
transport.auth_publickey(username, key)
def handler(title, instructions, fields):
if len(fields) > 1:
raise SSHException("Expecting one field only.")
return ["password"]
transport.auth_interactive(username, handler)
Note that the above code uses Transport
, so it by default bypasses host key verification. Use hostkey
argument of the Transport.connect
to correct that.
Upvotes: -2
Reputation: 683
Sorry for the late response but this problem was really hard to find any information on so i wanted to post a solution for anyone else stuck on this issue.
After pulling my hair out trying to solve this I found a solution thanks to some code posted by Doug Ellwanger and Daniel Brownridge. The problem seems to be caused by the way the multi-factor authentication is handled using more of an interactive style.
import paramiko
import threading
...
username = '...'
password = '...'
file_path = '...'
pkey = paramiko.RSAKey.from_private_key_file(file_path)
sftpClient = multifactor_auth('...', 22, username, pkey, password)
...
def multifactor_auth_sftp_client(host, port, username, key, password):
#Create an SSH transport configured to the host
transport = paramiko.Transport((host, port))
#Negotiate an SSH2 session
transport.connect()
#Attempt authenticating using a private key
transport.auth_publickey(username, key)
#Create an event for password auth
password_auth_event = threading.Event()
#Create password auth handler from transport
password_auth_handler = paramiko.auth_handler.AuthHandler(transport)
#Set transport auth_handler to password handler
transport.auth_handler = password_auth_handler
#Aquire lock on transport
transport.lock.acquire()
#Register the password auth event with handler
password_auth_handler.auth_event = password_auth_event
#Set the auth handler method to 'password'
password_auth_handler.auth_method = 'password'
#Set auth handler username
password_auth_handler.username = username
#Set auth handler password
password_auth_handler.password = password
#Create an SSH user auth message
userauth_message = paramiko.message.Message()
userauth_message.add_string('ssh-userauth')
userauth_message.rewind()
#Make the password auth attempt
password_auth_handler._parse_service_accept(userauth_message)
#Release lock on transport
transport.lock.release()
#Wait for password auth response
password_auth_handler.wait_for_response(password_auth_event)
#Create an open SFTP client channel
return transport.open_sftp_client()
I hope this helps, it worked for my project.
Upvotes: 18
Reputation: 21
In your script you declare
pkey = paramiko.RSAKey.from_private_key_file(file_path)
and then instead of pkey, you have pkey = key
.
Not sure what key is coming from but that might be an issue.
Upvotes: 2