title: ssh tunnel to server running on cluster compute node
body: <p>I have a script to launch Jupyter Notebook server running on a particular node of our cluster, and need to be able to access the running instance from a local windows machine (browser interface).  The ssh commands are handled through paramiko, and I'd like a similarly pythonic way of creating the tunnels, rather than just a system call. I am trying out sshtunnel, which is, unfortunately, my current stumbling block.</p>
  
  <p>I need to create two tunnels as outlined below. For clarity, HOME is the windows machine, HOST is the cluster head-node, and NODE is the particular node on which the server is running. </p>
  
  <p>The server is bound to localhost, by default at port 8888. The ports underneath HOME and HOST are arbitrary, I just use them for illustration (in reality, the most convenient HOME port is also 8888) </p>
  
  <pre><code>[HOME] &lt;------&gt; [HOST] &lt;------&gt; [NODE]
   1111            2222            8888
  </code></pre>
  
  <p>I know the code below is incomplete and just plain wrong, as I can't figure out how to correctly combine the local/remote bindings. Right now I don't know if I'm barking up the wrong tree - the docs suggest that I can create both tunnels in a single pass, but do I need two separate instances? </p>
  
  <pre><code>from sshtunnel import SSHTunnelForwarder
  
  t1 = SSHTunnelForwarder(
       ('headnode.server', 22),
       ssh_username='meagain',
       ssh_pkey='~/.ssh/id_rsa',
       local_bind_address = ('localhost',1111),
       remote_bind_addresses =[ ('localhost',2222), ('compute.node',8888)])
  t1.start()
  </code></pre>
  
  <p>edit: trying out option 2, as outlined below. The config is set up and working fine on the command-line using an SSH client. For reference, this is under Anaconda with python 3.6, on a Windows 7 machine (hence the additional file-path wrangling below)</p>
  
  <pre><code>print(sshtunnel.__version__)
  '0.1.0'
  print(paramiko.__version__)
  '2.1.2'
  
  
  
  t1 = sshtunnel.SSHTunnelForwarder(
      NODE,
      ssh_username=USER,
      ssh_pkey=paramiko.RSAKey.from_private_key_file(os.path.expanduser(os.path.normpath('~/.ssh/id_rsa'))),
      ssh_proxy_enabled=True,  # DEFAULT
      ssh_config_file=os.path.expanduser(os.path.normpath('~/.ssh/config')),  # if rather than default
      local_bind_address=('localhost', 1111),
      remote_bind_address=('localhost', 8889)
  )
  t1.start()
  </code></pre>
  
  <p>logs: </p>
  
  <pre><code>2017-04-06 12:28:25,523| INF | MainThrea/0981@sshtunnel | 0 keys loaded from agent
      t1.start()
    File "D:\Anaconda3\lib\site-packages\sshtunnel.py", line 1224, in start
      reason='Could not establish session to SSH gateway')
    File "D:\Anaconda3\lib\site-packages\sshtunnel.py", line 1036, in _raise
      raise exception(reason)
  sshtunnel.BaseSSHTunnelForwarderError: Could not establish session to SSH gateway
  &gt;&gt;&gt; 2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport | paramiko.ssh_exception.ProxyCommandFailure: ('ssh HOST -W NODE:22', 'An operation was attempted on something that is not a socket')
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport | 
  2017-04-06 12:28:25,538| ERR | MainThrea/1076@sshtunnel | Could not connect to gateway NODE:22 : ssh HOST -W NODE:22
  Traceback (most recent call last):
    File "D:\Bioinformatics\Scripts\bsub_jupyter\tunneller.py", line 234, in &lt;module&gt;
  2017-04-06 12:28:25,523| INF | MainThrea/0901@sshtunnel | Connecting to gateway: NODE:22 as user 'USER'
  2017-04-06 12:28:25,523| DEB | MainThrea/0904@sshtunnel | Concurrent connections allowed: True
  2017-04-06 12:28:25,523| DEB | MainThrea/1284@sshtunnel | Trying to log in with key: b'1e44a013d97d417e4900025c6c11d073'
  2017-04-06 12:28:25,523| DEB | MainThrea/1047@sshtunnel | Connecting via proxy: 'HOST'
  2017-04-06 12:28:25,523| ERR |  Thread-2/1572@transport | Exception: ('ssh HOST -W NODE:22', 'An operation was attempted on something that is not a socket')
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport | Traceback (most recent call last):
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport |   File "D:\Anaconda3\lib\site-packages\paramiko\proxy.py", line 96, in recv
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport |     [self.process.stdout], [], [], select_timeout)
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport | OSError: [WinError 10038] An operation was attempted on something that is not a socket
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport | 
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport | During handling of the above exception, another exception occurred:
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport | 
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport | Traceback (most recent call last):
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport |   File "D:\Anaconda3\lib\site-packages\paramiko\transport.py", line 1749, in run
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport |     self._check_banner()
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport |   File "D:\Anaconda3\lib\site-packages\paramiko\transport.py", line 1893, in _check_banner
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport |     buf = self.packetizer.readline(timeout)
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport |   File "D:\Anaconda3\lib\site-packages\paramiko\packet.py", line 331, in readline
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport |     buf += self._read_timeout(timeout)
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport |   File "D:\Anaconda3\lib\site-packages\paramiko\packet.py", line 485, in _read_timeout
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport |     x = self.__socket.recv(128)
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport |   File "D:\Anaconda3\lib\site-packages\paramiko\proxy.py", line 107, in recv
  2017-04-06 12:28:25,538| ERR |  Thread-2/1570@transport |     raise ProxyCommandFailure(' '.join(self.cmd), e.strerror)
  </code></pre>