NIFI-12771 Restored Client-side KMS encryption strategy for S3

This closes #8388

Signed-off-by: David Handermann <exceptionfactory@apache.org>v
This commit is contained in:
Peter Turcsanyi 2024-02-09 23:25:17 +01:00 committed by exceptionfactory
parent 7681054cf7
commit 0f4defa49a
No known key found for this signature in database
4 changed files with 96 additions and 9 deletions

View File

@ -60,10 +60,10 @@ public class ClientSideCEncryptionStrategy implements S3EncryptionStrategy {
final StaticEncryptionMaterialsProvider encryptionMaterialsProvider = new StaticEncryptionMaterialsProvider(new EncryptionMaterials(symmetricKey));
final CryptoConfigurationV2 cryptoConfig = new CryptoConfigurationV2();
cryptoConfig.setAwsKmsRegion(Region.getRegion(Regions.fromName(kmsRegion)));
// A placeholder KMS Region needs to be set due to bug https://github.com/aws/aws-sdk-java/issues/2530
cryptoConfig.setAwsKmsRegion(Region.getRegion(Regions.DEFAULT_REGION));
final AmazonS3EncryptionClientV2Builder builder = AmazonS3EncryptionClientV2.encryptionBuilder()
.disableChunkedEncoding()
.withCryptoConfiguration(cryptoConfig)
.withEncryptionMaterialsProvider(encryptionMaterialsProvider);
clientBuilder.accept(builder);

View File

@ -0,0 +1,62 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.nifi.processors.aws.s3.encryption;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Builder;
import com.amazonaws.services.s3.AmazonS3EncryptionClientV2;
import com.amazonaws.services.s3.AmazonS3EncryptionClientV2Builder;
import com.amazonaws.services.s3.model.CryptoConfigurationV2;
import com.amazonaws.services.s3.model.KMSEncryptionMaterialsProvider;
import org.apache.commons.lang3.StringUtils;
import java.util.function.Consumer;
/**
* This strategy uses KMS key id to perform client-side encryption. Use this strategy when you want the client to perform the encryption,
* (thus incurring the cost of processing) and manage the key in a KMS instance.
*
* See https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingClientSideEncryption.html#client-side-encryption-kms-managed-master-key-intro
*
*/
public class ClientSideKMSEncryptionStrategy implements S3EncryptionStrategy {
/**
* Create an encryption client.
*
* @param clientBuilder A consumer that is responsible for configuring the client builder
* @param kmsRegion AWS KMS region
* @param keyIdOrMaterial KMS key id
* @return AWS S3 client
*/
@Override
public AmazonS3 createEncryptionClient(final Consumer<AmazonS3Builder<?, ?>> clientBuilder, final String kmsRegion, final String keyIdOrMaterial) {
final KMSEncryptionMaterialsProvider encryptionMaterialsProvider = new KMSEncryptionMaterialsProvider(keyIdOrMaterial);
final CryptoConfigurationV2 cryptoConfig = new CryptoConfigurationV2();
if (StringUtils.isNotBlank(kmsRegion)) {
cryptoConfig.setAwsKmsRegion(Region.getRegion(Regions.fromName(kmsRegion)));
}
final AmazonS3EncryptionClientV2Builder builder = AmazonS3EncryptionClientV2.encryptionBuilder()
.withCryptoConfiguration(cryptoConfig)
.withEncryptionMaterialsProvider(encryptionMaterialsProvider);
clientBuilder.accept(builder);
return builder.build();
}
}

View File

@ -62,6 +62,7 @@ public class StandardS3EncryptionService extends AbstractControllerService imple
STRATEGY_NAME_SSE_S3, new ServerSideS3EncryptionStrategy(),
STRATEGY_NAME_SSE_KMS, new ServerSideKMSEncryptionStrategy(),
STRATEGY_NAME_SSE_C, new ServerSideCEncryptionStrategy(),
STRATEGY_NAME_CSE_KMS, new ClientSideKMSEncryptionStrategy(),
STRATEGY_NAME_CSE_C, new ClientSideCEncryptionStrategy()
);

View File

@ -35,6 +35,9 @@ import static org.junit.jupiter.api.Assertions.assertNull;
public class TestS3EncryptionStrategies {
private static final String REGION = Regions.DEFAULT_REGION.getName();
private static final String KEY_ID = "key-id";
private String randomKeyMaterial = "";
private ObjectMetadata metadata = null;
@ -63,8 +66,30 @@ public class TestS3EncryptionStrategies {
// This shows that the strategy builds a client:
assertNotNull(strategy.createEncryptionClient(builder -> {
builder.withRegion(Regions.DEFAULT_REGION.name());
}, Regions.DEFAULT_REGION.getName(), randomKeyMaterial));
builder.withRegion(REGION);
}, null, randomKeyMaterial));
// This shows that the strategy does not modify the metadata or any of the requests:
assertNull(metadata.getSSEAlgorithm());
assertNull(putObjectRequest.getSSEAwsKeyManagementParams());
assertNull(putObjectRequest.getSSECustomerKey());
assertNull(initUploadRequest.getSSEAwsKeyManagementParams());
assertNull(initUploadRequest.getSSECustomerKey());
assertNull(getObjectRequest.getSSECustomerKey());
assertNull(uploadPartRequest.getSSECustomerKey());
}
@Test
public void testClientSideKMSEncryptionStrategy() {
S3EncryptionStrategy strategy = new ClientSideKMSEncryptionStrategy();
// This shows that the strategy builds a client:
assertNotNull(strategy.createEncryptionClient(builder -> {
builder.withRegion(REGION);
}, REGION, KEY_ID));
// This shows that the strategy does not modify the metadata or any of the requests:
assertNull(metadata.getSSEAlgorithm());
@ -117,15 +142,14 @@ public class TestS3EncryptionStrategies {
assertNull(strategy.createEncryptionClient(null, null, null));
// This shows that the strategy sets the SSE KMS key id as expected:
String randomKeyId = "mock-key-id";
strategy.configurePutObjectRequest(putObjectRequest, metadata, randomKeyId);
assertEquals(randomKeyId, putObjectRequest.getSSEAwsKeyManagementParams().getAwsKmsKeyId());
strategy.configurePutObjectRequest(putObjectRequest, metadata, KEY_ID);
assertEquals(KEY_ID, putObjectRequest.getSSEAwsKeyManagementParams().getAwsKmsKeyId());
assertNull(putObjectRequest.getSSECustomerKey());
assertNull(metadata.getSSEAlgorithm());
// Same for InitiateMultipartUploadRequest:
strategy.configureInitiateMultipartUploadRequest(initUploadRequest, metadata, randomKeyId);
assertEquals(randomKeyId, initUploadRequest.getSSEAwsKeyManagementParams().getAwsKmsKeyId());
strategy.configureInitiateMultipartUploadRequest(initUploadRequest, metadata, KEY_ID);
assertEquals(KEY_ID, initUploadRequest.getSSEAwsKeyManagementParams().getAwsKmsKeyId());
assertNull(initUploadRequest.getSSECustomerKey());
assertNull(metadata.getSSEAlgorithm());
}