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_3
The 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,
HAS_TLSv1_3
This Boolean is compiled into the Python interpreter,- According to thePython documentationThe specification,
HAS_TLSv1_3
Is new in Python 3.6.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 library
import ssl
The statement of - Virtualenv was created without using the standard library
ssl.py
The files are copied over, so the Python 3.6.2 interpreter looks in the system directoryssl.py
File and execute - The Python 3.6.2 interpreter found the system directory
ssl.py
The Python 3.6.3 system library file. - The Python 3.6.2 interpreter executes the Python 3.6.3 library files
ssl.py
An 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
_ssl
In the importHAS_TLSv1_3
- This is not defined in the Python 3.6.2 interpreter
HAS_TLSv1_3
This 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.