We have a Django application running on aliyun’s ECS via UWSGi, which is run on Ubuntu 16.04, and the UWSGi is managed via the container container, where each application is run in a separate VirtualEnv. This is the general environment in which the software runs.

Because some software has not been upgraded, Ali Cloud has been warning of security vulnerabilities, so I want to upgrade the system on ECS. There is no problem with the upgrade process, after all, the aliyun maintains its own source, basically there will be no version dependency issues.

Error scenarios

Uwsgi reported an error when you restarted your Django application after the upgrade

from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN, HAS_TLSv1_3
ImportError: cannot import name 'HAS_TLSv1_3'Copy the code

Cause analysis,

Start with the exception to find the cause

The HAS_TLSv1_3 attribute does not exist in the _ssl module, so I looked it up in the Python documentation. It reads as follows:

Ssl.has_tlsv1_3 Whether the OpenSSL library has built-in support for the TLS 1.3 protocol. New in version 3.6.3.Copy the code

When I saw the words “New in version 3.6.3.” and checked the upgrade record of the system, I found that the Python of the host machine was upgraded from 3.6.2 to 3.6.3 in the latest upgrade. At this time, I vaguely thought that uWSGi started with an error because of this upgrade.

But I am upgrading Python on the host. How does this affect Python in Virtualenv? Take it easy, guys. Let’s keep digging.

HAS_TLSv1_3The definition of

The line that throws the exception is in line 118 of ssl.py in Python’s standard library. It reads as follows:

from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN, HAS_TLSv1_3Copy the code

As you can see, the exception was thrown because of an import exception when the Python interpreter imported HAS_TLSv1_3 from the _SSL module.

The _ssl module, written in C, is compiled into the Python interpreter in the Python source code file Modules/ _SSL.c.

In lines 5154-5206, we can see its definition for the _SSL module:

static struct PyModuleDef _sslmodule = { PyModuleDef_HEAD_INIT, "_ssl", module_doc, -1, PySSL_methods, NULL, NULL, NULL, NULL }; . PyMODINIT_FUNC PyInit__ssl(void) { PyObject *m, *d, *r; . // The _sslmodule is defined here, and the pointer m represents the _SSL module m = PyModule_Create(& _SSLModule); if (m == NULL) return NULL; .Copy the code

The _ssl module defines the HAS_TLSv1_3 attribute in lines 5642-5468:

#if defined(TLS1_3_VERSION) && ! defined(OPENSSL_NO_TLS1_3) r = Py_True; #else r = Py_False; #endif Py_INCREF(r); // Add the Boolean value HAS_TLSv1_3 PyModule_AddObject(m, "HAS_TLSv1_3", r) to the _SSL module;Copy the code

The above code determines whether the current system supports TLS 1.3 and adds a Boolean value HAS_TLSv1_3 to the _SSL module to indicate whether the current system supports TLS 1.3.

Repetition error

From the above analysis we can see that we can know three things,

  1. HAS_TLSv1_3This Boolean is compiled into the Python interpreter,
  2. According to thePython documentationThe specification,HAS_TLSv1_3Is new in Python 3.6.3.
  3. We upgraded the host Python from 3.6.2 to 3.6.3, but our Virtualenv still uses version 3.6.2 of the Python interpreter.

With these three things in mind, we can replay our error scenario and tease out the process of what went wrong:

  • Uwsgi starts with the Python interpreter version 3.6.2 in Virtualenv
  • The Python 3.6.2 interpreter executes in some third-party libraryimport sslThe statement of
  • Virtualenv was created without using the standard libraryssl.pyThe files are copied over, so the Python 3.6.2 interpreter looks in the system directoryssl.pyFile and execute
  • The Python 3.6.2 interpreter found the system directoryssl.pyThe Python 3.6.3 system library file.
  • The Python 3.6.2 interpreter executes the Python 3.6.3 library filesssl.pyAn import statement in:
from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN, HAS_TLSv1_3Copy the code
  • The Python 3.6.2 interpreter is a module built into the interpreter_sslIn the importHAS_TLSv1_3
  • This is not defined in the Python 3.6.2 interpreterHAS_TLSv1_3This Boolean is thrown by the programImportError

The above error flow can also be summarized by the following diagram:

Error summary

Virtualenv does not copy ssl.py because I did not specify its — always-copy property. Virtualenv does not copy ssl.py because I did not specify its — always-copy property.

I asked a big shot in the company about this problem. Virtualenv is designed to isolate Python third-party libraries, not Python versions.

So if we need to install multiple Versions of Python on the same system in the future, we will need to use a tool like Pyenv and not count on Virutalenv’s -Python option.