Uploaded image for project: 'Node.js Driver'
  1. Node.js Driver
  2. NODE-5127

Promise returned by libmongocrypt's StateMachine.kmsRequest never resolves when server closes connection without an error

    • 5
    • 0
    • Not Needed
    • Not Needed
    • Hide

      1. What would you like to communicate to the user about this feature?
      2. Would you like the user to see examples of the syntax and/or executable code and its output?
      3. Which versions of the driver/connector does this apply to?

      Show
      1. What would you like to communicate to the user about this feature? 2. Would you like the user to see examples of the syntax and/or executable code and its output? 3. Which versions of the driver/connector does this apply to?

      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:

      1. 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);
      1. 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
      • 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

            Assignee:
            alena.khineika@mongodb.com Alena Khineika
            Reporter:
            himanshu.singh@mongodb.com Himanshu Singh
            Neal Beeken
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: