Home > Back-end >  Install ssl certificates for discord.py in an app
Install ssl certificates for discord.py in an app

Time:03-21

I am making a python-based mac app that uses discord.py to do stuff with discord. As I knew from previous experience making discord bots, running discord bots requires that you run Install Certificates.command in your version of python. However, if another users uses this app, I don't want to require them to install python. I took a snippet of code from Install Certificates.command, thinking it would put the certificate in the right place on a user's computer. However, a tester got this error running the app on their computer:

Traceback (most recent call last):
  File "Interface.py", line 136, in <module>
  File "installCerts.py", line 25, in installCerts
FileNotFoundError: [Errno 2] No such file or directory: '/Library/Frameworks/Python.framework/Versions/3.8/etc/openssl'
[2514] Failed to execute script 'Interface' due to unhandled exception: [Errno 2] No such file or directory: '/Library/Frameworks/Python.framework/Versions/3.8/etc/openssl'
[2514] Traceback:
Traceback (most recent call last):
  File "Interface.py", line 136, in <module>
  File "installCerts.py", line 25, in installCerts
FileNotFoundError: [Errno 2] No such file or directory: '/Library/Frameworks/Python.framework/Versions/3.8/etc/openssl'

It's pretty clear what this error is saying: They don't have python (3.8) installed, so it can't put the ssl certificates anywhere (this is because the app is running in a python 3.8 environment).

By the way, the path mentioned in the error is the directory name of the path given by ssl.get_default_verify_paths().openssl_cafile.

I'm not super well-versed in the finer points of web connections and stuff like that, so I don't know the exact role of these certificates. Here's my question:

Is it possible to get this to work without the user installing python on their computer?

I.e. Can I add the ssl certificates to the app's local python version (as far as I can tell, in my app, python is simply a large bundled exec file)? Is there somewhere deep in the file system where I can put the certificates to let the connection to discord happen? . Pretty much any solution would be appreciated.

Additional Info:

My Code to Install Certificates:

STAT_0o775 = (stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
                  | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP
                  | stat.S_IROTH | stat.S_IXOTH)

    openssl_dir, openssl_cafile = os.path.split(
        ssl.get_default_verify_paths().openssl_cafile)
    os.chdir(openssl_dir) #Error happens here
    relpath_to_certifi_cafile = os.path.relpath(certifi.where())
    print(" -- removing any existing file or link")
    try:
        os.remove(openssl_cafile)
    except FileNotFoundError:
        pass
    print(" -- creating symlink to certifi certificate bundle")
    os.symlink(relpath_to_certifi_cafile, openssl_cafile)
    print(" -- setting permissions")
    os.chmod(openssl_cafile, STAT_0o775)
    print(" -- update complete")

The error that discord.py throws when the user doesn't have correct certificates installed:

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/aiohttp/connector.py", line 969, in _wrap_create_connection
    return await self._loop.create_connection(*args, **kwargs)  # type: ignore  # noqa
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py", line 1050, in create_connection
    transport, protocol = await self._create_connection_transport(
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py", line 1080, in _create_connection_transport
    await waiter
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/sslproto.py", line 529, in data_received
    ssldata, appdata = self._sslpipe.feed_ssldata(data)
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/sslproto.py", line 189, in feed_ssldata
    self._sslobj.do_handshake()
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/ssl.py", line 944, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1125)

If you need more info, let me know.

CodePudding user response:

Ok. This was very tough, but I got to an answer after much research. ssl in python is basically just a set of bindings for openSSL. When you do import ssl, it builds an openSSL environment (I don't think I'm using the exact right words here). As you could see, it was defaulting to the openSSL folder in Python because from python's perspective, that is where openSSL keeps its certs. Turns out, ssl.DefaultVerifyPaths objects have other attributes, namely cafile. This was how I made the path to the cert whatever I wanted. You see, when openSSL builds, it looks for an environment variable SSL_CERT_FILE. As long as I set that variable with os.environ before I imported ssl, it would work, because ssl would find the certificate. I simplified installCerts down to the following:

import os
import stat
import certifi

def installCerts():
    os.environ['SSL_CERT_FILE'] = certifi.where()
    import ssl
    # ssl build needs to happen after enviro var is set
    STAT_0o775 = (stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
                  | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP
                  | stat.S_IROTH | stat.S_IXOTH)
    cafile = ssl.get_default_verify_paths().cafile
    os.chmod(cafile, STAT_0o775)

And it seems to work fine on other people's computers now without them needing to install python.

This question helped me:

How to change the 'cafile' argument in the ssl module in Python3?

  • Related