Skip to content

Commit 6ac4655

Browse files
RUBY-2833 Add KMIP Support (#2383)
1 parent 4f5e06d commit 6ac4655

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+15761
-4324
lines changed

.evergreen/config-atlas.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,20 @@ functions:
420420
${PREPARE_SHELL}
421421
SERVERLESS=1 SSL=ssl RVM_RUBY="${RVM_RUBY}" SINGLE_MONGOS="${SINGLE_MONGOS}" MULTI_ATLASPROXY_SERVERLESS_URI="${MULTI_ATLASPROXY_SERVERLESS_URI}" SINGLE_ATLASPROXY_SERVERLESS_URI="${SINGLE_ATLASPROXY_SERVERLESS_URI}" .evergreen/run-tests-serverless.sh
422422
423+
"start kmip server":
424+
- command: shell.exec
425+
params:
426+
background: true
427+
shell: bash
428+
working_dir: "src"
429+
script: |
430+
. .evergreen/csfle/activate_venv.sh
431+
python -u .evergreen/csfle/kms_http_server.py --ca_file .evergreen/x509gen/ca.pem --cert_file .evergreen/x509gen/server.pem --port 7999 &
432+
python -u .evergreen/csfle/kms_http_server.py --ca_file .evergreen/x509gen/ca.pem --cert_file .evergreen/x509gen/expired.pem --port 8000 &
433+
python -u .evergreen/csfle/kms_http_server.py --ca_file .evergreen/x509gen/ca.pem --cert_file .evergreen/x509gen/wrong-host.pem --port 8001 &
434+
python -u .evergreen/csfle/kms_http_server.py --ca_file .evergreen/x509gen/ca.pem --cert_file .evergreen/x509gen/server.pem --port 8002 --require_client_cert &
435+
python -u .evergreen/csfle/kms_kmip_server.py &
436+
423437
pre:
424438
- func: "fetch source"
425439
- func: "fetch egos"

.evergreen/config.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,20 @@ functions:
437437
${PREPARE_SHELL}
438438
SERVERLESS=1 SSL=ssl RVM_RUBY="${RVM_RUBY}" SINGLE_MONGOS="${SINGLE_MONGOS}" MULTI_ATLASPROXY_SERVERLESS_URI="${MULTI_ATLASPROXY_SERVERLESS_URI}" SINGLE_ATLASPROXY_SERVERLESS_URI="${SINGLE_ATLASPROXY_SERVERLESS_URI}" .evergreen/run-tests-serverless.sh
439439
440+
"start kmip server":
441+
- command: shell.exec
442+
params:
443+
background: true
444+
shell: bash
445+
working_dir: "src"
446+
script: |
447+
. .evergreen/csfle/activate_venv.sh
448+
python -u .evergreen/csfle/kms_http_server.py --ca_file .evergreen/x509gen/ca.pem --cert_file .evergreen/x509gen/server.pem --port 7999 &
449+
python -u .evergreen/csfle/kms_http_server.py --ca_file .evergreen/x509gen/ca.pem --cert_file .evergreen/x509gen/expired.pem --port 8000 &
450+
python -u .evergreen/csfle/kms_http_server.py --ca_file .evergreen/x509gen/ca.pem --cert_file .evergreen/x509gen/wrong-host.pem --port 8001 &
451+
python -u .evergreen/csfle/kms_http_server.py --ca_file .evergreen/x509gen/ca.pem --cert_file .evergreen/x509gen/server.pem --port 8002 --require_client_cert &
452+
python -u .evergreen/csfle/kms_kmip_server.py &
453+
440454
pre:
441455
- func: "fetch source"
442456
- func: "fetch egos"
@@ -471,6 +485,7 @@ tasks:
471485
- name: "test-fle"
472486
commands:
473487
- func: "export FLE credentials"
488+
- func: "start kmip server"
474489
- func: "run tests"
475490
- name: "test-aws-auth"
476491
commands:

.evergreen/config/common.yml.erb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,20 @@ functions:
502502
${PREPARE_SHELL}
503503
SERVERLESS=1 SSL=ssl RVM_RUBY="${RVM_RUBY}" SINGLE_MONGOS="${SINGLE_MONGOS}" MULTI_ATLASPROXY_SERVERLESS_URI="${MULTI_ATLASPROXY_SERVERLESS_URI}" SINGLE_ATLASPROXY_SERVERLESS_URI="${SINGLE_ATLASPROXY_SERVERLESS_URI}" .evergreen/run-tests-serverless.sh
504504

505+
"start kmip server":
506+
- command: shell.exec
507+
params:
508+
background: true
509+
shell: bash
510+
working_dir: "src"
511+
script: |
512+
. .evergreen/csfle/activate_venv.sh
513+
python -u .evergreen/csfle/kms_http_server.py --ca_file .evergreen/x509gen/ca.pem --cert_file .evergreen/x509gen/server.pem --port 7999 &
514+
python -u .evergreen/csfle/kms_http_server.py --ca_file .evergreen/x509gen/ca.pem --cert_file .evergreen/x509gen/expired.pem --port 8000 &
515+
python -u .evergreen/csfle/kms_http_server.py --ca_file .evergreen/x509gen/ca.pem --cert_file .evergreen/x509gen/wrong-host.pem --port 8001 &
516+
python -u .evergreen/csfle/kms_http_server.py --ca_file .evergreen/x509gen/ca.pem --cert_file .evergreen/x509gen/server.pem --port 8002 --require_client_cert &
517+
python -u .evergreen/csfle/kms_kmip_server.py &
518+
505519
pre:
506520
- func: "fetch source"
507521
- func: "fetch egos"
@@ -547,6 +561,7 @@ tasks:
547561
- name: "test-fle"
548562
commands:
549563
- func: "export FLE credentials"
564+
- func: "start kmip server"
550565
- func: "run tests"
551566
- name: "test-aws-auth"
552567
commands:

.evergreen/csfle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../.mod/drivers-evergreen-tools/.evergreen/csfle

.evergreen/run-tests.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,24 @@ if test -n "$FLE"; then
139139

140140
export LIBMONGOCRYPT_PATH=`pwd`/rhel-70-64-bit/nocrypto/lib64/libmongocrypt.so
141141
test -f "$LIBMONGOCRYPT_PATH"
142+
143+
echo "Waiting for mock KMS servers to start..."
144+
wait_for_kms_server() {
145+
for i in $(seq 60); do
146+
if curl -s "localhost:$1"; test $? -ne 7; then
147+
return 0
148+
else
149+
sleep 1
150+
fi
151+
done
152+
echo "Could not detect mock KMS server on port $1"
153+
return 1
154+
}
155+
wait_for_kms_server 8000
156+
wait_for_kms_server 8001
157+
wait_for_kms_server 8002
158+
wait_for_kms_server 5698
159+
echo "Waiting for mock KMS servers to start... done."
142160
fi
143161

144162
if test -n "$OCSP_CONNECTIVITY"; then

.evergreen/x509gen

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../.mod/drivers-evergreen-tools/.evergreen/x509gen/

docs/reference/client-side-encryption.txt

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,12 @@ Please note that GCP private key can be in different formats. Ruby driver
403403
supports DER encoded RSA private key as base64 encoded string. For MRI Ruby
404404
the driver additionally support PEM encoded RSA private key.
405405

406+
If you have created a master key using a Key Management Interoperability
407+
Protocol (KMIP) compatible key management server, note the server host and port,
408+
and key id. You will use that information to generate a data key. You may also
409+
need certificate authority certificate(s), as well as and your client
410+
certificate and private key to authenticate to KMIP server.
411+
406412

407413
.. code-block:: ruby
408414

@@ -430,6 +436,18 @@ the driver additionally support PEM encoded RSA private key.
430436
# :private_key value should be GCP private key as base64 encoded
431437
# DER RSA private key, or PEM RSA private key, if you are using MRI Ruby.
432438
private_key: 'GCP-PRIVATE-KEY',
439+
},
440+
kmip: {
441+
# KMIP server endpoint may include port.
442+
endpoint: 'KMIP-SERVER-HOST'
443+
},
444+
# TLS options to connect to KMIP server.
445+
kms_tls_options: {
446+
kmip: {
447+
ssl_ca_cert: 'PATH-TO-CA-FILE',
448+
ssl_cert: 'PATH-TO-CLIENT-CERT-FILE',
449+
ssl_key: 'PATH-TO-CLIENT-KEY-FILE'
450+
}
433451
}
434452
}
435453
)
@@ -477,6 +495,11 @@ For more information about creating a data key, see the
477495
:drivers:`Create a Data Encryption Key </security/client-side-field-level-encryption-guide/#b.-create-a-data-encryption-key>`
478496
section of the MongoDB manual.
479497

498+
For a list of possible KMS TLS options
499+
see :manual:`create client reference </reference/config-database/>`.
500+
``Mongo::ClientEncryption`` constructor accepts same ``ssl_`` options as
501+
``Mongo::Client``.
502+
480503
Auto-Encryption Options
481504
=======================
482505
Automatic encryption can be configured on a ``Mongo::Client`` using the
@@ -523,6 +546,50 @@ keys are stored in the ``admin`` database in the ``datakeys`` collection:
523546

524547
There is no default key vault namespace, and this option must be provided.
525548

549+
``:kms_providers``
550+
~~~~~~~~~~~~~~~~~~
551+
A Hash that contains KMP provider names as keys, and provider options as values.
552+
553+
.. code-block:: ruby
554+
555+
Mongo::Client.new(['localhost:27017],
556+
auto_encryption_options: {
557+
key_vault_namespace: 'admin.datakeys',
558+
kms_providers: {
559+
aws: {
560+
access_key_id: 'IAM-ACCESS-KEY-ID',
561+
secret_access_key: 'IAM-SECRET-ACCESS-KEY'
562+
}
563+
}
564+
}
565+
)
566+
567+
``:kms_tls_options``
568+
~~~~~~~~~~~~~~~~~~
569+
A Hash that contains KMP provider names as keys, and TLS options to connect to
570+
corresponding providers.
571+
572+
.. code-block:: ruby
573+
574+
Mongo::Client.new(['localhost:27017],
575+
auto_encryption_options: {
576+
key_vault_namespace: 'admin.datakeys',
577+
kms_providers: {
578+
kmip: {
579+
endpoint: 'KMIP-SERVER-HOST'
580+
}
581+
},
582+
kms_tls_options: {
583+
kmip: {
584+
ssl_ca_cert: 'PATH-TO-CA-FILE',
585+
ssl_cert: 'PATH-TO-CLIENT-CERT-FILE',
586+
ssl_key: 'PATH-TO-CLIENT-KEY-FILE'
587+
}
588+
}
589+
}
590+
)
591+
592+
526593
``:schema_map``
527594
~~~~~~~~~~~~~~~
528595
A schema map is a Hash with information about which fields to automatically

docs/release-notes.txt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ comprehensive list of changes in each version of the driver and the
1717
for the complete list of changes, including those internal to the driver and
1818
its test suite.
1919

20+
2.18
21+
====
22+
23+
This release includes the following new features:
24+
25+
- Added support for Azure Key Vault, Google Cloud Key Management, and any
26+
KMIP compliant Key Management System to be used as master key storage for
27+
client side encryption.
2028

2129
2.17
2230
====
@@ -148,7 +156,7 @@ functionality has been added:
148156
cross-driver mechanims to discover deployment topology or force direct
149157
connection.
150158
- Support for :ref:`MONGODB-AWS authentication mechanism <aws-auth>`.
151-
- When SCRAM authentication is used with 4.4 and newer servers, the driver will
159+
- When SCRAM authentication is used with 4.4 and newer servers, the driver will
152160
complete authentication with fewer network roundtrips.
153161
- The driver creates an additional monitoring connection for 4.4 and newer
154162
servers, permitting the server to notify the driver when its state changes.
@@ -265,7 +273,7 @@ This release adds the following new features:
265273

266274
The following smaller improvements have been made:
267275

268-
- Support for the ``startAfter`` option in the ``$changeStream``
276+
- Support for the ``startAfter`` option in the ``$changeStream``
269277
aggregation pipeline stage.
270278
- Field order of BSON documents sent to the server changed for better logging.
271279
- Certificate paths with unescaped slashes can now be specified in

lib/mongo/client.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -429,9 +429,13 @@ def hash
429429
# instance containing the encryption key vault
430430
# - :key_vault_namespace => String, the namespace of the key vault in the
431431
# format database.collection
432-
# - :kms_providers => Hash, A hash of key management service configuration
433-
# information. Valid hash keys are :local or :aws. There may be more
434-
# than one kms provider specified.
432+
# - :kms_providers => Hash, A hash of key management service (KMS) configuration
433+
# information. Valid hash keys are :aws, :azure, :gcp, :kmip, :local.
434+
# There may be more than one kms provider specified.
435+
# - :kms_tls_options => Hash, A hash of TLS options to authenticate to
436+
# KMS providers, usually used for KMIP servers. Valid hash keys
437+
# are :aws, :azure, :gcp, :kmip, :local. There may be more than one
438+
# kms provider specified.
435439
# - :schema_map => Hash | nil, JSONSchema for one or more collections
436440
# specifying which fields should be encrypted.
437441
# - Note: Schemas supplied in the schema_map only apply to configuring

lib/mongo/crypt/encryption_io.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ def with_ssl_socket(endpoint, tls_options)
265265
)
266266
yield(mongo_socket.socket)
267267
rescue => e
268-
raise Error::KmsError, "Error decrypting data key: #{e.class}: #{e.message}"
268+
raise Error::KmsError, "Error when connecting to KMS provider: #{e.class}: #{e.message}"
269269
ensure
270270
mongo_socket&.close
271271
end

lib/mongo/crypt/kms.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,5 @@ def validate_tls_options(options)
113113
require 'mongo/crypt/kms/aws'
114114
require 'mongo/crypt/kms/azure'
115115
require 'mongo/crypt/kms/gcp'
116+
require 'mongo/crypt/kms/kmip'
116117
require 'mongo/crypt/kms/local'

lib/mongo/crypt/kms/credentials.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,17 @@ def initialize(kms_providers)
4949
if kms_providers.key?(:gcp)
5050
@gcp = GCP::Credentials.new(kms_providers[:gcp])
5151
end
52+
if kms_providers.key?(:kmip)
53+
@kmip = KMIP::Credentials.new(kms_providers[:kmip])
54+
end
5255
if kms_providers.key?(:local)
5356
@local = Local::Credentials.new(kms_providers[:local])
5457
end
55-
if @aws.nil? && @azure.nil? && @gcp.nil? && @local.nil?
56-
raise ArgumentError.new("KMS providers options must have one of the following keys: :aws, :azure, :gcp, :local")
58+
if @aws.nil? && @azure.nil? && @gcp.nil? && @kmip.nil? && @local.nil?
59+
raise ArgumentError.new(
60+
"KMS providers options must have one of the following keys: " +
61+
":aws, :azure, :gcp, :kmip, :local"
62+
)
5763
end
5864
end
5965

@@ -65,6 +71,7 @@ def to_document
6571
bson[:aws] = @aws.to_document if @aws
6672
bson[:azure] = @azure.to_document if @azure
6773
bson[:gcp] = @gcp.to_document if @gcp
74+
bson[:kmip] = @kmip.to_document if @kmip
6875
bson[:local] = @local.to_document if @local
6976
end
7077
end

0 commit comments

Comments
 (0)