SFTP with an SSH ProxyCommand in python
<p>I am downloading some files with the following SSH config:</p>

<pre><code>Host proxy
    Hostname      proxy.example.com
    User          proxyuser
    IdentityFile  ~/.ssh/proxy_id_rsa

Host target       # uses password auth
    Hostname      target.example.com
    User          targetuser
    ProxyCommand  ssh proxy nc %h %p
</code></pre>

<p>I'm trying to automate downloading the files--currently using paramiko, but could use another library if it would be easier.</p>

<p>Here's what I'm trying based on some other answers:</p>

<pre><code>from paramiko.proxy import ProxyCommand
from paramiko.transport import Transport
from paramiko.sftp_client import SFTPClient

proxy = ProxyCommand('ssh -i /Users/ben/.ssh/proxy_id_rsa proxyuser@proxy.example.com nc target.example.com 22')
client = SFTPClient(proxy)
client.connect(username='targetuser', password='targetpassword')
</code></pre>

<p>However, this throws the error</p>

<pre><code>Traceback (most recent call last):
  File "sftp.py", line 6, in &lt;module&gt;
    client = SFTPClient(proxy)
  File "/Users/ben/.virtualenvs/venv/lib/python3.5/site-packages/paramiko/sftp_client.py", line 99, in __init__
    server_version = self._send_version()
  File "/Users/ben/.virtualenvs/venv/lib/python3.5/site-packages/paramiko/sftp.py", line 105, in _send_version
    t, data = self._read_packet()
  File "/Users/ben/.virtualenvs/venv/lib/python3.5/site-packages/paramiko/sftp.py", line 177, in _read_packet
    raise SFTPError('Garbage packet received')
paramiko.sftp.SFTPError: Garbage packet received
</code></pre>

<p>Unfortunately the error message is not very helpful so I'm at a loss for what I could change. I can't change the config on <code>target</code> and I'd prefer not to change the config on <code>proxy</code> if avoidable. Any suggestions?</p>

<p>Solved with the following:</p>

<pre><code>class PatchedProxyCommand(ProxyCommand):
    # work around https://github.com/paramiko/paramiko/issues/789

    @property
    def closed(self):
        return self.process.returncode is not None

    @property
    def _closed(self):
        # Concession to Python 3 socket-like API
        return self.closed

    def close(self):
        self.process.kill()
        self.process.poll()

proxy = PatchedProxyCommand('ssh -i /Users/ben/.ssh/proxy_id_rsa '
                            'proxyuser@proxy.example.com nc target.example.com 22')

transport = Transport(proxy)
key = HostKeyEntry.from_line('target.example.com ssh-rsa '
                             'AAAAB3NzaC1yc2EAAAA/base64+stuff==').key
transport.connect(hostkey=key,
                  username='targetuser', password='targetpass')
sftp = SFTPClient.from_transport(transport)

print(sftp.listdir())
</code></pre>

python ssh proxy sftp paramiko