Skip to content

Commit a7fb328

Browse files
authored
PYTHON-3004 Support kmip FLE KMS provider (#786)
Resync CSFLE spec tests.
1 parent 754e528 commit a7fb328

File tree

12 files changed

+5328
-39
lines changed

12 files changed

+5328
-39
lines changed

.evergreen/config.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,49 @@ functions:
359359
PYTHON_BINARY=${PYTHON_BINARY} bash ${PROJECT_DIRECTORY}/.evergreen/run-doctests.sh
360360
361361
"run tests":
362+
# If testing FLE, start the KMS mock servers, first create the virtualenv.
363+
- command: shell.exec
364+
params:
365+
script: |
366+
if [ -n "${test_encryption}" ]; then
367+
${PREPARE_SHELL}
368+
cd ${DRIVERS_TOOLS}/.evergreen/csfle
369+
. ./activate_venv.sh
370+
fi
371+
# Run in the background so the mock servers don't block the EVG task.
372+
- command: shell.exec
373+
params:
374+
background: true
375+
script: |
376+
if [ -n "${test_encryption}" ]; then
377+
${PREPARE_SHELL}
378+
cd ${DRIVERS_TOOLS}/.evergreen/csfle
379+
. ./activate_venv.sh
380+
# The -u options forces the stdout and stderr streams to be unbuffered.
381+
# TMPDIR is required to avoid "AF_UNIX path too long" errors.
382+
TMPDIR="$(dirname $DRIVERS_TOOLS)" python -u kms_kmip_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/server.pem --port 5698 &
383+
python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/expired.pem --port 8000 &
384+
python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/wrong-host.pem --port 8001 &
385+
python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/server.pem --port 8002 --require_client_cert &
386+
fi
387+
# Wait up to 10 seconds for the KMIP server to start.
388+
- command: shell.exec
389+
params:
390+
script: |
391+
if [ -n "${test_encryption}" ]; then
392+
${PREPARE_SHELL}
393+
cd ${DRIVERS_TOOLS}/.evergreen/csfle
394+
. ./activate_venv.sh
395+
for i in $(seq 1 1 10); do
396+
sleep 1
397+
if python -u kms_kmip_client.py; then
398+
echo 'KMS KMIP server started!'
399+
exit 0
400+
fi
401+
done
402+
echo 'Failed to start KMIP server!'
403+
exit 1
404+
fi
362405
- command: shell.exec
363406
type: test
364407
params:

.evergreen/run-tests.sh

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,6 @@ if [ -n "$TEST_ENCRYPTION" ]; then
146146
# Get access to the AWS temporary credentials:
147147
# CSFLE_AWS_TEMP_ACCESS_KEY_ID, CSFLE_AWS_TEMP_SECRET_ACCESS_KEY, CSFLE_AWS_TEMP_SESSION_TOKEN
148148
. $DRIVERS_TOOLS/.evergreen/csfle/set-temp-creds.sh
149-
150-
# Start the mock KMS servers.
151-
pushd ${DRIVERS_TOOLS}/.evergreen/csfle
152-
python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/expired.pem --port 8000 &
153-
python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/wrong-host.pem --port 8001 &
154-
trap 'kill $(jobs -p)' EXIT HUP
155-
popd
156149
fi
157150

158151
if [ -z "$DATA_LAKE" ]; then

pymongo/encryption.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def kms_request(self, kms_context):
109109
message = kms_context.message
110110
provider = kms_context.kms_provider
111111
ctx = self.opts._kms_ssl_contexts.get(provider)
112-
if not ctx:
112+
if ctx is None:
113113
# Enable strict certificate verification, OCSP, match hostname, and
114114
# SNI using the system default CA certificates.
115115
ctx = get_ssl_context(
@@ -378,9 +378,8 @@ def __init__(self, kms_providers, key_vault_namespace, key_vault_client,
378378
See :ref:`explicit-client-side-encryption` for an example.
379379
380380
:Parameters:
381-
- `kms_providers`: Map of KMS provider options. Two KMS providers
382-
are supported: "aws" and "local". The kmsProviders map values
383-
differ by provider:
381+
- `kms_providers`: Map of KMS provider options. The `kms_providers`
382+
map values differ by provider:
384383
385384
- `aws`: Map with "accessKeyId" and "secretAccessKey" as strings.
386385
These are the AWS access key ID and AWS secret access key used
@@ -396,6 +395,8 @@ def __init__(self, kms_providers, key_vault_namespace, key_vault_client,
396395
Additionally, "endpoint" may also be specified as a string
397396
(defaults to 'oauth2.googleapis.com'). These are the
398397
credentials used to generate Google Cloud KMS messages.
398+
- `kmip`: Map with "endpoint" as a host with required port.
399+
For example: ``{"endpoint": "example.com:443"}``.
399400
- `local`: Map with "key" as `bytes` (96 bytes in length) or
400401
a base64 encoded string which decodes
401402
to 96 bytes. "key" is the master key used to encrypt/decrypt
@@ -424,7 +425,7 @@ def __init__(self, kms_providers, key_vault_namespace, key_vault_client,
424425
kms_tls_options={'kmip': {'tlsCAFile': certifi.where()}}
425426
426427
.. versionchanged:: 4.0
427-
Added the `kms_tls_options` parameter.
428+
Added the `kms_tls_options` parameter and the "kmip" KMS provider.
428429
429430
.. versionadded:: 3.9
430431
"""
@@ -458,7 +459,7 @@ def create_data_key(self, kms_provider, master_key=None,
458459
459460
:Parameters:
460461
- `kms_provider`: The KMS provider to use. Supported values are
461-
"aws" and "local".
462+
"aws", "azure", "gcp", "kmip", and "local".
462463
- `master_key`: Identifies a KMS-specific key used to encrypt the
463464
new data key. If the kmsProvider is "local" the `master_key` is
464465
not applicable and may be omitted.
@@ -493,6 +494,16 @@ def create_data_key(self, kms_provider, master_key=None,
493494
- `endpoint` (string): Optional. Host with optional port.
494495
Defaults to "cloudkms.googleapis.com".
495496
497+
If the `kms_provider` is "kmip" it is optional and has the
498+
following fields::
499+
500+
- `keyId` (string): Optional. `keyId` is the KMIP Unique
501+
Identifier to a 96 byte KMIP Secret Data managed object. If
502+
keyId is omitted, the driver creates a random 96 byte KMIP
503+
Secret Data managed object.
504+
- `endpoint` (string): Optional. Host with optional
505+
port, e.g. "example.vault.azure.net:".
506+
496507
- `key_alt_names` (optional): An optional list of string alternate
497508
names used to reference a key. If a key is created with alternate
498509
names, then encryption may refer to the key by the unique alternate

pymongo/encryption_options.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,8 @@ def __init__(self, kms_providers, key_vault_namespace,
5555
See :ref:`automatic-client-side-encryption` for an example.
5656
5757
:Parameters:
58-
- `kms_providers`: Map of KMS provider options. Two KMS providers
59-
are supported: "aws" and "local". The kmsProviders map values
60-
differ by provider:
58+
- `kms_providers`: Map of KMS provider options. The `kms_providers`
59+
map values differ by provider:
6160
6261
- `aws`: Map with "accessKeyId" and "secretAccessKey" as strings.
6362
These are the AWS access key ID and AWS secret access key used
@@ -73,6 +72,8 @@ def __init__(self, kms_providers, key_vault_namespace,
7372
Additionally, "endpoint" may also be specified as a string
7473
(defaults to 'oauth2.googleapis.com'). These are the
7574
credentials used to generate Google Cloud KMS messages.
75+
- `kmip`: Map with "endpoint" as a host with required port.
76+
For example: ``{"endpoint": "example.com:443"}``.
7677
- `local`: Map with "key" as `bytes` (96 bytes in length) or
7778
a base64 encoded string which decodes
7879
to 96 bytes. "key" is the master key used to encrypt/decrypt
@@ -129,7 +130,7 @@ def __init__(self, kms_providers, key_vault_namespace,
129130
kms_tls_options={'kmip': {'tlsCAFile': certifi.where()}}
130131
131132
.. versionchanged:: 4.0
132-
Added the `kms_tls_options` parameter.
133+
Added the `kms_tls_options` parameter and the "kmip" KMS provider.
133134
134135
.. versionadded:: 3.9
135136
"""

0 commit comments

Comments
 (0)