0

Hello I'm working on simple python ssh tunnel scrpit but I allways receive Could not resolve hostname error, but it works if run it manually. this is my code:

#!/usr/bin/env python

import subprocess
import time
import tempfile

class TunnelSSH():
    def __init__(self, ssh_user: str, ssh_password: str, ssh_host: str, ssh_port: int,
                local_tunnel_port:int, remote_tunnel_host:str, remote_tunnel_port:int):
        self.ssh_user = ssh_user
        self.ssh_password = ssh_password
        self.ssh_host = ssh_host
        self.ssh_port = ssh_port
        self.local_tunnel_port = local_tunnel_port
        self.remote_tunnel_port = remote_tunnel_port
        self.remote_tunnel_host = remote_tunnel_host

        _socket_file = tempfile.NamedTemporaryFile()
        _socket_file.close()
        self.socket = _socket_file.name
        self.connected = False


    def start(self):
        ssh_conection = ['ssh', '-CN',
                        f'"{self.ssh_user}:{self.ssh_password}"@{self.ssh_host} -p {self.ssh_port}',
                        f'-L {self.local_tunnel_port}:{self.remote_tunnel_host}:{self.remote_tunnel_port}',
                        f'-S {self.socket}',
                        '-o ExitOnForwardFailure=True'
                    ]
        if not self.connected:
            status = subprocess.call(ssh_conection)
            self._check_connection(status)
            time.sleep(self.retry_sleep)
        else:
            raise Exception('Tunnel is open')

    def stop(self):
        if self.connected:
            if self._send_control_command('exit') != 0:
                raise Exception('SSH tunnel failed to exit')
            self.connected = False

    def _check_connection(self, status) -> None:
        """Check connection status and set connected to True is tunnel is open"""
        if status != 0:
            raise Exception(f'SSH tunnel failed status: {status}')
        if self._send_control_command('check'):
            raise Exception(f'SSH tunnel failed to check')
        self.connected = True

    def _send_control_command(self, ctl_cmd:str):
        call = ['ssh',f'-S {self.socket}',f'-O {self.ctl_cmd}', f'-l {self.ssh_user}', f'{self.ssh_host}']
        return subprocess.check_call(call)


if __name__ == "__main__":
    tunnel = TunnelSSH(ssh_user='...',
                        ssh_password='...',
                        ssh_host='...',
                        ssh_port=...,
                        local_tunnel_port=...,
                        remote_tunnel_host='...',
                        remote_tunnel_port=...
                    )

    retry = 10 # times
    wait_for_retry = 5 #s

    for i in range(retry):
        print(f'Connection attempt: {i}')
        try:
            tunnel.start()
        except Exception as err:
            tunnel.stop()
            print(err)
            time.sleep(wait_for_retry)

    print(f'Connected: {tunnel.connected}')
5
  • Are you by chance using an ssh alias as a host? Output of host <ssh_host> would be very helpful Commented Apr 23, 2020 at 14:45
  • @Marat I have also tried with the ssh server ip, Commented Apr 23, 2020 at 14:55
  • can you confirm even with an IP address the error is still "cannot resolve"? Commented Apr 23, 2020 at 17:17
  • @Marat, yes even when usign IP address I have the same ansswer Commented Apr 24, 2020 at 15:48
  • Try github.com/pahaz/sshtunnel Commented Nov 2, 2020 at 11:49

1 Answer 1

1

subprocess.call expects a list of arguments. When ssh_conection is formed, several arguments are slapped together, so e.g. this part gets quoted into a single argument:

'"{self.ssh_user}:{self.ssh_password}"@{self.ssh_host} -p {self.ssh_port}'

Fix: properly split the arguments:

...
    ssh_conection = ['ssh', '-CN',
                    f'{self.ssh_user}:{self.ssh_password}@{self.ssh_host}',  # will be quoted automatically
                    '-p', f'{self.ssh_port}',
                    '-L', f'{self.local_tunnel_port}:{self.remote_tunnel_host}:{self.remote_tunnel_port}',
                    '-S', f'{self.socket}',
                    '-o', 'ExitOnForwardFailure=True'
                ]
...

What hinted the problem: IP addresses are used directly. 'cannot be resolved' on an IP address says that it is interpreted as a symbolic name, which makes spotting this easier.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.