1

Тема: створення ssh тунелю і відкривання url

Поки що забиваю тему.

команда в консолі для створення ssh тунелю:

ssh -L 3081:172.16.0.4:81 user1@example.com -C -N

url:
http://127.0.0.1:3081/login

Шукаю як це зробити в Python.

2 Востаннє редагувалося frz (15.11.2023 09:41:20)

Re: створення ssh тунелю і відкривання url

Тепер нащо це потрібно...
Оскільки відповідно до секуріті полісі сервіс повинен бути дотупним лише через ssh, то я створив доку для тіми як конектитися через ssh тунель, все працює ок.
Для зовнішніх користувачів це складно, бо вони технічно далекі. Тому це має бути апка яку просто запустять і вона всередині запустить ssh тунель і одразу ж відкриє веб апку.

import sys
import paramiko
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout
from PyQt5.QtWebEngineWidgets import QWebEngineView

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("cool app")
        self.setGeometry(150, 150, 900, 700)

        # Create a web browser widget
        self.browser = QWebEngineView()
        self.browser.setUrl(QUrl("http://172.16.0.4:81"))

        # Create a layout for the main window
        layout = QVBoxLayout()
        layout.addWidget(self.browser)

        # Create a central widget and set the layout
        central_widget = QWidget()
        central_widget.setLayout(layout)
        self.setCentralWidget(central_widget)

        # Create an SSH tunnel to the Azure Virtual Machine
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh_key = paramiko.RSAKey.from_private_key_file('/home/user1/.ssh/id_rsa')
        ssh.connect('example.com', username='u1', pkey=ssh_key, port=22, timeout=100)
        ssh_transport = ssh.get_transport()
        ssh_channel = ssh_transport.open_channel('direct-tcpip', ('172.16.0.4', 81), ('172.16.0.4', 81))

        # Set the web browser URL to use the SSH tunnel
        self.browser.setUrl(QUrl("http://172.16.0.4:81"))

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

Поки що вікно апки відкривається і після деякого очікування показує повідомлення

172.16.0.4 took too long to respond

Буду думати далі.

3

Re: створення ssh тунелю і відкривання url

При спробі дебажити за допомогою pdp

import pdb

...

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        pdb.set_trace()
...

отримую помилку:

Segmentation fault (core dumped)

За допомогою нехитрих принтів з'ясував, що падає на pdb.set_trace()

print("ok0")
import pdb
print("ok1")

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        print("ok2")
        pdb.set_trace()
        print("ok3")

ok0
ok1
ok2
Segmentation fault (core dumped)

Стаковерфлоу підказує, що може закінчилася пам'ять

https://stackoverflow.com/a/31048386

You might be working with a lot of data and your RAM is full

Однак в моєму випадку це не так

$ free -h
              total        used        free      shared  buff/cache   available
Mem:           15Gi       9,8Gi       622Mi       1,3Gi       5,0Gi       4,0Gi
Swap:          15Gi       7,5Gi       8,3Gi

Поки що незрозуміло як рухатися далі.

4

Re: створення ssh тунелю і відкривання url

Переписав код без PyQt5 GUI щоб віддебажити в консолі, тепер pdb почав працювати, однак результати поки не дуже втішні.

(1)

self.ssh_channel = self.ssh_transport.open_channel('direct-tcpip', ('172.16.0.4', 81), ('127.0.0.1', 3084))

content = connection.get_url("http://127.0.0.1:3084")
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

requests.exceptions.ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=3084): Max retries exceeded with url: / (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7fc6030fd8b0>: Failed to establish a new connection: [Errno 111] Connection refused'))

(2)

self.ssh_channel = self.ssh_transport.open_channel('direct-tcpip', ('127.0.0.1', 3084), ('172.16.0.4', 81))

self.ssh_channel = self.ssh_transport.open_channel('direct-tcpip', ('127.0.0.1', 3084), ('172.16.0.4', 81))
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

paramiko.ssh_exception.ChannelException: ChannelException(2, 'Connect failed')

Нічо неясно.

5

Re: створення ssh тунелю і відкривання url

Ось так все працює ок:

from sshtunnel import SSHTunnelForwarder
import requests

ssh_host = 'example.com'
ssh_user = 'user1'
local_port = 3081
remote_host = '172.16.0.4'
remote_port = 81
ssh_private_key_path = "/path/to/private/key"

with SSHTunnelForwarder(
    (ssh_host, 22),
    ssh_username=ssh_user,
    ssh_private_key=ssh_private_key_path,
    remote_bind_address=(remote_host, remote_port),
    local_bind_address=('127.0.0.1', local_port)
) as tunnel:
    response = requests.get(f'http://127.0.0.1:{local_port}/login')
    print(response.text)

6

Re: створення ssh тунелю і відкривання url

Версія GUI чомусь не працює

from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import QUrl
from sshtunnel import SSHTunnelForwarder
import sys

ssh_host = 'example.com'
ssh_user = 'user1'
local_port = 3081
remote_host = '172.16.0.4'
remote_port = 81
ssh_private_key_path = "/path/to/private/key"

class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.browser = QWebEngineView()
        self.setCentralWidget(self.browser)

    def set_tunnel(self, tunnel):
        self.tunnel = tunnel

    def load_url(self):
        self.browser.setUrl(QUrl(f'http://127.0.0.1:{local_port}/login'))

app = QApplication(sys.argv)

with SSHTunnelForwarder(
    (ssh_host, 22),
    ssh_username=ssh_user,
    ssh_private_key=ssh_private_key_path,
    remote_bind_address=(remote_host, remote_port),
    local_bind_address=('127.0.0.1', local_port)
) as tunnel:
    window = MainWindow()
    window.set_tunnel(tunnel)
    window.load_url()
    window.show()

sys.exit(app.exec_())

127.0.0.1 refused to connect.

7

Re: створення ssh тунелю і відкривання url

sys.exit(app.exec_())

Це було за межами with, тому тунель було закрито до запуску GUI.
Нарешті працює нормально...

8

Re: створення ssh тунелю і відкривання url

Прохання зробити рев'ю:
https://github.com/marchelloUA/ssh_tunnel_wrapper
Апка дуже проста, мені працює для моїх потреб ідеально і в мене до себе нема ніяких питань. Однак я це планую включити в своє резюме і тому потребую конструктивної критики.

9

Re: створення ssh тунелю і відкривання url

додав keepalive щоб впродовж 96 годин не закривалося з'єднання, навіть якщо не було ніякої активності

...
) as tunnel:
    tunnel._transport.set_keepalive(96*60*60) # 96 hours
...