Skip to content

feat(NODE-7546): add HTTP Proxy support for QE & CSFLE#5007

Draft
tadjik1 wants to merge 6 commits into
mainfrom
NODE-7546
Draft

feat(NODE-7546): add HTTP Proxy support for QE & CSFLE#5007
tadjik1 wants to merge 6 commits into
mainfrom
NODE-7546

Conversation

@tadjik1

@tadjik1 tadjik1 commented Jul 17, 2026

Copy link
Copy Markdown
Member

Description

Summary of Changes

Adds kmsConnectCallback to AutoEncryptionOptions and ClientEncryptionOptions. When provided, the driver invokes the callback to establish the TCP connection to a KMS host instead of opening the socket itself, then wraps the returned socket with TLS using the KMS provider's configured TLS options. The primary use case is routing KMS requests through an HTTP proxy via the HTTP CONNECT method, which the existing SOCKS5 proxyOptions does not cover.

Spec: mongodb/specifications#1956

Release Highlight

HTTP proxy support for KMS requests in CSFLE and Queryable Encryption

In-use encryption can now route KMS requests through an HTTP proxy. Set kmsConnectCallback on your ClientEncryption or auto-encryption options to control how the driver connects to a KMS host. The callback receives the target host and port and returns a connected socket (for example, a tunnel
opened with HTTP CONNECT); the driver then performs the KMS TLS handshake over that socket using the provider's configured TLS options. This unblocks CSFLE and Queryable Encryption in environments that require an HTTP forward proxy for outbound KMS traffic, which the existing SOCKS5 proxyOptions does not cover.

const clientEncryption = new ClientEncryption(keyVaultClient, {
  keyVaultNamespace,
  kmsProviders,
  // Establish the KMS connection through your HTTP proxy; the driver adds TLS.
  kmsConnectCallback: ({ host, port }) => connectThroughHttpProxy(host, port)
});

Double check the following

  • Lint is passing (npm run check:lint)
  • Self-review completed using the steps outlined here
  • PR title follows the correct format: type(NODE-xxxx)[!]: description
    • Example: feat(NODE-1234)!: rewriting everything in coffeescript
  • Changes are covered by tests
  • New TODOs have a related JIRA ticket

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for customizing how KMS sockets are established for CSFLE/Queryable Encryption by introducing a kmsConnectCallback API (enabling HTTP CONNECT proxy tunneling) and wiring it through the CSFLE state machine, with accompanying unit + prose integration coverage.

Changes:

  • Introduces KMSConnectCallback (and related TLS/socket option types) in a new kms_options module and exports them from the public index.
  • Wires kmsConnectCallback through AutoEncrypter/ClientEncryption into the StateMachine KMS request path (including CSOT-aware behavior) and enforces mutual exclusivity with proxyOptions.
  • Adds unit tests and a new prose integration test (Prose test 28) validating callback invocation, proxy tunneling behavior, and timeout/error propagation.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/unit/client-side-encryption/state_machine.test.ts Adds unit coverage for CSOT behavior and new kmsConnectCallback socket establishment + timeout semantics.
test/unit/client-side-encryption/client_encryption.test.ts Adds unit coverage for proxyOptions vs kmsConnectCallback conflict validation in ClientEncryption.
test/unit/client-side-encryption/auto_encrypter.test.ts Adds unit coverage for proxyOptions vs kmsConnectCallback conflict validation in AutoEncrypter.
test/mongodb_all.ts Re-exports new kms_options types for tests.
test/integration/client-side-encryption/client_side_encryption.prose.28.kms_connect_callback.test.ts Adds prose integration coverage for KMS connect callback through HTTP/HTTPS proxy.
src/index.ts Publicly exports kms_options types and removes those type exports from state_machine.
src/client-side-encryption/state_machine.ts Implements kmsConnectCallback support and revises CSOT timeout handling for KMS requests.
src/client-side-encryption/kms_options.ts New module defining public KMS TLS/socket option types and KMSConnectCallback contract.
src/client-side-encryption/client_encryption.ts Stores/validates kmsConnectCallback and passes it into StateMachine.
src/client-side-encryption/auto_encrypter.ts Stores/validates kmsConnectCallback and passes it into StateMachine.

Comment thread src/client-side-encryption/state_machine.ts Outdated
const timeoutMS = Number.isFinite(remainingTimeMS) ? remainingTimeMS : undefined;
kmsRequestTimeout = timeoutMS ? Timeout.expires(timeoutMS) : undefined;
await (kmsRequestTimeout
? Promise.race([willResolveKmsRequest, kmsRequestTimeout])

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: .race() instead of .all() since the timeout is throwing on expire. This is fix for the pre-existing bug (test coverage also added).

}
}

/**

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is small refactoring, instead of adding one more Options type into StateMachine code I have extracted it into separate module kms_options.ts.

@tadjik1
tadjik1 marked this pull request as ready for review July 17, 2026 14:28
@tadjik1
tadjik1 requested a review from a team as a code owner July 17, 2026 14:28
@tadjik1
tadjik1 requested a review from nbbeeken July 17, 2026 14:28
Comment thread src/client-side-encryption/kms_options.ts Outdated
@dariakp dariakp added the Blocked Blocked on other work label Jul 17, 2026
nbbeeken
nbbeeken previously approved these changes Jul 17, 2026

@nbbeeken nbbeeken left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apologies for the piecemeal review, noticed this on the second read. LGTM either way

Comment thread src/client-side-encryption/state_machine.ts

@dariakp dariakp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this PR must wait for mongodb/specifications#1956 to merge before it can be fully reviewed and merged. (Will resolve this comment once this PR is unblocked).

@dariakp
dariakp marked this pull request as draft July 20, 2026 14:54
@dariakp
dariakp requested a review from PavelSafronov July 20, 2026 14:55
host: socketOptions.host,
port: socketOptions.port,
timeoutMS,
signal: controller.signal

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the signal also include options.signal? That's an optional signal that is honored elsewhere (line 450), but we're ignore it here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, addressed!

const timeoutError = new MongoOperationTimeoutError('KMS request timed out');
// Abort with the timeout error as the reason so the callback sees it on `signal.reason`.
controller.abort(timeoutError);
throw timeoutError;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If connectPromise eventually (after timeout) returns a socket, that socket will never be closed. Consider something like to close the socket:

connectPromise.then(
  sock => { if (controller.signal.aborted) sock.destroy(); },
  () => {} // nothing to cleanup
);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

THanks! Added.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Blocked Blocked on other work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants