-
Type: Bug
-
Resolution: Fixed
-
Priority: Major - P3
-
Affects Version/s: None
-
Component/s: Client Side Encryption
What problem are you facing?
Implementation of `StateMachine.kmsRequest` in libmongocrypt does not resolve the returned promise when the server that TLS socket is trying to connect to, closes the connection without an explicit error.
Error Specifics:
We only handle the `timeout` and `error` event on tls socket but not the `end` and `close` events. In one particular case (in the HELP ticket it was an invalid tls client certificate), the server will close the connection without an error and because we don't handle those (`end` and `close`) events the promise is never resolved.
As a result of this odd case, end products, Mongosh and built-in shell in Compass will hang while evaluating the following piece of code:
db.getMongo().getKeyVault().createKey("kmip", {}, ["uniq-key-1"]);
What driver and relevant dependency versions are you using?
mongodb: 5.1.0
mongodb-client-encryption: 2.6.0
Steps to reproduce?
Just to fabricate this odd situation follow the steps below:
- Have a mock KMIP server close the connection as soon as handshake is done.
const tls = require('node:tls'); const fs = require('node:fs'); const server = tls.createServer({ key: fs.readFileSync('server-key.pem'), // use server key from x509gen folder in drivers-evergreen-tools cert: fs.readFileSync('server-cert.pem'), // use server cert from x509gen folder in drivers-evergreen-tools ca: [fs.readFileSync('ca.pem')] // use ca from x509gen folder in drivers-evergreen-tools }, (socket) => socket.end()); server.listen(5698);
- Run the following snippet in mongosh in attempt to create a data key
var endpoint = "https://localhost:5698"; var tlsOptions = { tlsCAFile: "ca.pem", tlsCertificateKeyFile: "client.pem" }; var provider = "kmip"; var kmsProviders = { kmip: { endpoint } }; var masterKey = {}; var keyVaultNamespace = "encryption.__keyVault"; var autoEncryptionOpts = { keyVaultNamespace: keyVaultNamespace, kmsProviders: kmsProviders, tlsOptions: { [provider]: tlsOptions }, explicitEncryptionOnly: true}; var encClient = Mongo(db.getMongo(), autoEncryptionOpts); var keyVault = encClient.getKeyVault(); var dek1 = keyVault.createKey(provider, { masterKey, keyAltNames: ["0703-dataKe6"] });
If you're interested in how could scenario come up, there is an EC2 instance configured with a Thales KMIP server in Integrations account on our AWS. Connect to that using the following credentials:
var endpoint = "https://ip-of-ec2:port-config-on-ec2"; var tlsOptions = { tlsCAFile: "thales-ca.pem" // please ask for it tlsCertificateKeyFile: "thales-bad-certificate.pem" // please ask for it} // also attached is a certificate with which things work - please ask for it // Rest of the snippet same as above
AC
Implementation
- Add listeners to the missing socket events to make the below cases pass
- We should listen to the close event
- Manually check that the end event happens, confirm whether or not it is something we need to add handling for
- Align with our driver's Connection (in the constructor) socket event handling
- We should listen to the close event
- Refactoring kmsRequest to async/await
Testing
- Reproduce this issue in CI with a test case
- May need to launch a dummy KMS server
- Test for the server closing without an error
- None of the encryption APIs should hang (ex. createKey), the end or close events should trigger an error
- A lack of a response should reject the promise since the KMS request did not succeed
- fixes
-
NODE-3959 Refactor async executor function inside stateMachine of FLE lib
- Closed