Skip to content

Commit f373b72

Browse files
committed
Support AWS authentication with temporary credentials in CSFLE (#668)
JAVA-3980
1 parent d1ac231 commit f373b72

File tree

7 files changed

+505
-22
lines changed

7 files changed

+505
-22
lines changed

.evergreen/.evg.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,16 @@ functions:
247247
working_dir: "src"
248248
script: |
249249
${PREPARE_SHELL}
250+
export AWS_ACCESS_KEY_ID=${aws_access_key_id}
251+
export AWS_SECRET_ACCESS_KEY=${aws_secret_access_key}
252+
export AWS_DEFAULT_REGION=us-east-1
253+
. ${DRIVERS_TOOLS}/.evergreen/csfle/set-temp-creds.sh
250254
AUTH="${AUTH}" SSL="${SSL}" MONGODB_URI="${MONGODB_URI}" SAFE_FOR_MULTI_MONGOS="${SAFE_FOR_MULTI_MONGOS}" TOPOLOGY="${TOPOLOGY}" \
251255
COMPRESSOR="${COMPRESSOR}" JDK="${JDK}" \
252256
AWS_ACCESS_KEY_ID=${aws_access_key_id} AWS_SECRET_ACCESS_KEY=${aws_secret_access_key} \
257+
AWS_TEMP_ACCESS_KEY_ID=$CSFLE_AWS_TEMP_ACCESS_KEY_ID \
258+
AWS_TEMP_SECRET_ACCESS_KEY=$CSFLE_AWS_TEMP_SECRET_ACCESS_KEY \
259+
AWS_TEMP_SESSION_TOKEN=$CSFLE_AWS_TEMP_SESSION_TOKEN \
253260
AZURE_TENANT_ID=${azure_tenant_id} AZURE_CLIENT_ID=${azure_client_id} AZURE_CLIENT_SECRET=${azure_client_secret} \
254261
GCP_EMAIL=${gcp_email} GCP_PRIVATE_KEY=${gcp_private_key} \
255262
.evergreen/run-tests.sh

.evergreen/run-tests.sh

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,25 @@ set -o xtrace # Write all commands first to stderr
44
set -o errexit # Exit the script with error if any of the commands fail
55

66
# Supported/used environment variables:
7-
# AUTH Set to enable authentication. Values are: "auth" / "noauth" (default)
8-
# SSL Set to enable SSL. Values are "ssl" / "nossl" (default)
9-
# MONGODB_URI Set the suggested connection MONGODB_URI (including credentials and topology info)
10-
# TOPOLOGY Allows you to modify variables and the MONGODB_URI based on test topology
11-
# Supported values: "server", "replica_set", "sharded_cluster"
12-
# COMPRESSOR Set to enable compression. Values are "snappy" and "zlib" (default is no compression)
13-
# STREAM_TYPE Set the stream type. Values are "nio2" or "netty". Defaults to "nio2".
14-
# JDK Set the version of java to be used. Java versions can be set from the java toolchain /opt/java
15-
# SLOW_TESTS_ONLY Set to true to only run the slow tests
16-
# AWS_ACCESS_KEY_ID The AWS access key identifier for client-side encryption
17-
# AWS_SECRET_ACCESS_KEY The AWS secret access key for client-side encryption
18-
# AZURE_TENANT_ID The Azure tenant identifier for client-side encryption
19-
# AZURE_CLIENT_ID The Azure client identifier for client-side encryption
20-
# AZURE_CLIENT_SECRET The Azure client secret for client-side encryption
21-
# GCP_EMAIL The GCP email for client-side encryption
22-
# GCP_PRIVATE_KEY The GCP private key for client-side encryption
7+
# AUTH Set to enable authentication. Values are: "auth" / "noauth" (default)
8+
# SSL Set to enable SSL. Values are "ssl" / "nossl" (default)
9+
# MONGODB_URI Set the suggested connection MONGODB_URI (including credentials and topology info)
10+
# TOPOLOGY Allows you to modify variables and the MONGODB_URI based on test topology
11+
# Supported values: "server", "replica_set", "sharded_cluster"
12+
# COMPRESSOR Set to enable compression. Values are "snappy" and "zlib" (default is no compression)
13+
# STREAM_TYPE Set the stream type. Values are "nio2" or "netty". Defaults to "nio2".
14+
# JDK Set the version of java to be used. Java versions can be set from the java toolchain /opt/java
15+
# SLOW_TESTS_ONLY Set to true to only run the slow tests
16+
# AWS_ACCESS_KEY_ID The AWS access key identifier for client-side encryption
17+
# AWS_SECRET_ACCESS_KEY The AWS secret access key for client-side encryption
18+
# AWS_TEMP_ACCESS_KEY_ID The temporary AWS access key identifier for client-side encryption
19+
# AWS_TEMP_SECRET_ACCESS_KEY The temporary AWS secret access key for client-side encryption
20+
# AWS_TEMP_SESSION_TOKEN The temporary AWS secret access key for client-side encryption
21+
# AZURE_TENANT_ID The Azure tenant identifier for client-side encryption
22+
# AZURE_CLIENT_ID The Azure client identifier for client-side encryption
23+
# AZURE_CLIENT_SECRET The Azure client secret for client-side encryption
24+
# GCP_EMAIL The GCP email for client-side encryption
25+
# GCP_PRIVATE_KEY The GCP private key for client-side encryption
2326

2427
AUTH=${AUTH:-noauth}
2528
SSL=${SSL:-nossl}
@@ -122,6 +125,7 @@ if [ "$SLOW_TESTS_ONLY" == "true" ]; then
122125
else
123126
./gradlew -PjdkHome=/opt/java/${JDK} -Dorg.mongodb.test.uri=${MONGODB_URI} \
124127
-Dorg.mongodb.test.awsAccessKeyId=${AWS_ACCESS_KEY_ID} -Dorg.mongodb.test.awsSecretAccessKey=${AWS_SECRET_ACCESS_KEY} \
128+
-Dorg.mongodb.test.tmpAwsAccessKeyId=${AWS_TEMP_ACCESS_KEY_ID} -Dorg.mongodb.test.tmpAwsSecretAccessKey=${AWS_TEMP_SECRET_ACCESS_KEY} -Dorg.mongodb.test.tmpAwsSessionToken=${AWS_TEMP_SESSION_TOKEN} \
125129
-Dorg.mongodb.test.azureTenantId=${AZURE_TENANT_ID} -Dorg.mongodb.test.azureClientId=${AZURE_CLIENT_ID} -Dorg.mongodb.test.azureClientSecret=${AZURE_CLIENT_SECRET} \
126130
-Dorg.mongodb.test.gcpEmail=${GCP_EMAIL} -Dorg.mongodb.test.gcpPrivateKey=${GCP_PRIVATE_KEY} \
127131
${TRANSACTION_URI} ${GRADLE_EXTRA_VARS} ${ASYNC_TYPE} --stacktrace --info --continue test

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ ext {
4848
nettyVersion = '4.1.43.Final'
4949
snappyVersion = '1.1.4'
5050
zstdVersion = '1.3.8-3'
51-
mongoCryptVersion = '1.1.0'
51+
mongoCryptVersion = '1.2.0'
5252
projectReactorVersion = 'Californium-SR23'
5353
gitVersion = getGitVersion()
5454
}

driver-core/src/test/functional/com/mongodb/ClusterFixture.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ public static List<Integer> getVersionList(final String versionString) {
182182

183183
public static boolean hasEncryptionTestsEnabled() {
184184
List<String> requiredSystemProperties = asList("awsAccessKeyId", "awsSecretAccessKey", "azureTenantId", "azureClientId",
185-
"azureClientSecret", "gcpEmail", "gcpPrivateKey");
185+
"azureClientSecret", "gcpEmail", "gcpPrivateKey", "tmpAwsAccessKeyId", "tmpAwsSecretAccessKey", "tmpAwsSessionToken");
186186
return requiredSystemProperties.stream()
187187
.map(name -> System.getProperty("org.mongodb.test." + name, ""))
188188
.filter(s -> !s.isEmpty())

driver-core/src/test/resources/client-side-encryption/README.rst

Lines changed: 242 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,72 @@ Then for each element in ``tests``:
147147

148148
#. Create a **new** MongoClient using ``clientOptions``.
149149

150-
#. If ``autoEncryptOpts`` includes ``aws``, ``azure``, and/or ``gcp`` as a KMS provider, pass in credentials from the environment.
151-
#. If ``autoEncryptOpts`` does not include ``keyVaultNamespace``, default it to ``keyvault.datakeys``.
150+
#. If ``autoEncryptOpts`` includes ``aws``, ``awsTemporary``, ``awsTemporaryNoSessionToken``,
151+
``azure``, and/or ``gcp`` as a KMS provider, pass in credentials from the environment.
152+
153+
- ``awsTemporary``, and ``awsTemporaryNoSessionToken`` require temporary
154+
AWS credentials. These can be retrieved using the csfle `set-temp-creds.sh
155+
<https://github.com/mongodb-labs/drivers-evergreen-tools/tree/master/.evergreen/csfle>`_
156+
script.
157+
158+
- ``aws``, ``awsTemporary``, and ``awsTemporaryNoSessionToken`` are
159+
mutually exclusive.
160+
161+
``aws`` should be substituted with:
162+
163+
.. code:: javascript
164+
165+
"aws": {
166+
"accessKeyId": <set from environment>,
167+
"secretAccessKey": <set from environment>
168+
}
169+
170+
``awsTemporary`` should be substituted with:
171+
172+
.. code:: javascript
173+
174+
"aws": {
175+
"accessKeyId": <set from environment>,
176+
"secretAccessKey": <set from environment>
177+
"sessionToken": <set from environment>
178+
}
179+
180+
``awsTemporaryNoSessionToken`` should be substituted with:
181+
182+
.. code:: javascript
183+
184+
"aws": {
185+
"accessKeyId": <set from environment>,
186+
"secretAccessKey": <set from environment>
187+
}
188+
189+
``gcp`` should be substituted with:
190+
191+
.. code:: javascript
192+
193+
"gcp": {
194+
"email": <set from environment>,
195+
"privateKey": <set from environment>,
196+
}
197+
198+
``azure`` should be substituted with:
199+
200+
.. code:: javascript
201+
202+
"azure": {
203+
"tenantId": <set from environment>,
204+
"clientId": <set from environment>,
205+
"clientSecret": <set from environment>,
206+
}
207+
208+
``local`` should be substituted with:
209+
210+
.. code:: javascript
211+
212+
"local": { "key": <base64 decoding of LOCAL_MASTERKEY> }
213+
214+
#. If ``autoEncryptOpts`` does not include ``keyVaultNamespace``, default it
215+
to ``keyvault.datakeys``.
152216

153217
#. For each element in ``operations``:
154218

@@ -318,7 +382,7 @@ For each KMS provider (``aws``, ``azure``, ``gcp``, and ``local``), referred to
318382

319383
- Expect the return value to be a BSON binary subtype 6, referred to as ``encrypted``.
320384
- Use ``client_encrypted`` to insert ``{ _id: "<provider_name>", "value": <encrypted> }`` into ``db.coll``.
321-
- Use ``client_encrypted`` to run a find querying with ``_id`` of "<provider_name>" and expect ``value`` to be "hello local".
385+
- Use ``client_encrypted`` to run a find querying with ``_id`` of "<provider_name>" and expect ``value`` to be "hello <provider_name>".
322386

323387
#. Call ``client_encryption.encrypt()`` with the value "hello <provider_name>", the algorithm ``AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic``, and the ``key_alt_name`` of ``<provider_name>_altname``.
324388

@@ -769,4 +833,178 @@ The following tests that setting ``bypassAutoEncryption=true`` really does bypas
769833

770834
#. Use ``client_encrypted`` to insert the document ``{"unencrypted": "test"}`` into ``db.coll``. Expect this to succeed.
771835

772-
#. Validate that mongocryptd was not spawned. Create a MongoClient to localhost:27021 (or whatever was passed via ``--port``) with serverSelectionTimeoutMS=1000. Run an ``isMaster`` command and ensure it fails with a server selection timeout.
836+
#. Validate that mongocryptd was not spawned. Create a MongoClient to localhost:27021 (or whatever was passed via ``--port``) with serverSelectionTimeoutMS=1000. Run an ``isMaster`` command and ensure it fails with a server selection timeout.
837+
838+
Deadlock tests
839+
~~~~~~~~~~~~~~
840+
841+
.. _Connection Monitoring and Pooling: /source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst
842+
843+
The following tests only apply to drivers that have implemented a connection pool (see the `Connection Monitoring and Pooling`_ specification).
844+
845+
There are multiple parameterized test cases. Before each test case, perform the setup.
846+
847+
Setup
848+
`````
849+
850+
Create a ``MongoClient`` for setup operations named ``client_test``.
851+
852+
Create a ``MongoClient`` for key vault operations with ``maxPoolSize=1`` named ``client_keyvault``. Capture command started events.
853+
854+
Using ``client_test``, drop the collections ``keyvault.datakeys`` and ``db.coll``.
855+
856+
Insert the document `external/external-key.json <../external/external-key.json>`_ into ``keyvault.datakeys`` with majority write concern.
857+
858+
Create a collection ``db.coll`` configured with a JSON schema `external/external-schema.json <../external/external-schema.json>`_ as the validator, like so:
859+
860+
.. code:: typescript
861+
862+
{"create": "coll", "validator": {"$jsonSchema": <json_schema>}}
863+
864+
Create a ``ClientEncryption`` object, named ``client_encryption`` configured with:
865+
- ``keyVaultClient``=``client_test``
866+
- ``keyVaultNamespace``="keyvault.datakeys"
867+
- ``kmsProviders``=``{ "local": { "key": <base64 decoding of LOCAL_MASTERKEY> } }``
868+
869+
Use ``client_encryption`` to encrypt the value "string0" with ``algorithm``="AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" and ``keyAltName``="local". Store the result in a variable named ``ciphertext``.
870+
871+
Proceed to run the test case.
872+
873+
Each test case configures a ``MongoClient`` with automatic encryption (named ``client_encrypted``).
874+
875+
Each test must assert the number of unique ``MongoClient``s created. This can be accomplished by capturing ``TopologyOpeningEvent``, or by checking command started events for a client identifier (not possible in all drivers).
876+
877+
Running a test case
878+
```````````````````
879+
- Create a ``MongoClient`` named ``client_encrypted`` configured as follows:
880+
- Set ``AutoEncryptionOpts``:
881+
- ``keyVaultNamespace="keyvault.datakeys"``
882+
- ``kmsProviders``=``{ "local": { "key": <base64 decoding of LOCAL_MASTERKEY> } }``
883+
- Append ``TestCase.AutoEncryptionOpts`` (defined below)
884+
- Capture command started events.
885+
- Set ``maxPoolSize=TestCase.MaxPoolSize``
886+
- If the testcase sets ``AutoEncryptionOpts.bypassAutoEncryption=true``:
887+
- Use ``client_test`` to insert ``{ "_id": 0, "encrypted": <ciphertext> }`` into ``db.coll``.
888+
- Otherwise:
889+
- Use ``client_encrypted`` to insert ``{ "_id": 0, "encrypted": "string0" }``.
890+
- Use ``client_encrypted`` to run a ``findOne`` operation on ``db.coll``, with the filter ``{ "_id": 0 }``.
891+
- Expect the result to be ``{ "_id": 0, "encrypted": "string0" }``.
892+
- Check captured events against ``TestCase.Expectations``.
893+
- Check the number of unique ``MongoClient``s created is equal to ``TestCase.ExpectedNumberOfClients``.
894+
895+
Case 1
896+
``````
897+
- MaxPoolSize: 1
898+
- AutoEncryptionOpts:
899+
- bypassAutoEncryption=false
900+
- keyVaultClient=unset
901+
- Expectations:
902+
- Expect ``client_encrypted`` to have captured four ``CommandStartedEvent``:
903+
- a listCollections to "db".
904+
- a find on "keyvault".
905+
- an insert on "db".
906+
- a find on "db"
907+
- ExpectedNumberOfClients: 2
908+
909+
Case 2
910+
``````
911+
- MaxPoolSize: 1
912+
- AutoEncryptionOpts:
913+
- bypassAutoEncryption=false
914+
- keyVaultClient=client_keyvault
915+
- Expectations:
916+
- Expect ``client_encrypted`` to have captured three ``CommandStartedEvent``:
917+
- a listCollections to "db".
918+
- an insert on "db".
919+
- a find on "db"
920+
- Expect ``client_keyvault`` to have captured one ``CommandStartedEvent``:
921+
- a find on "keyvault".
922+
- ExpectedNumberOfClients: 2
923+
924+
Case 3
925+
``````
926+
- MaxPoolSize: 1
927+
- AutoEncryptionOpts:
928+
- bypassAutoEncryption=true
929+
- keyVaultClient=unset
930+
- Expectations:
931+
- Expect ``client_encrypted`` to have captured three ``CommandStartedEvent``:
932+
- a find on "db"
933+
- a find on "keyvault".
934+
- ExpectedNumberOfClients: 2
935+
936+
Case 4
937+
``````
938+
- MaxPoolSize: 1
939+
- AutoEncryptionOpts:
940+
- bypassAutoEncryption=true
941+
- keyVaultClient=client_keyvault
942+
- Expectations:
943+
- Expect ``client_encrypted`` to have captured two ``CommandStartedEvent``:
944+
- a find on "db"
945+
- Expect ``client_keyvault`` to have captured one ``CommandStartedEvent``:
946+
- a find on "keyvault".
947+
- ExpectedNumberOfClients: 1
948+
949+
Case 5
950+
``````
951+
Drivers that do not support an unlimited maximum pool size MUST skip this test.
952+
953+
- MaxPoolSize: 0
954+
- AutoEncryptionOpts:
955+
- bypassAutoEncryption=false
956+
- keyVaultClient=unset
957+
- Expectations:
958+
- Expect ``client_encrypted`` to have captured five ``CommandStartedEvent``:
959+
- a listCollections to "db".
960+
- a listCollections to "keyvault".
961+
- a find on "keyvault".
962+
- an insert on "db".
963+
- a find on "db"
964+
- ExpectedNumberOfClients: 1
965+
966+
Case 6
967+
``````
968+
Drivers that do not support an unlimited maximum pool size MUST skip this test.
969+
970+
- MaxPoolSize: 0
971+
- AutoEncryptionOpts:
972+
- bypassAutoEncryption=false
973+
- keyVaultClient=client_keyvault
974+
- Expectations:
975+
- Expect ``client_encrypted`` to have captured three ``CommandStartedEvent``:
976+
- a listCollections to "db".
977+
- an insert on "db".
978+
- a find on "db"
979+
- Expect ``client_keyvault`` to have captured one ``CommandStartedEvent``:
980+
- a find on "keyvault".
981+
- ExpectedNumberOfClients: 1
982+
983+
Case 7
984+
``````
985+
Drivers that do not support an unlimited maximum pool size MUST skip this test.
986+
987+
- MaxPoolSize: 0
988+
- AutoEncryptionOpts:
989+
- bypassAutoEncryption=true
990+
- keyVaultClient=unset
991+
- Expectations:
992+
- Expect ``client_encrypted`` to have captured three ``CommandStartedEvent``:
993+
- a find on "db"
994+
- a find on "keyvault".
995+
- ExpectedNumberOfClients: 1
996+
997+
Case 8
998+
``````
999+
Drivers that do not support an unlimited maximum pool size MUST skip this test.
1000+
1001+
- MaxPoolSize: 0
1002+
- AutoEncryptionOpts:
1003+
- bypassAutoEncryption=true
1004+
- keyVaultClient=client_keyvault
1005+
- Expectations:
1006+
- Expect ``client_encrypted`` to have captured two ``CommandStartedEvent``:
1007+
- a find on "db"
1008+
- Expect ``client_keyvault`` to have captured one ``CommandStartedEvent``:
1009+
- a find on "keyvault".
1010+
- ExpectedNumberOfClients: 1

0 commit comments

Comments
 (0)