Как открыть SSH-туннель с помощью python? - PullRequest
4 голосов
/ 06 декабря 2010

Я пытаюсь подключиться к удаленной базе данных mysql, используя django.
В документации указано, что сначала необходимо открыть туннель SSH для подключения к базе данных.
Существует ли библиотека Python, которая может открывать туннель SSH при определенных настройках?

Ответы [ 4 ]

7 голосов
/ 06 декабря 2010

Вы можете попробовать paramiko * forward . Для обзора paramiko см. здесь .

5 голосов
/ 30 октября 2014

Вот фрагмент кода для Python3 (но вы должны иметь возможность без проблем установить его в Python2). Он запускает SSH-туннель в отдельном потоке; затем основной поток что-то делает для получения сетевого трафика через туннель SSH.

В этом примере туннель ssh перенаправляет локальный порт 2222 на порт 80 на локальном хосте. Основным видом деятельности является бег

curl http://localhost:2222

т. Е. Получение веб-страницы, но из порта 2222.

Класс SshTunnel инициализируется с 4 параметрами: локальный и удаленный порт, удаленный пользователь и удаленный хост. Все, что он делает, это запускает SSH следующим образом:

ssh -N -L localport:remotehost:remoteport remoteuser@remotehost

Чтобы это работало, вам потребуется логин без пароля для remoteuser @ remotehost (через ~ / .ssh / id_rsa.pub, который известен на удаленном сервере). Работающий таким образом туннель ssh находится в одном потоке; главная задача должна быть в другом. Туннельный поток ssh помечен как демон, поэтому он автоматически останавливается после завершения основной активности.

Я не включил полный пример подключения MySQL, потому что он не требует пояснений. Как только SshTunnel настроит локальный порт TCP, вы можете подключиться к нему - будь то через клиент MySQL, curl или любой другой.

import subprocess
import time
import threading

class SshTunnel(threading.Thread):
    def __init__(self, localport, remoteport, remoteuser, remotehost):
        threading.Thread.__init__(self)
        self.localport = localport      # Local port to listen to
        self.remoteport = remoteport    # Remote port on remotehost
        self.remoteuser = remoteuser    # Remote user on remotehost
        self.remotehost = remotehost    # What host do we send traffic to
        self.daemon = True              # So that thread will exit when
                                        # main non-daemon thread finishes

    def run(self):
        if subprocess.call([
            'ssh', '-N',
                   '-L', str(self.localport) + ':' + self.remotehost + ':' + str(self.remoteport),
                   self.remoteuser + '@' + self.remotehost ]):
            raise Exception ('ssh tunnel setup failed')


if __name__ == '__main__':
    tunnel = SshTunnel(2222, 80, 'karel', 'localhost')
    tunnel.start()
    time.sleep(1)
    subprocess.call(['curl', 'http://localhost:2222'])
3 голосов
/ 17 июня 2015

Попробуйте использовать пакет sshtunnel .

Это просто:

pip install sshtunnel
python -m sshtunnel -U vagrant -P vagrant -L :3306 -R 127.0.0.1:3306 -p 2222 localhost

Раскрытие информации: я автор и сопровождающий этого пакета.

2 голосов
/ 07 апреля 2016

Вот небольшой класс, который вы можете добавить в свой код:

import subprocess
import random
import tempfile

class SSHTunnel:

    def __init__(self, host, user, port, key, remote_port):
        self.host = host
        self.user = user
        self.port = port
        self.key = key
        self.remote_port = remote_port
        # Get a temporary file name
        tmpfile = tempfile.NamedTemporaryFile()
        tmpfile.close()
        self.socket = tmpfile.name
        self.local_port = random.randint(10000, 65535)
        self.local_host = '127.0.0.1'
        self.open = False

    def start(self):
        exit_status = subprocess.call(['ssh', '-MfN',
            '-S', self.socket,
            '-i', self.key,
            '-p', self.port,
            '-l', self.user,
            '-L', '{}:{}:{}'.format(self.local_port, self.local_host, self.remote_port),
            '-o', 'ExitOnForwardFailure=True',
            self.host
        ])
        if exit_status != 0:
            raise Exception('SSH tunnel failed with status: {}'.format(exit_status))
        if self.send_control_command('check') != 0:
            raise Exception('SSH tunnel failed to check')
        self.open = True

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

    def send_control_command(self, cmd):
        return subprocess.check_call(['ssh', '-S', self.socket, '-O', cmd, '-l', self.user, self.host])

    def __enter__(self):
        self.start()
        return self

    def __exit__(self, type, value, traceback):
        self.stop()

А вот как вы можете использовать его, например, с MySQL (обычно порт 3306):

with SSHTunnel('database.server.com', 'you', '22', '/path/to/private_key', '3306') as tunnel:
    print "Connected on port {} at {}".format(tunnel.local_port, tunnel.local_host)
...