[macOS] SysCallError: (32, 'EPIPE') when the server requires certs but the client does not present one
Opened this issue · 4 comments
In pymongo we have various tests to ensure that TLS is configured correctly. One such test is when the server is configured to require a client cert but the client does not present one. In this test we expect the client to see an error with "certificate required", "SSL handshake failed", "Connection reset by peer", or one of the equivalent errnos (like ECONNRESET) but we're actually seeing pyopenssl raise (32, 'EPIPE') on macOS (in https://jira.mongodb.org/browse/PYTHON-3607). My understanding is that EPIPE indicates a bug in openssl/pyopenssl, what do you think?
Here's more info about the environment:
Collecting cryptography>=2
Using cached cryptography-39.0.1-cp36-abi3-macosx_10_12_x86_64.whl (2.9 MB)
....
Collecting dnspython<3.0.0,>=1.16.0
Using cached dnspython-2.3.0-py3-none-any.whl (283 kB)
Collecting pymongo-auth-aws<2.0.0
Using cached pymongo_auth_aws-1.1.0-py2.py3-none-any.whl (11 kB)
Collecting pyopenssl>=17.2.0
Using cached pyOpenSSL-23.0.0-py3-none-any.whl (57 kB)
Requirement already satisfied: requests<3.0.0 in ./venv-encryption/lib/python3.9/site-packages (from pymongo==4.4.0.dev1) (2.28.2)
Collecting service_identity>=18.1.0
Using cached service_identity-21.1.0-py2.py3-none-any.whl (12 kB)
Requirement already satisfied: certifi in ./venv-encryption/lib/python3.9/site-packages (from pymongo==4.4.0.dev1) (2022.12.7)
Requirement already satisfied: boto3 in ./venv-encryption/lib/python3.9/site-packages (from pymongo-auth-aws<2.0.0->pymongo==4.4.0.dev1) (1.26.73)
Requirement already satisfied: botocore in ./venv-encryption/lib/python3.9/site-packages (from pymongo-auth-aws<2.0.0->pymongo==4.4.0.dev1) (1.29.73)
Requirement already satisfied: cryptography<40,>=38.0.0 in ./venv-encryption/lib/python3.9/site-packages (from pyopenssl>=17.2.0->pymongo==4.4.0.dev1) (39.0.1)
Requirement already satisfied: idna<4,>=2.5 in ./venv-encryption/lib/python3.9/site-packages (from requests<3.0.0->pymongo==4.4.0.dev1) (3.4)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in ./venv-encryption/lib/python3.9/site-packages (from requests<3.0.0->pymongo==4.4.0.dev1) (1.26.14)
Requirement already satisfied: charset-normalizer<4,>=2 in ./venv-encryption/lib/python3.9/site-packages (from requests<3.0.0->pymongo==4.4.0.dev1) (3.0.1)
Collecting pyasn1-modules
Using cached pyasn1_modules-0.2.8-py2.py3-none-any.whl (155 kB)
Collecting attrs>=19.1.0
Using cached attrs-22.2.0-py3-none-any.whl (60 kB)
Requirement already satisfied: six in ./venv-encryption/lib/python3.9/site-packages (from service_identity>=18.1.0->pymongo==4.4.0.dev1) (1.16.0)
Collecting pyasn1
Using cached pyasn1-0.4.8-py2.py3-none-any.whl (77 kB)
Requirement already satisfied: cffi>=1.12 in ./venv-encryption/lib/python3.9/site-packages (from cryptography<40,>=38.0.0->pyopenssl>=17.2.0->pymongo==4.4.0.dev1) (1.15.1)
Requirement already satisfied: jmespath<2.0.0,>=0.7.1 in ./venv-encryption/lib/python3.9/site-packages (from boto3->pymongo-auth-aws<2.0.0->pymongo==4.4.0.dev1) (1.0.1)
Requirement already satisfied: s3transfer<0.7.0,>=0.6.0 in ./venv-encryption/lib/python3.9/site-packages (from boto3->pymongo-auth-aws<2.0.0->pymongo==4.4.0.dev1) (0.6.0)
Requirement already satisfied: python-dateutil<3.0.0,>=2.1 in ./venv-encryption/lib/python3.9/site-packages (from botocore->pymongo-auth-aws<2.0.0->pymongo==4.4.0.dev1) (2.8.2)
Requirement already satisfied: pycparser in ./venv-encryption/lib/python3.9/site-packages (from cffi>=1.12->cryptography<40,>=38.0.0->pyopenssl>=17.2.0->pymongo==4.4.0.dev1) (2.21)
Building wheels for collected packages: pymongo
Building wheel for pymongo (setup.py): started
Building wheel for pymongo (setup.py): finished with status 'done'
Created wheel for pymongo: filename=pymongo-4.4.0.dev1-cp39-cp39-macosx_11_0_x86_64.whl size=385239 sha256=6907c24d0b03797d5cbe1245fb6ac1bc786e92b4e0a40c82c029112bc9ef858f
Stored in directory: /System/Volumes/Data/data/mci/4527dad69325df5546afe7b616b18d7d/drivers-tools/.evergreen/orchestration/db/pip-ephem-wheel-cache-ldh4fy0r/wheels/39/f3/d9/27a37bbf2df83660f5cbf8bae4a0c81495ac5a49e4975b8ea9
Successfully built pymongo
Installing collected packages: pyasn1, pyasn1-modules, dnspython, attrs, pymongo, service_identity, pyopenssl, pymongo-auth-aws
Successfully installed attrs-22.2.0 dnspython-2.3.0 pyasn1-0.4.8 pyasn1-modules-0.2.8 pymongo-4.4.0.dev1 pymongo-auth-aws-1.1.0 pyopenssl-23.0.0 service_identity-21.1.0
Python version:
3.9.10 (main, Jan 15 2022, 11:48:00)
[Clang 13.0.0 (clang-1300.0.29.3)]
macOS 11
No pipes, just standard tcp sockets. I actually just remembered that we're using pykmip in these tests and these errors might be caused by it's funky use of shutdown() which I'm trying to fix in: OpenKMIP/PyKMIP#682
It would be possible to confirm that theory by testing the "server requires certs but the client does not present one" case using only pyopenssl.
Update, I just tested two scenarios:
- A MongoDB server (running locally on macOS) configured to require client certs.
- A PyKMIP server configured to require client certs with and without the fix for OpenKMIP/PyKMIP#682.
In 1) I correctly see this error:
>>> Traceback (most recent call last):
File "/Users/shane/git/mongo-python-driver/pymongo/pool.py", line 1061, in _configured_socket
sock = ssl_context.wrap_socket(sock, server_hostname=host)
File "/Users/shane/git/mongo-python-driver/pymongo/pyopenssl_context.py", line 369, in wrap_socket
ssl_conn.do_handshake()
File "/Users/shane/git/mongo-python-driver/pymongo/pyopenssl_context.py", line 125, in do_handshake
return self._call(super(_sslConn, self).do_handshake, *args, **kwargs)
File "/Users/shane/git/mongo-python-driver/pymongo/pyopenssl_context.py", line 108, in _call
return call(*args, **kwargs)
File "/Users/shane/work/pycharm/pymongo-py310/lib/python3.10/site-packages/OpenSSL/SSL.py", line 2075, in do_handshake
self._raise_ssl_error(self._ssl, result)
File "/Users/shane/work/pycharm/pymongo-py310/lib/python3.10/site-packages/OpenSSL/SSL.py", line 1715, in _raise_ssl_error
_openssl_assert(
File "/Users/shane/work/pycharm/pymongo-py310/lib/python3.10/site-packages/OpenSSL/_util.py", line 71, in openssl_assert
exception_from_error_queue(error)
File "/Users/shane/work/pycharm/pymongo-py310/lib/python3.10/site-packages/OpenSSL/_util.py", line 57, in exception_from_error_queue
raise exception_type(errors)
OpenSSL.SSL.Error: [('STORE routines', '', 'unregistered scheme'), ('system library', '', ''), ('STORE routines', '', 'unregistered scheme'), ('system library', '', ''), ('SSL routines', '', 'certificate verify failed')]
In 2) I see EPIPE:
File "/Users/shane/git/mongo-python-driver/pymongo/encryption.py", line 726, in create_data_key
return self._encryption.create_data_key(
File "/Users/shane/work/pycharm/pymongo-py310/lib/python3.10/site-packages/pymongocrypt/explicit_encrypter.py", line 174, in create_data_key
key = run_state_machine(ctx, self.callback)
File "/Users/shane/work/pycharm/pymongo-py310/lib/python3.10/site-packages/pymongocrypt/state_machine.py", line 150, in run_state_machine
callback.kms_request(kms_ctx)
File "/Users/shane/git/mongo-python-driver/pymongo/encryption.py", line 143, in kms_request
conn.sendall(message)
File "/Users/shane/git/mongo-python-driver/pymongo/pyopenssl_context.py", line 151, in sendall
sent = self._call(super(_sslConn, self).send, view[total_sent:], flags)
File "/Users/shane/git/mongo-python-driver/pymongo/pyopenssl_context.py", line 108, in _call
return call(*args, **kwargs)
File "/Users/shane/work/pycharm/pymongo-py310/lib/python3.10/site-packages/OpenSSL/SSL.py", line 1899, in send
self._raise_ssl_error(self._ssl, result)
File "/Users/shane/work/pycharm/pymongo-py310/lib/python3.10/site-packages/OpenSSL/SSL.py", line 1699, in _raise_ssl_error
raise SysCallError(errno, errorcode.get(errno))
OpenSSL.SSL.SysCallError: (32, 'EPIPE')
Interestingly, from this trace I can see that the client thinks the TLS handshake completed successfully (do_handshake() completes without error) but then the connection raises EPIPE on the first send() of application data.
The kmip server is here https://github.com/mongodb-labs/drivers-evergreen-tools/blob/62f34e8/.evergreen/csfle/kms_http_server.py#L231:
$ bash
$ git clone git@github.com:mongodb-labs/drivers-evergreen-tools.git
$ cd drivers-evergreen-tools/.evergreen/csfle
$ . activate_venv.sh
$ python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/server.pem --port 8002 --require_client_cert
Mock KMS Web Server Listening on port 8002
There's some work I could do here to try and create a minimal repro but I won't have time for a while. In the meantime, any theories would be appreciated!