Create app for ssh tunneling and opening web app

Hi all,

This is my very first message, so few words about me. I’m a Data Engineer and mostly do scripting in Python, but recently I’ve created few web apps in Flask in a docker container, also looking into Django a bit.

Since security policy requires working through ssh tunneling, I have created a document for the team about creating ssh key and setting up a tunnel. Though for internal usage it is ok, our external users are less technically advanced and we would need to simplify the process.

This is how I create a tunnel:

ssh u1@example.com -D9521 -C -N

socks5 browser settings:

127.0.0.1:9521

url to access the application in browser:

http://172.1.0.1:81

Now I’m trying to write an app that creates an ssh tunnel and opens the app automatically. I’d need someone experienced with ssh tunneling to look into it and advise where is the issue.

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.1.0.1: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.1.0.1', 81), ('172.1.0.1', 81))

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

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

For now the app opens the window, but after some time of waiting it shows error

172.1.0.1 took too long to respond.

Please advise.

I’ve moved this topic to Python Help. The Core Development category is for discussions on developing Python itself.

That is a public IP address that is for 172-1-0-1.lightspeed.hstntx.sbcglobal.net.
Did you mean to use a private IP address?

I would suggest debugging this with a simple command line script before running in the PyQt GUI.

In your example ssh command you use -D9521 but I do not see that port in the python version.

@barry-scott
You are right, the private IP address should be 172.16.0.4

The console command without 9521 socks5 port would be:

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

In this case browser url would be http://127.0.0.1:3081/

Is there any way to do this in Python code?

I am not a user of the ssh library you are using.
Have you read it docs to see how to tunnel?

You could always start ssh in a subprocess and kill the ssh on e you are done.

Console version works fine this way:

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)
Login

Login

...

but GUI version somehow returns error:

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.

Any idea why??..

reposted: Python app for ssh tunneling and opening web app

This is outside of the with tunnel so the tunnel is closed before the gui starts.

@barry-scott Thanks man! I wasted several hours on this… Have a great day.

Please review my repository
It works perfectly fine for my needs and I’m totally happy with it, though for public usage I would need your opinion…