Skip to content

Support NEED_KMS_CREDENTIALS state for CSFLE #889

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .evergreen/.evg.yml
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,17 @@ functions:
set +o xtrace
MONGODB_URI="${MONGODB_URI}" KMS_TLS_ERROR_TYPE=${KMS_TLS_ERROR_TYPE} .evergreen/run-kms-tls-tests.sh

"run-csfle-aws-from-environment-test":
- command: shell.exec
type: test
params:
working_dir: "src"
script: |
${PREPARE_SHELL}
set +o xtrace
MONGODB_URI="${MONGODB_URI}" AWS_ACCESS_KEY_ID=${aws_access_key_id} AWS_SECRET_ACCESS_KEY=${aws_secret_access_key} \
.evergreen/run-csfle-aws-from-environment.sh

"publish snapshot":
- command: shell.exec
type: test
Expand Down Expand Up @@ -1361,6 +1372,17 @@ tasks:
TOPOLOGY: "server"
AUTH: "noauth"
SSL: "nossl"

- name: "test-csfle-aws-from-environment"
tags: ["csfle-aws-from-environment"]
commands:
- func: "bootstrap mongo-orchestration"
vars:
TOPOLOGY: "server"
AUTH: "noauth"
SSL: "nossl"
- func: run-csfle-aws-from-environment-test

axes:
- id: version
display_name: MongoDB Version
Expand Down Expand Up @@ -1772,3 +1794,9 @@ buildvariants:
display_name: "CSFLE KMS TLS"
tasks:
- name: ".kms-tls"

- matrix_name: "csfle-aws-from-environment-test"
matrix_spec: { os: "linux", version: [ "5.0" ], topology: ["standalone"] }
display_name: "CSFLE AWS From Environment"
tasks:
- name: ".csfle-aws-from-environment"
39 changes: 39 additions & 0 deletions .evergreen/run-csfle-aws-from-environment.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/bin/bash

# Don't trace since the URI contains a password that shouldn't show up in the logs
set -o errexit # Exit the script with error if any of the commands fail

# Supported/used environment variables:
# MONGODB_URI Set the suggested connection MONGODB_URI (including credentials and topology info)
# AWS_ACCESS_KEY_ID The AWS access key identifier for client-side encryption
# AWS_SECRET_ACCESS_KEY The AWS secret access key for client-side encryption

############################################
# Main Program #
############################################
RELATIVE_DIR_PATH="$(dirname "${BASH_SOURCE:-$0}")"
. "${RELATIVE_DIR_PATH}/javaConfig.bash"
echo "Running CSFLE AWS from environment tests"

./gradlew -version

export AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
export AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}

./gradlew --stacktrace --info -Dorg.mongodb.test.uri=${MONGODB_URI} \
--no-build-cache driver-sync:cleanTest driver-sync:test --tests ClientSideEncryptionAwsCredentialFromEnvironmentTest
first=$?
echo $first

./gradlew --stacktrace --info -Dorg.mongodb.test.uri=${MONGODB_URI} \
--no-build-cache driver-reactive-streams:cleanTest driver-reactive-streams:test --tests ClientSideEncryptionAwsCredentialFromEnvironmentTest
second=$?
echo $second

if [ $first -ne 0 ]; then
exit $first
elif [ $second -ne 0 ]; then
exit $second
else
exit 0
fi
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ ext {
nettyTcnativeBoringsslVersion = '2.0.46.Final'
snappyVersion = '1.1.8.4'
zstdVersion = '1.5.0-4'
mongoCryptVersion = '1.3.0'
mongoCryptVersion = '1.4.0-alpha0'
projectReactorVersion = 'Californium-SR23'
junitBomVersion = '5.8.1'
gitVersion = getGitVersion()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@

package com.mongodb.internal.capi;

import com.mongodb.AwsCredential;
import com.mongodb.Block;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientException;
import com.mongodb.MongoClientSettings;
import com.mongodb.connection.ClusterSettings;
import com.mongodb.connection.SocketSettings;
import com.mongodb.crypt.capi.MongoCryptOptions;
import com.mongodb.internal.authentication.AwsCredentialHelper;
import org.bson.BsonDocument;
import org.bson.BsonDocumentWrapper;
import org.bson.BsonString;
import org.bson.Document;
import org.bson.codecs.DocumentCodec;

Expand All @@ -40,13 +43,36 @@ public static MongoCryptOptions createMongoCryptOptions(final Map<String, Map<St
final Map<String, BsonDocument> namespaceToLocalSchemaDocumentMap) {
MongoCryptOptions.Builder mongoCryptOptionsBuilder = MongoCryptOptions.builder();

BsonDocument bsonKmsProviders = getKmsProvidersAsBsonDocument(kmsProviders);
mongoCryptOptionsBuilder.kmsProviderOptions(bsonKmsProviders);
mongoCryptOptionsBuilder.localSchemaMap(namespaceToLocalSchemaDocumentMap);
mongoCryptOptionsBuilder.needsKmsCredentialsStateEnabled(true);
return mongoCryptOptionsBuilder.build();
}

public static BsonDocument fetchCredentials(final Map<String, Map<String, Object>> kmsProviders) {
BsonDocument kmsProvidersDocument = MongoCryptHelper.getKmsProvidersAsBsonDocument(kmsProviders);
if (kmsProvidersDocument.containsKey("aws") && kmsProvidersDocument.get("aws").asDocument().isEmpty()) {
AwsCredential awsCredential = AwsCredentialHelper.obtainFromEnvironment();
if (awsCredential != null) {
BsonDocument awsCredentialDocument = new BsonDocument();
awsCredentialDocument.put("accessKeyId", new BsonString(awsCredential.getAccessKeyId()));
awsCredentialDocument.put("secretAccessKey", new BsonString(awsCredential.getSecretAccessKey()));
if (awsCredential.getSessionToken() != null) {
awsCredentialDocument.put("sessionToken", new BsonString(awsCredential.getSessionToken()));
}
kmsProvidersDocument.put("aws", awsCredentialDocument);
}
}
return kmsProvidersDocument;
}

private static BsonDocument getKmsProvidersAsBsonDocument(final Map<String, Map<String, Object>> kmsProviders) {
BsonDocument bsonKmsProviders = new BsonDocument();
for (Map.Entry<String, Map<String, Object>> entry : kmsProviders.entrySet()) {
bsonKmsProviders.put(entry.getKey(), new BsonDocumentWrapper<>(new Document(entry.getValue()), new DocumentCodec()));
}
mongoCryptOptionsBuilder.kmsProviderOptions(bsonKmsProviders);
mongoCryptOptionsBuilder.localSchemaMap(namespaceToLocalSchemaDocumentMap);
return mongoCryptOptionsBuilder.build();
return bsonKmsProviders;
}

@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.mongodb.crypt.capi.MongoKeyDecryptor;
import com.mongodb.diagnostics.logging.Logger;
import com.mongodb.diagnostics.logging.Loggers;
import com.mongodb.internal.capi.MongoCryptHelper;
import com.mongodb.lang.Nullable;
import com.mongodb.reactivestreams.client.MongoClient;
import org.bson.BsonBinary;
Expand All @@ -39,6 +40,7 @@
import reactor.core.publisher.MonoSink;

import java.io.Closeable;
import java.util.Map;
import java.util.function.Supplier;

import static com.mongodb.assertions.Assertions.notNull;
Expand All @@ -48,6 +50,7 @@
public class Crypt implements Closeable {
private static final Logger LOGGER = Loggers.getLogger("client");
private final MongoCrypt mongoCrypt;
private final Map<String, Map<String, Object>> kmsProviders;
private final CollectionInfoRetriever collectionInfoRetriever;
private final CommandMarker commandMarker;
private final KeyRetriever keyRetriever;
Expand All @@ -60,11 +63,13 @@ public class Crypt implements Closeable {
* Create an instance to use for explicit encryption and decryption, and data key creation.
*
* @param mongoCrypt the mongoCrypt wrapper
* @param kmsProviders the kms providers
* @param keyRetriever the key retriever
* @param keyManagementService the key management service
*/
Crypt(final MongoCrypt mongoCrypt, final KeyRetriever keyRetriever, final KeyManagementService keyManagementService) {
this(mongoCrypt, null, null, keyRetriever, keyManagementService, false, null);
Crypt(final MongoCrypt mongoCrypt, final Map<String, Map<String, Object>> kmsProviders, final KeyRetriever keyRetriever,
final KeyManagementService keyManagementService) {
this(mongoCrypt, kmsProviders, null, null, keyRetriever, keyManagementService, false, null);
}

/**
Expand All @@ -77,13 +82,15 @@ public class Crypt implements Closeable {
* @param commandMarker the command marker
*/
Crypt(final MongoCrypt mongoCrypt,
final Map<String, Map<String, Object>> kmsProviders,
@Nullable final CollectionInfoRetriever collectionInfoRetriever,
@Nullable final CommandMarker commandMarker,
final KeyRetriever keyRetriever,
final KeyManagementService keyManagementService,
final boolean bypassAutoEncryption,
@Nullable final MongoClient internalClient) {
this.mongoCrypt = mongoCrypt;
this.kmsProviders = kmsProviders;
this.collectionInfoRetriever = collectionInfoRetriever;
this.commandMarker = commandMarker;
this.keyRetriever = keyRetriever;
Expand Down Expand Up @@ -213,6 +220,9 @@ private void executeStateMachineWithSink(final MongoCryptContext cryptContext, @
case NEED_MONGO_MARKINGS:
mark(cryptContext, databaseName, sink);
break;
case NEED_KMS_CREDENTIALS:
fetchCredentials(cryptContext, databaseName, sink);
break;
case NEED_MONGO_KEYS:
fetchKeys(cryptContext, databaseName, sink);
break;
Expand All @@ -227,6 +237,16 @@ private void executeStateMachineWithSink(final MongoCryptContext cryptContext, @
}
}

private void fetchCredentials(final MongoCryptContext cryptContext, @Nullable final String databaseName,
final MonoSink<RawBsonDocument> sink) {
try {
cryptContext.provideKmsProviderCredentials(MongoCryptHelper.fetchCredentials(kmsProviders));
executeStateMachineWithSink(cryptContext, databaseName, sink);
} catch (Exception e) {
sink.error(e);
}
}

private void collInfo(final MongoCryptContext cryptContext,
@Nullable final String databaseName,
final MonoSink<RawBsonDocument> sink) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public static Crypt createCrypt(final MongoClientImpl client, final AutoEncrypti
? internalClient : MongoClients.create(keyVaultMongoClientSettings);
return new Crypt(MongoCrypts.create(createMongoCryptOptions(options.getKmsProviders(),
options.getSchemaMap())),
options.getKmsProviders(),
options.isBypassAutoEncryption() ? null : new CollectionInfoRetriever(collectionInfoRetrieverClient),
new CommandMarker(options.isBypassAutoEncryption(), options.getExtraOptions()),
new KeyRetriever(keyVaultClient, new MongoNamespace(options.getKeyVaultNamespace())),
Expand All @@ -63,6 +64,7 @@ public static Crypt createCrypt(final MongoClientImpl client, final AutoEncrypti
public static Crypt create(final MongoClient keyVaultClient, final ClientEncryptionSettings options) {
return new Crypt(MongoCrypts.create(
createMongoCryptOptions(options.getKmsProviders(), null)),
options.getKmsProviders(),
new KeyRetriever(keyVaultClient, new MongoNamespace(options.getKeyVaultNamespace())),
createKeyManagementService(options.getKmsProviderSslContextMap()));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2008-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.mongodb.reactivestreams.client;

import com.mongodb.ClientEncryptionSettings;
import com.mongodb.MongoClientSettings;
import com.mongodb.client.AbstractClientSideEncryptionAwsCredentialFromEnvironmentTest;
import com.mongodb.client.MongoClient;
import com.mongodb.client.vault.ClientEncryption;
import com.mongodb.lang.NonNull;
import com.mongodb.reactivestreams.client.syncadapter.SyncClientEncryption;
import com.mongodb.reactivestreams.client.syncadapter.SyncMongoClient;
import com.mongodb.reactivestreams.client.vault.ClientEncryptions;

public class ClientSideEncryptionAwsCredentialFromEnvironmentTest extends AbstractClientSideEncryptionAwsCredentialFromEnvironmentTest {
@Override
@NonNull
protected ClientEncryption createClientEncryption(@NonNull final ClientEncryptionSettings settings) {
return new SyncClientEncryption(ClientEncryptions.create(settings));
}

@Override
@NonNull
protected MongoClient createMongoClient(@NonNull final MongoClientSettings settings) {
return new SyncMongoClient(MongoClients.create(settings));
}
}
33 changes: 24 additions & 9 deletions driver-sync/src/main/com/mongodb/client/internal/Crypt.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.mongodb.crypt.capi.MongoDataKeyOptions;
import com.mongodb.crypt.capi.MongoExplicitEncryptOptions;
import com.mongodb.crypt.capi.MongoKeyDecryptor;
import com.mongodb.internal.capi.MongoCryptHelper;
import com.mongodb.lang.Nullable;
import org.bson.BsonBinary;
import org.bson.BsonDocument;
Expand All @@ -38,13 +39,15 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.Map;

import static com.mongodb.assertions.Assertions.notNull;
import static com.mongodb.crypt.capi.MongoCryptContext.State;

class Crypt implements Closeable {

private final MongoCrypt mongoCrypt;
private final Map<String, Map<String, Object>> kmsProviders;
private final CollectionInfoRetriever collectionInfoRetriever;
private final CommandMarker commandMarker;
private final KeyRetriever keyRetriever;
Expand All @@ -56,26 +59,31 @@ class Crypt implements Closeable {
* Create an instance to use for explicit encryption and decryption, and data key creation.
*
* @param mongoCrypt the mongoCrypt wrapper
* @param kmsProviders the kms providers
* @param keyRetriever the key retriever
* @param keyManagementService the key management service
*/
Crypt(final MongoCrypt mongoCrypt, final KeyRetriever keyRetriever, final KeyManagementService keyManagementService) {
this(mongoCrypt, null, null, keyRetriever, keyManagementService, false, null);
Crypt(final MongoCrypt mongoCrypt, final Map<String, Map<String, Object>> kmsProviders, final KeyRetriever keyRetriever,
final KeyManagementService keyManagementService) {
this(mongoCrypt, kmsProviders, null, null, keyRetriever, keyManagementService, false, null);
}

/**
* Create an instance to use for auto-encryption and auto-decryption.
*
* @param mongoCrypt the mongoCrypt wrapper
* @param keyRetriever the key retriever
* @param keyManagementService the key management service
* @param mongoCrypt the mongoCrypt wrapper
* @param kmsProviders the KMS provider credentials
* @param collectionInfoRetriever the collection info retriever
* @param commandMarker the command marker
* @param keyRetriever the key retriever
* @param keyManagementService the key management service
*/
Crypt(final MongoCrypt mongoCrypt, @Nullable final CollectionInfoRetriever collectionInfoRetriever,
@Nullable final CommandMarker commandMarker, final KeyRetriever keyRetriever,
final KeyManagementService keyManagementService, final boolean bypassAutoEncryption, @Nullable final MongoClient internalClient) {
Crypt(final MongoCrypt mongoCrypt, final Map<String, Map<String, Object>> kmsProviders,
@Nullable final CollectionInfoRetriever collectionInfoRetriever,
@Nullable final CommandMarker commandMarker, final KeyRetriever keyRetriever,
final KeyManagementService keyManagementService, final boolean bypassAutoEncryption,
@Nullable final MongoClient internalClient) {
this.mongoCrypt = mongoCrypt;
this.kmsProviders = kmsProviders;
this.collectionInfoRetriever = collectionInfoRetriever;
this.commandMarker = commandMarker;
this.keyRetriever = keyRetriever;
Expand Down Expand Up @@ -241,6 +249,9 @@ private RawBsonDocument executeStateMachine(final MongoCryptContext cryptContext
case NEED_MONGO_MARKINGS:
mark(cryptContext, databaseName);
break;
case NEED_KMS_CREDENTIALS:
fetchCredentials(cryptContext);
break;
case NEED_MONGO_KEYS:
fetchKeys(cryptContext);
break;
Expand All @@ -255,6 +266,10 @@ private RawBsonDocument executeStateMachine(final MongoCryptContext cryptContext
}
}

private void fetchCredentials(final MongoCryptContext cryptContext) {
cryptContext.provideKmsProviderCredentials(MongoCryptHelper.fetchCredentials(kmsProviders));
}

private void collInfo(final MongoCryptContext cryptContext, final String databaseName) {
try {
BsonDocument collectionInfo = collectionInfoRetriever.filter(databaseName, cryptContext.getMongoOperation());
Expand Down
2 changes: 2 additions & 0 deletions driver-sync/src/main/com/mongodb/client/internal/Crypts.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public static Crypt createCrypt(final MongoClientImpl client, final AutoEncrypti
? internalClient : MongoClients.create(keyVaultMongoClientSettings);
return new Crypt(MongoCrypts.create(createMongoCryptOptions(options.getKmsProviders(),
options.getSchemaMap())),
options.getKmsProviders(),
options.isBypassAutoEncryption() ? null : new CollectionInfoRetriever(collectionInfoRetrieverClient),
new CommandMarker(options.isBypassAutoEncryption(), options.getExtraOptions()),
new KeyRetriever(keyVaultClient, new MongoNamespace(options.getKeyVaultNamespace())),
Expand All @@ -57,6 +58,7 @@ public static Crypt createCrypt(final MongoClientImpl client, final AutoEncrypti
static Crypt create(final MongoClient keyVaultClient, final ClientEncryptionSettings options) {
return new Crypt(MongoCrypts.create(
createMongoCryptOptions(options.getKmsProviders(), null)),
options.getKmsProviders(),
createKeyRetriever(keyVaultClient, options.getKeyVaultNamespace()),
createKeyManagementService(options.getKmsProviderSslContextMap()));
}
Expand Down
Loading