How to use Python SSHTunnle to forward multiple ports - python

I need to forward to multiple ports which are sits behind a server
server1(22) -> Server2(mysql, 3360) = local 3360
-> Server3(http, 8080) = local 8080
-> Server4(oracle,1234) = local 1234
I can only access Server2,3, and 4 via server1.
I am using Python ssltunnel package https://pypi.org/project/sshtunnel/
In example1&2, I can only specify one remote&local bind address.
Not sure how to connect multiple servers(2,3,4)
Example1
from sshtunnel import SSHTunnelForwarder
server = SSHTunnelForwarder(
'pahaz.urfuclub.ru',
ssh_username="pahaz",
ssh_password="secret",
remote_bind_address=('127.0.0.1', 8080)
)
server.start()
print(server.local_bind_port) # show assigned local port
# work with `SECRET SERVICE` through `server.local_bind_port`.
server.stop()
Example 2
import paramiko
import sshtunnel
with sshtunnel.open_tunnel(
(REMOTE_SERVER_IP, 443),
ssh_username="",
ssh_pkey="/var/ssh/rsa_key",
ssh_private_key_password="secret",
remote_bind_address=(PRIVATE_SERVER_IP, 22),
local_bind_address=('0.0.0.0', 10022)
) as tunnel:
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('127.0.0.1', 10022)
# do some operations with client session
client.close()
print('FINISH!')
I could use any other Python package that can do the job.

Both examples can be modified slightly to work the way you want.
There is the singular versions of bindings (local_bind_address & remote_bind_address) and the plural versions of bindings (local_bind_addresses & remote_bind_addresses.
The singular verisons expects a tuple containing variables for the connections, while the plural versions expects a list of one or more tuple(s).
Here is a modified version of your example 2:
import paramiko
import sshtunnel
tunnels = [("172.16.0.1", 80),
("172.16.0.2", 22)]
localPorts = [("127.0.0.1", 1180),
("127.0.0.1", 10022)]
with sshtunnel.open_tunnel(
(REMOTE_SERVER_IP, 22),
ssh_username="",
ssh_pkey="/var/ssh/rsa_key",
ssh_private_key_password="secret",
remote_bind_addresses=tunnels,
local_bind_addresses=localPorts
) as tunnel:
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('127.0.0.1', 10022)
# do some operations with client session
client.close()
If the lengths of the lists are the same length, then the IP-addresses / ports will correspond with each other.
In my example above, the following is happening:
Connection: 172.16.0.1 Port: 80, Is tunneled via: 127.0.0.1 Port: 1180
Connection: 172.16.0.2 Port: 22, Is tunneled via: 127.0.0.1 Port:
10022

I am adding this solution for multiple hops on multiple ports:
I have this setup:
the goal is to access the database by calling my machine on port 33306. This isn't possible because only gateway2 is allowed to speak to the database. We cant access gateway2 because only gateway1 is allowed to speak to it.
the following is the corresponding ssh .config file:
Host gateway1
HostName gtw1_IP_address
User gtw1_user
IdentityFile "path_to_gtw1_ssh_key"
IdentitiesOnly True
Host gateway2
User gtw2_user
Hostname gtw2_IP_address
IdentityFile "path_to_gtw2_ssh_key"
IdentitiesOnly True
# mysql
LocalForward 127.0.0.1:33306 127.0.0.1:3306
ProxyCommand ssh -W %h:%p switch-cede
this is how I reproduce it in python:
from sqlalchemy import create_engine
import config
import pandas as pd
import sshtunnel
from paramiko import SSHClient
with sshtunnel.open_tunnel(
ssh_username='gtw1_user',
ssh_address_or_host=('gtw1_IP_address', 22),
remote_bind_addresses=[('gtw2_IP_address', 22), ('gtw2_IP_address', 33306)],
local_bind_addresses=[('0.0.0.0', 22), ('0.0.0.0', 33306)], #this line is optional
ssh_pkey=path_to_gtw1_ssh_key,
) as tunnel1: # tunnel1 is the tunnel between myMachine and gateway1 I believe
print(tunnel1.local_bind_ports)
with sshtunnel.open_tunnel(
ssh_address_or_host=('localhost', tunnel1.local_bind_ports[0]),
remote_bind_addresses=[('127.0.0.1', 22),('127.0.0.1', 3306)],
local_bind_addresses=[('0.0.0.0', 22), ('127.0.0.1', 33306)],
ssh_username='gtw2_user',
ssh_pkey=path_to_gtw2_ssh_key,
) as tunnel2: # tunnel2 is the tunnel between gtw1 and gtw2 I believe
print(tunnel2.local_bind_ports)
db = create_engine(
f"mysql+pymysql://{config.USER}:{config.PASSWORD}#{config.HOST}:{config.PORT}/{config.DATABASE}")
print(db)
query = "SELECT * FROM randomTable LIMIT 10;"
df = pd.read_sql(query, db)
print(df)
# note that config is a file holding the credentials to connect to the database

Related

Query MySQL database with Python using sshtunnel, with SSH ppk key file

I have been able to sshtunnel into a remote database via the following code utilizing passwords
import sshtunnel
import mysql.connector
import numpy as np
import pandas as pd
host =""
username=""
password=""
tunnel_username=""
tunnel_password=""
with sshtunnel.SSHTunnelForwarder(
(host, 22),
ssh_username=username,
ssh_password=password,
remote_bind_address=('localhost', 3306),
local_bind_address=('0.0.0.0', 3306)
) as tunnel:
connection = mysql.connector.connect(
user= tunnel_username,
password= tunnel_password,
host='localhost',
database= database,
port=3306)
data = pd.read_sql_query(query, connection)
connection.close()
print(data)
However, circumstances have changed, and I have been forced to only connect via SSH keys (generated with PuTTYgen). With that being said, I have the private key (ppk file), but it is unclear what I need to do (or if possible) to get the following code to work again.
I have not seen an option to reference the ppk file path instead of the sshtunnel password.
Use ssh_pkey parameter of SSHTunnelForwarder constructor to provide the path to your private key file.
And you will need to convert your private key file to the OpenSSH format, as Paramiko (used by sshtunnel) does not support PuTTY key format.
See How to ssh connect through python Paramiko with ppk public key

Ssh tunnel hanging infinitely when trying to connect to a Phpmyadmin MySQL database through Python

I have a web app running on AWS Ubuntu and I am unable to connect to a database via ssh tunneling. I am able to access the db via MySQL workbench's SSH tunneling option but access via SSH tunneling on Python fails to work. Kindly suggest ways to solve it or of accessing a Phpmyadmin MySQL database other than via SSH tunneling.
from os.path import expanduser
pkeyfilepath = '/html/fbads/phpmyadmin-ec2.pem'
home = expanduser('~')
print(pkeyfilepath)
print("hello")
print(home)
mypkey = paramiko.RSAKey.from_private_key_file(home + pkeyfilepath)
print(mypkey)
server = SSHTunnelForwarder(
('ec2-52-91-169-11.compute-1.amazonaws.com', 22),
ssh_username="ec2-user",
ssh_pkey="~/html/fbads/phpmyadmin-ec2.pem",
remote_bind_address=('phpmyadmin.cpsisadn02rn.us-east-1.rds.amazonaws.com', 3306)
)
server.start()
print(server)
local_port = str(server.local_bind_port)
engine = create_engine('mysql://{}:{}#{}:{}/{}'.format("root", "passwd", "127.0.0.1", local_port, "passwd"))
dataDF = pd.read_sql("SELECT * FROM test;", engine) #testing teh connection
#server.stop() #use this command to stop the connection

python sshtunnel ssh to proxy

I want create proxy/socks service from my ssh linux server. I already created port forwarder with sshtunnel for mysql, example code:
from sshtunnel import SSHTunnelForwarder
from sqlalchemy import create_engine
from sqlalchemy.engine.url import URL
with SSHTunnelForwarder(('mydomain.com', 22), ssh_username='root', ssh_password='password', local_bind_address=('127.0.0.1', 3306), remote_bind_address=('127.0.0.1', 3306)) as server:
myDB = URL(drivername='mysql+pymysql', host='127.0.0.1', database='test_db', username='root', password='THawr_tapH3f', port=3306)
engine = create_engine(name_or_url=myDB)
connection = engine.connect()
connection.close()
I want to transform my ssh linux machine in proxy/socks4/5 service with python; how can I do this?

python mysql connectivity via ssh

I connect to my db manually using these steps:
1>load a putty session with ip 1.1.1.1 and port 1111
2>login as: login1
3>login1#1.1.1.1's password: pwd1
4>[login1#G ~]$ ssh login1#2.2.2.2
5>[login1#l ~]$ MySQL -h 3.3.3.3 -u login2 -p'pwd2' -D mydb
Now I can can query anything successfully like select * from my_table.
I have a python selenium webdriver code from where I want to read my db. I am not able to achieve it because of ssh tunnelling and 3 IP's involved.
from sshtunnel import SSHTunnelForwarder
import MySQLdb
with SSHTunnelForwarder(
('host', 1111),
ssh_password="pwd1
ssh_username="login1",
remote_bind_address=('2.2.2.2', 1111)) as server:
con = None
con = MySQLdb.connect(user='login2',passwd='pwd2',db='mydb',host=3.3.3.3,port=3306)
cur = con.cursor()
I am getting this error:
BaseSSHTunnelForwarderError: Could not resolve IP address for %s, aborting!
When instantiating SSHTunnelForwarder:
The param host is your remote ssh 2.2.2.2.
Argument for remote_bind_address will be for the tunnel, the port is the one you want to tunnel, so your mysql port. ('3.3.3.3', 3306)
Then when connecting to mysql, you want it to pass though the tunnel to access the mysql instance. The tunnel port is server.local_bind_port.
from sshtunnel import SSHTunnelForwarder
import MySQLdb
with SSHTunnelForwarder(
('2.2.2.2', 1111),
ssh_password="pwd1"
ssh_username="login1",
remote_bind_address=('3.3.3.3', 3306)) as server:
con = MySQLdb.connect(
user='login2',passwd='pwd2',
db='mydb',host=1.1.1.1,
port=server.local_bind_port)

ssh first with mysqldb in python

I'm trying to connect to a MySQL database on a remote server using MySQLdb in python. The problem is that first I need to SSH into the host, and then from there, I need to connect to the MySQL server. The problem I'm having, though, is that MySQLdb does not seem to have a way of establishing an SSH connection before connecting to the SQL server. I've checked the documentation but have not had any luck.
This is how I'm connecting:
conn = MySQLdb.connect(host = 'mysqlhost.domain.com:3306', user = 'user', passwd = 'password', db = 'dbname')
But what I really need is something like this:
conn = MySQLdb.connect(sshhost = 'sshhost.domain.com', sshuser = 'sshusername', sshpasswd = 'sshpasswd', host = 'mysqlhost.domain.com:3306', user = 'user', passwd = 'password', db = 'dbname')
Which is of course just made up. Can anyone make any recommendations?
I prefer keeping the tunnel within the python code, I did hate to create tunnels manually, or separately, thanks to sshtunnel library its very simple to use.
Here is some simple sample that will work for what you want.
import MySQLdb
from sshtunnel import SSHTunnelForwarder
with SSHTunnelForwarder(
('sshhost.domain.com', 22),
ssh_password="sshpasswd",
ssh_username="sshusername",
remote_bind_address=('mysqlhost.domain.com', 3306)) as server:
conn = MySQLdb.connect(host='127.0.0.1',
port=server.local_bind_port,
user='user',
passwd='password',
db='dbname')
Setup an ssh tunnel before you use MySQLdb.connect. The tunnel will make it appear as though you have the mysql running locally, set it up something like this
ssh user#host.com -L 9990:localhost:3306
here your local port 9990 will bind to 3306 on the remote host, -L stands for local, then 9990:localhost:3306 means LOCALPORT:
conn = MySQLdb.connect(host = 'mysqlhost.domain.com:9990', user = 'user', passwd = 'password', db = 'dbname')
notice the 9990.
Add your public ssh key of user to the host.com so that you dont have to type the password each time you want to setup the tunnel (use public key authentication).
If you need to do this within python there is python-to-ssh binding libraries you could call from within python to setup the tunnel for you.

Categories

Resources