Forums

Can't connect using Paramiko, can via sshtunnel

My code was using sshtunnel and connected to my database correctly. However due to a conflict between grequests and sshtunnel I had to switch to using another library for the ssh, and have had most luck with Paramiko (as that works withmonkeypatch on grequests).

The ssh tunnel section is working correctly, however I am now getting the message:

An error occurred: 1045 (28000): Access denied for user 'suspiciousleaf'@'localhost' (using password: YES)

I'm using environment variables that haven't changed, as far as I can see everything is the same between the working code with sshtunnel and without grequests, and the version with Paramiko that can't connect.

Full code here:

from gevent import monkey

monkey.patch_all()

import grequests
import mysql.connector
import paramiko
from pprint import pprint
import os

ssh_password = os.environ.get("PA_SSH_PASS")
db_password = os.environ.get("PA_DB_PASS")
database = os.environ.get("PA_DB_NAME")

def get_current_listing_urls():
    try:
        ssh_client = paramiko.SSHClient()
        ssh_client.load_system_host_keys()
        ssh_client.set_missing_host_key_policy(paramiko.WarningPolicy)

        ssh_client.connect(
            "ssh.eu.pythonanywhere.com",
            username="suspiciousleaf",
            password=ssh_password,
        )

        transport = ssh_client.get_transport()

        channel = transport.open_channel(
            "direct-tcpip",
            dest_addr=("suspiciousleaf.mysql.eu.pythonanywhere-services.com", 3306),
            src_addr=("127.0.0.1", 3306),
        )

        db = mysql.connector.connect(
            user="suspiciousleaf",
            password=db_password,
            host="127.0.0.1",
            port=3306,
            database=database,
            use_pure=True,
        )

        table_name = "listings"

        cursor = db.cursor(dictionary=True)

        query = f"SELECT COUNT(*) FROM {table_name};"

        cursor.execute(query)

        results = cursor.fetchall()

        return results

    except Exception as e:
        print(f"An error occurred: {str(e)}")
    finally:
        if "cursor" in locals() and cursor:
            cursor.close()
        if "db" in locals() and db:
            db.close()
        if "ssh_client" in locals() and ssh_client:
            ssh_client.close()


old_listing_urls = get_current_listing_urls()

pprint(old_listing_urls)

I've tried just about everything I can think of, but I can't see where I'm going wrong. The message presumably means it's connecting and trying to authenticate, but failing. However the database name, username, and password are all correct as they are all working when using sshtunnel.

Any help would be much appreciated! Thanks

I'm not sure if the code above would work, but to be able to SSH into PythonAnywhere, you need to use a paid account -- did you try this code when your account was upgraded?

Hi Pafk,

Yes my account is premium and I can connect to the database fine using my same credentials with MySQL Workbench, and also using almost the same code but using sshtunnel, as below:

import mysql.connector
import sshtunnel
from pprint import pprint
import os

sshtunnel.SSH_TIMEOUT = 5.0
sshtunnel.TUNNEL_TIMEOUT = 5.0

ssh_password = os.environ.get("PA_SSH_PASS")
db_password = os.environ.get("PA_DB_PASS")
database = os.environ.get("PA_DB_NAME")


def get_current_listing_urls():
    try:
        with sshtunnel.SSHTunnelForwarder(
            ("ssh.eu.pythonanywhere.com"),
            ssh_username="suspiciousleaf",
            ssh_password=ssh_password,
            remote_bind_address=(
                "suspiciousleaf.mysql.eu.pythonanywhere-services.com",
                3306,
            ),
            threaded=False,
        ) as tunnel:
            db = mysql.connector.connect(
                user="suspiciousleaf",
                password=db_password,
                host="127.0.0.1",
                port=tunnel.local_bind_port,
                database=database,
                use_pure=True,
            )

            table_name = "listings"

            cursor = db.cursor(dictionary=True)

            query = f"SELECT COUNT(*)  FROM {table_name};"

            cursor.execute(query)

            results = cursor.fetchall()

            return results

    except Exception as e:
        print(f"An error occurred: {str(e)}")
    finally:
        if "cursor" in locals() and cursor:
            cursor.close()
        if "db" in locals() and db:
            db.close()
        if "tunnel" in locals() and tunnel:
            tunnel.close()


old_listing_urls = get_current_listing_urls()

pprint(old_listing_urls)

The above works perfectly. However I need to import grequests when I integrate it into my main code, and if I run the above with the added line "import grequests" then I get an Error reading SSH protocol banner cannot switch to a different thread error. This is a conflict between sshtunnel and grequests (based on gevent).
There is a fix:

from gevent import monkey
monkey.patch_all(thread=False, select=False)
import grequests

However that doesn't solve the conflict when using sshtunnel. It does solve the issue if I use Paramiko instead of sshtunnel, but I can't get Paramiko to connect. The ssh section is working, but I can't then connect to the database. Setting deliberately incorrect ssh password gives the error Authentication failed. Using the correct ssh credentials I get An error occurred: 1045 (28000): Access denied for user 'suspiciousleaf'@'localhost' (using password: YES). I have tried hard coding the username and password to ensure they are correct and that still gives the same error message.

Your account suspiciousleaf has not been a paid one since Sep 18, so SSH connections would not work for a week now. Regarding the paramiko part -- I tried to play with your code a bit, but I was also getting errors. Interestingly, sshtunnel is just a wrapper around paramiko and they reference this example https://github.com/paramiko/paramiko/blob/main/demos/forward.py, so maybe you should try to work with that?

I'm confused - my account was migrated from US to EU on the 18th Sept. When I go to my account page I see 1GB storage capacity, and 2,000s CPU time. Also I can still ssh in using sshtunnel, I just can't get the Paramiko only version to work.

Hi there, can I aid in the confusion; we mentioned in the email explaining the us to eu migration that we're currently not able to migrate over your billing to the eu cluster. This is because we don't actually store your payment details. Therefore to continue with a paid account we ask that you sign up again in the regular way on the EU cluster. Now to ease the migration we allow your new user on the EU cluster to access paid features even before you've signed up again. This is why you can ssh

Note that you'll lose these paid features soon if you dont sign up again