20

I've recently changed my project to use SQLAlchemy and my project runs fine, it used an external MySQL server.

Now I'm trying to work with a different MySQL server with SSL CA, and it doesn't connect.

(It did connect using MySQL Workbench, so the certificate should be fine)

I'm using the following code:

ssl_args = {'ssl': {'ca': ca_path}}
engine = create_engine("mysql+pymysql://<user>:<pass>@<addr>/<schema>",
                        connect_args=ssl_args)

and I get the following error:

Can't connect to MySQL server on '\addr\' ([WinError 10054] An existing connection was forcibly closed by the remote host)

Any suggestions?

Shahaf Finder
  • 604
  • 1
  • 4
  • 11

4 Answers4

21

I changed the DBAPI to MySQL-Connector, and used the following code:

ssl_args = {'ssl_ca': ca_path}
engine = create_engine("mysql+mysqlconnector://<user>:<pass>@<addr>/<schema>",
                        connect_args=ssl_args)

And now it works.

Shahaf Finder
  • 604
  • 1
  • 4
  • 11
5

If you just connect from a client machine with an ssl connection (so you don't have access to the cert and key), you could simple add ssl=true to your uri.

Edit:

For example: mysql_db = "mysql+mysqlconnector://<user>:<pass>@<addr>/<schema>?ssl=true"

Chris S
  • 422
  • 1
  • 4
  • 13
Ben
  • 696
  • 9
  • 19
  • For Postgres, try `sslmode=require` if you receive `sqlalchemy.exc.ProgrammingError: (psycopg.ProgrammingError) invalid connection option "ssl"` – danialk Jun 15 '23 at 10:07
3

The official doc is well documented:

engine = create_engine(
    db_url,
    connect_args={
        "ssl": {
            "ssl_ca": "ca.pem",
            "ssl_cert": "client-cert.pem",
            "ssl_key": "client-key.pem"
        }
    }
)
  • sqlalchemy v.1.4.45 (late 2022) apparently has a bug in docs: keys in subdictionary should not have "ssl_" prefix. See https://stackoverflow.com/a/14992181/65736 which is still True. – bialix Dec 27 '22 at 20:31
0

Another solution is to use sqlalchemy.engine.url.URL to define the URL and pass it to create_engine.

sqlUrl = sqlalchemy.engine.url.URL(
    drivername="mysql+pymysql",
    username=db_user,
    password=db_pass,
    host=db_host,
    port=3306,
    database=db_name,
    query={"ssl_ca": "main_app/certs/BaltimoreCyberTrustRoot.crt.pem"},
)
create_engine(sqlUrl)

You can include SSL parameters as a dictionary in the query argument.

This approach is useful if you are using Flask to initialize the SqlAlchemy engine with a config parameter like SQLALCHEMY_DATABASE_URI rather than directly using create_engine.

STEM FabLab
  • 380
  • 3
  • 13