byteskeptical / sftpretty

Provides multi-threaded routines and high level protocol abstractions for a pretty quick & simple file transfer experience. Super duper close to a drop in replacement for pysftp.
https://docs.sftpretty.com/
BSD 3-Clause "New" or "Revised" License
34 stars 8 forks source link

Hostkey checking fails on nonstandard port #55

Open riley-martine opened 1 week ago

riley-martine commented 1 week ago

When trying to access a server on a nonstandard port, sftpretty fails to check the host keys correctly, and fails to connect. This means I have to disable hostkey checking, which is potentially dangerous.

I have a server server.example.com that I am trying to access via SFTP on port 2108. Using sftpretty:

#!/usr/bin/env python3

import sftpretty
from paramiko.agent import Agent

agent = Agent()
keys = [
    pkey
    for pkey in agent.get_keys()
    if pkey.fingerprint == "SOME_HASH"
]

with sftpretty.Connection(
    host="server.example.com",
    username="some_user",
    port=2108,
    private_key=keys[0],
) as sftp:
    print(sftp.listdir())

This fails with the following:

paramiko.ssh_exception.SSHException: No hostkey for host [server.example.com] found. ```console Traceback (most recent call last): File "/Users/zero/dev/homebrew-auto-seedbox/./scripts/sftpretty_demo.py", line 16, in with sftpretty.Connection( ^^^^^^^^^^^^^^^^^^^^^ File "/Users/zero/dev/homebrew-auto-seedbox/.direnv/python-3.12/lib/python3.12/site-packages/sftpretty/__init__.py", line 198, in __init__ self._start_transport(self._config.get('hostname') or host, File "/Users/zero/dev/homebrew-auto-seedbox/.direnv/python-3.12/lib/python3.12/site-packages/sftpretty/__init__.py", line 392, in _start_transport raise err File "/Users/zero/dev/homebrew-auto-seedbox/.direnv/python-3.12/lib/python3.12/site-packages/sftpretty/__init__.py", line 378, in _start_transport user_hostkey = self._cnopts.get_hostkey(host) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/zero/dev/homebrew-auto-seedbox/.direnv/python-3.12/lib/python3.12/site-packages/sftpretty/__init__.py", line 156, in get_hostkey raise SSHException(f'No hostkey for host [{host}] found.') paramiko.ssh_exception.SSHException: No hostkey for host [server.example.com] found. ```

This is because in known_hosts, this server is represented like so:

[server.example.com]:2108 ssh-ed25519 AAAAhash
github.com ssh-ed25519 AAAAhash

It needs to be looked up as [server.example.com]:2108, but it is being looked up as server.example.com.

See how paramiko's SSH client handles this case:

if port == SSH_PORT:
     server_hostkey_name = hostname
 else:
     server_hostkey_name = "[{}]:{}".format(hostname, port)

I think the solution is either to delegate hostkey checking to paramiko, or to implement the same strategy they use here.

OS: MacOS 14.2.1 (23C71) (ARM) sftpretty version: 1.1.6

Thank you for making this very useful library!

byteskeptical commented 1 week ago

Hi Riley,

Thank you for the detailed report. I went ahead an implemented similar logic to the paramiko example and tried adding some tests. I was unable to enable them for now due to pytest-sftpserver limitations. I have a PR that I'm preparing but currently it seems the project is unmaintained so forking may be necessary.