diff --git a/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/BootstrapProperties.java b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/BootstrapProperties.java
index cc31acf5ff..1bdf023e14 100644
--- a/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/BootstrapProperties.java
+++ b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/BootstrapProperties.java
@@ -34,7 +34,7 @@ public class BootstrapProperties extends StandardReadableProperties {
public enum BootstrapPropertyKey {
SENSITIVE_KEY("bootstrap.sensitive.key"),
HASHICORP_VAULT_SENSITIVE_PROPERTY_PROVIDER_CONF("bootstrap.protection.hashicorp.vault.conf"),
- AWS_KMS_SENSITIVE_PROPERTY_PROVIDER_CONF("bootstrap.protection.aws.kms.conf"),
+ AWS_SENSITIVE_PROPERTY_PROVIDER_CONF("bootstrap.protection.aws.conf"),
AZURE_KEYVAULT_SENSITIVE_PROPERTY_PROVIDER_CONF("bootstrap.protection.azure.keyvault.conf"),
GCP_KMS_SENSITIVE_PROPERTY_PROVIDER_CONF("bootstrap.protection.gcp.kms.conf"),
CONTEXT_MAPPING_PREFIX("bootstrap.protection.context.mapping.");
diff --git a/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/ProtectedPropertyContext.java b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/ProtectedPropertyContext.java
index ca2da37f99..0cdf46b1d6 100644
--- a/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/ProtectedPropertyContext.java
+++ b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/ProtectedPropertyContext.java
@@ -64,4 +64,20 @@ public class ProtectedPropertyContext {
public String getContextKey() {
return String.format("%s/%s", contextName, propertyName);
}
+
+ /**
+ * Returns the property name
+ * @return The property name
+ */
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ /**
+ * Returns the context name
+ * @return The context name
+ */
+ public String getContextName() {
+ return contextName;
+ }
}
diff --git a/nifi-commons/nifi-sensitive-property-provider/pom.xml b/nifi-commons/nifi-sensitive-property-provider/pom.xml
index 0fb83e359e..aa23a5aada 100644
--- a/nifi-commons/nifi-sensitive-property-provider/pom.xml
+++ b/nifi-commons/nifi-sensitive-property-provider/pom.xml
@@ -71,6 +71,21 @@
+
+ software.amazon.awssdk
+ secretsmanager
+ ${aws.sdk.version}
+
+
+ software.amazon.awssdk
+ netty-nio-client
+
+
+ software.amazon.awssdk
+ apache-client
+
+
+
com.azure
azure-security-keyvault-secrets
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/AbstractHashiCorpVaultSensitivePropertyProvider.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/AbstractHashiCorpVaultSensitivePropertyProvider.java
index 63c8c6274e..f992ccdba3 100644
--- a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/AbstractHashiCorpVaultSensitivePropertyProvider.java
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/AbstractHashiCorpVaultSensitivePropertyProvider.java
@@ -43,7 +43,7 @@ public abstract class AbstractHashiCorpVaultSensitivePropertyProvider extends Ab
if (hasRequiredVaultProperties()) {
try {
vaultCommunicationService = new StandardHashiCorpVaultCommunicationService(getVaultPropertySource(vaultBootstrapConfFilename));
- } catch (IOException e) {
+ } catch (final IOException e) {
throw new SensitivePropertyProtectionException("Error configuring HashiCorpVaultCommunicationService", e);
}
} else {
@@ -64,7 +64,7 @@ public abstract class AbstractHashiCorpVaultSensitivePropertyProvider extends Ab
try {
vaultBootstrapProperties = AbstractBootstrapPropertiesLoader.loadBootstrapProperties(
Paths.get(vaultBootstrapConfFilename), VAULT_PREFIX);
- } catch (IOException e) {
+ } catch (final IOException e) {
throw new SensitivePropertyProtectionException("Could not load " + vaultBootstrapConfFilename, e);
}
} else {
@@ -97,14 +97,6 @@ public abstract class AbstractHashiCorpVaultSensitivePropertyProvider extends Ab
return hasRequiredVaultProperties();
}
- /**
- * Returns the Vault-specific bootstrap properties (e.g., bootstrap-vault.properties)
- * @return The Vault-specific bootstrap properties
- */
- protected BootstrapProperties getVaultBootstrapProperties() {
- return vaultBootstrapProperties;
- }
-
private boolean hasRequiredVaultProperties() {
return vaultBootstrapProperties != null
&& (vaultBootstrapProperties.getProperty(VaultConfigurationKey.URI.getKey()) != null)
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/AwsSecretsManagerSensitivePropertyProvider.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/AwsSecretsManagerSensitivePropertyProvider.java
new file mode 100644
index 0000000000..cb899fd080
--- /dev/null
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/AwsSecretsManagerSensitivePropertyProvider.java
@@ -0,0 +1,163 @@
+/*
+ * 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.properties;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
+import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse;
+import software.amazon.awssdk.services.secretsmanager.model.ResourceNotFoundException;
+import software.amazon.awssdk.services.secretsmanager.model.SecretsManagerException;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public class AwsSecretsManagerSensitivePropertyProvider extends AbstractSensitivePropertyProvider {
+ private final SecretsManagerClient client;
+ private final ObjectMapper objectMapper;
+
+ private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
+ private final Lock readLock = rwLock.readLock();
+ private final Lock writeLock = rwLock.writeLock();
+
+ AwsSecretsManagerSensitivePropertyProvider(final SecretsManagerClient client) {
+ super(null);
+
+ this.client = client;
+ this.objectMapper = new ObjectMapper();
+ }
+
+ @Override
+ public boolean isSupported() {
+ return client != null;
+ }
+
+ @Override
+ public String protect(final String unprotectedValue, final ProtectedPropertyContext context)
+ throws SensitivePropertyProtectionException {
+ Objects.requireNonNull(context, "Property context must be provided");
+ Objects.requireNonNull(unprotectedValue, "Property value must be provided");
+
+ if (client == null) {
+ throw new SensitivePropertyProtectionException("AWS Secrets Manager Provider Not Configured");
+ }
+
+ try {
+ writeLock.lock();
+ final String secretName = context.getContextName();
+ final Optional secretKeyValuesOptional = getSecretKeyValues(context);
+ final ObjectNode secretObject = secretKeyValuesOptional.orElse(objectMapper.createObjectNode());
+
+ secretObject.put(context.getPropertyName(), unprotectedValue);
+ final String secretString = objectMapper.writeValueAsString(secretObject);
+
+ if (secretKeyValuesOptional.isPresent()) {
+ client.putSecretValue(builder -> builder.secretId(secretName).secretString(secretString));
+ } else {
+ client.createSecret(builder -> builder.name(secretName).secretString(secretString));
+ }
+ return context.getContextKey();
+ } catch (final SecretsManagerException | JsonProcessingException e) {
+ throw new SensitivePropertyProtectionException(String.format("AWS Secrets Manager Secret Could Not Be Stored for [%s]", context), e);
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ @Override
+ public String unprotect(final String protectedValue, final ProtectedPropertyContext context)
+ throws SensitivePropertyProtectionException {
+ Objects.requireNonNull(context, "Property context must be provided");
+
+ if (client == null) {
+ throw new SensitivePropertyProtectionException("AWS Secrets Manager Provider Not Configured");
+ }
+ try {
+ readLock.lock();
+
+ String propertyValue = null;
+ final Optional secretKeyValuesOptional = getSecretKeyValues(context);
+ if (secretKeyValuesOptional.isPresent()) {
+ final ObjectNode secretKeyValues = secretKeyValuesOptional.get();
+ final String propertyName = context.getPropertyName();
+ if (secretKeyValues.has(propertyName)) {
+ propertyValue = secretKeyValues.get(propertyName).textValue();
+ }
+ }
+ if (propertyValue == null) {
+ throw new SensitivePropertyProtectionException(
+ String.format("AWS Secret Name [%s] Property Name [%s] not found", context.getContextName(), context.getPropertyName()));
+ }
+
+ return propertyValue;
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * Returns the optional parsed JSON from the matching secret, or empty if the secret does not exist.
+ * @param context The property context
+ * @return The optional parsed JSON, or empty if the secret does not exist
+ */
+ private Optional getSecretKeyValues(final ProtectedPropertyContext context) {
+ try {
+ final GetSecretValueResponse response = client.getSecretValue(builder -> builder.secretId(context.getContextName()));
+
+ if (response.secretString() == null) {
+ throw new SensitivePropertyProtectionException(String.format("AWS Secret Name [%s] string value not found",
+ context.getContextKey()));
+ }
+ final JsonNode responseNode = objectMapper.readTree(response.secretString());
+ if (!(responseNode instanceof ObjectNode)) {
+ throw new SensitivePropertyProtectionException(String.format("AWS Secrets Manager Secret [%s] JSON parsing failed",
+ context.getContextKey()));
+ }
+ return Optional.of((ObjectNode) responseNode) ;
+ } catch (final ResourceNotFoundException e) {
+ return Optional.empty();
+ } catch (final SecretsManagerException e) {
+ throw new SensitivePropertyProtectionException(String.format("AWS Secrets Manager Secret [%s] retrieval failed",
+ context.getContextKey()), e);
+ } catch (final JsonProcessingException e) {
+ throw new SensitivePropertyProtectionException(String.format("AWS Secrets Manager Secret [%s] JSON parsing failed",
+ context.getContextKey()), e);
+ }
+ }
+
+ @Override
+ protected PropertyProtectionScheme getProtectionScheme() {
+ return PropertyProtectionScheme.AWS_SECRETSMANAGER;
+ }
+
+ @Override
+ public String getIdentifierKey() {
+ return getProtectionScheme().getIdentifier();
+ }
+
+ @Override
+ public void cleanUp() {
+ if (client != null) {
+ client.close();
+ }
+ }
+}
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/PropertyProtectionScheme.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/PropertyProtectionScheme.java
index 7d710040b0..4a85799832 100644
--- a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/PropertyProtectionScheme.java
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/PropertyProtectionScheme.java
@@ -25,6 +25,7 @@ import java.util.Objects;
*/
public enum PropertyProtectionScheme {
AES_GCM("aes/gcm/(128|192|256)", "aes/gcm/%s", "AES Sensitive Property Provider", true),
+ AWS_SECRETSMANAGER("aws/secretsmanager", "aws/secretsmanager", "AWS Secrets Manager Sensitive Property Provider", false),
AWS_KMS("aws/kms", "aws/kms", "AWS KMS Sensitive Property Provider", false),
AZURE_KEYVAULT_KEY("azure/keyvault/key", "azure/keyvault/key", "Azure Key Vault Key Sensitive Property Provider", false),
AZURE_KEYVAULT_SECRET("azure/keyvault/secret", "azure/keyvault/secret", "Azure Key Vault Secret Sensitive Property Provider", false),
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/StandardSensitivePropertyProviderFactory.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/StandardSensitivePropertyProviderFactory.java
index 1d7e12f050..f9a9e6c568 100644
--- a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/StandardSensitivePropertyProviderFactory.java
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/StandardSensitivePropertyProviderFactory.java
@@ -21,6 +21,7 @@ import com.azure.security.keyvault.secrets.SecretClient;
import com.google.cloud.kms.v1.KeyManagementServiceClient;
import org.apache.nifi.properties.BootstrapProperties.BootstrapPropertyKey;
import org.apache.nifi.properties.configuration.AwsKmsClientProvider;
+import org.apache.nifi.properties.configuration.AwsSecretsManagerClientProvider;
import org.apache.nifi.properties.configuration.AzureCryptographyClientProvider;
import org.apache.nifi.properties.configuration.AzureSecretClientProvider;
import org.apache.nifi.properties.configuration.ClientProvider;
@@ -30,6 +31,7 @@ import org.apache.nifi.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.services.kms.KmsClient;
+import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
import java.io.IOException;
import java.util.Arrays;
@@ -137,6 +139,13 @@ public class StandardSensitivePropertyProviderFactory implements SensitiveProper
final Optional kmsClient = clientProvider.getClient(clientProperties);
return new AwsKmsSensitivePropertyProvider(kmsClient.orElse(null), clientProperties);
});
+ case AWS_SECRETSMANAGER:
+ return providerMap.computeIfAbsent(protectionScheme, s -> {
+ final AwsSecretsManagerClientProvider clientProvider = new AwsSecretsManagerClientProvider();
+ final Properties clientProperties = getClientProperties(clientProvider);
+ final Optional secretsManagerClient = clientProvider.getClient(clientProperties);
+ return new AwsSecretsManagerSensitivePropertyProvider(secretsManagerClient.orElse(null));
+ });
case AZURE_KEYVAULT_KEY:
return providerMap.computeIfAbsent(protectionScheme, s -> {
final AzureCryptographyClientProvider clientProvider = new AzureCryptographyClientProvider();
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/AbstractAwsClientProvider.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/AbstractAwsClientProvider.java
new file mode 100644
index 0000000000..9b002b60e8
--- /dev/null
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/AbstractAwsClientProvider.java
@@ -0,0 +1,89 @@
+/*
+ * 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.properties.configuration;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.properties.BootstrapProperties;
+import org.apache.nifi.properties.SensitivePropertyProtectionException;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.auth.credentials.AwsCredentials;
+import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
+import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
+import software.amazon.awssdk.core.SdkClient;
+
+import java.util.Properties;
+
+/**
+ * Amazon Web Services Service Client Provider base class
+ */
+public abstract class AbstractAwsClientProvider extends BootstrapPropertiesClientProvider {
+ private static final String ACCESS_KEY_PROPS_NAME = "aws.access.key.id";
+
+ private static final String SECRET_KEY_PROPS_NAME = "aws.secret.access.key";
+
+ private static final String REGION_KEY_PROPS_NAME = "aws.region";
+
+ public AbstractAwsClientProvider() {
+ super(BootstrapProperties.BootstrapPropertyKey.AWS_SENSITIVE_PROPERTY_PROVIDER_CONF);
+ }
+
+ /**
+ * Get Configured Client using either Client Properties or AWS Default Credentials Provider
+ *
+ * @param clientProperties Client Properties
+ * @return KMS Client
+ */
+ @Override
+ protected T getConfiguredClient(final Properties clientProperties) {
+ final String accessKey = clientProperties.getProperty(ACCESS_KEY_PROPS_NAME);
+ final String secretKey = clientProperties.getProperty(SECRET_KEY_PROPS_NAME);
+ final String region = clientProperties.getProperty(REGION_KEY_PROPS_NAME);
+
+ if (StringUtils.isNoneBlank(accessKey, secretKey, region)) {
+ logger.debug("AWS Credentials Location: Client Properties");
+ try {
+ final AwsBasicCredentials credentials = AwsBasicCredentials.create(accessKey, secretKey);
+ return createClient(credentials, region);
+ } catch (final RuntimeException e) {
+ throw new SensitivePropertyProtectionException("AWS Client Builder Failed using Client Properties", e);
+ }
+ } else {
+ logger.debug("AWS Credentials Location: Default Credentials Provider");
+ try {
+ final DefaultCredentialsProvider credentialsProvider = DefaultCredentialsProvider.builder().build();
+ return createDefaultClient(credentialsProvider);
+ } catch (final RuntimeException e) {
+ throw new SensitivePropertyProtectionException("AWS Client Builder Failed using Default Credentials Provider", e);
+ }
+ }
+ }
+
+ /**
+ * Create a client with the given credentials and region.
+ * @param credentials AWS credentials
+ * @param region AWS region
+ * @return The created client
+ */
+ protected abstract T createClient(AwsCredentials credentials, String region);
+
+ /**
+ * Create a default client with the given credentials provider.
+ * @param credentialsProvider AWS credentials provider
+ * @return The created client
+ */
+ protected abstract T createDefaultClient(AwsCredentialsProvider credentialsProvider);
+}
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/AwsKmsClientProvider.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/AwsKmsClientProvider.java
index 5e01fed969..d501735aca 100644
--- a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/AwsKmsClientProvider.java
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/AwsKmsClientProvider.java
@@ -16,65 +16,37 @@
*/
package org.apache.nifi.properties.configuration;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.properties.BootstrapProperties;
-import org.apache.nifi.properties.SensitivePropertyProtectionException;
-
-import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
-import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
+import software.amazon.awssdk.auth.credentials.AwsCredentials;
+import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.kms.KmsClient;
-import software.amazon.awssdk.services.kms.KmsClientBuilder;
-import java.util.Properties;
+import java.util.Collections;
+import java.util.Set;
/**
* Amazon Web Services Key Management Service Client Provider
*/
-public class AwsKmsClientProvider extends BootstrapPropertiesClientProvider {
- private static final String ACCESS_KEY_PROPS_NAME = "aws.access.key.id";
+public class AwsKmsClientProvider extends AbstractAwsClientProvider {
- private static final String SECRET_KEY_PROPS_NAME = "aws.secret.access.key";
+ protected static final String KEY_ID_PROPERTY = "aws.kms.key.id";
- private static final String REGION_KEY_PROPS_NAME = "aws.region";
-
- public AwsKmsClientProvider() {
- super(BootstrapProperties.BootstrapPropertyKey.AWS_KMS_SENSITIVE_PROPERTY_PROVIDER_CONF);
+ @Override
+ protected KmsClient createClient(final AwsCredentials credentials, final String region) {
+ return KmsClient.builder()
+ .credentialsProvider(StaticCredentialsProvider.create(credentials))
+ .region(Region.of(region))
+ .build();
}
- /**
- * Get Configured Client using either Client Properties or AWS Default Credentials Provider
- *
- * @param clientProperties Client Properties
- * @return KMS Client
- */
@Override
- protected KmsClient getConfiguredClient(final Properties clientProperties) {
- final String accessKey = clientProperties.getProperty(ACCESS_KEY_PROPS_NAME);
- final String secretKey = clientProperties.getProperty(SECRET_KEY_PROPS_NAME);
- final String region = clientProperties.getProperty(REGION_KEY_PROPS_NAME);
+ protected KmsClient createDefaultClient(final AwsCredentialsProvider credentialsProvider) {
+ return KmsClient.builder().credentialsProvider(credentialsProvider).build();
+ }
- final KmsClientBuilder kmsClientBuilder = KmsClient.builder();
- if (StringUtils.isNoneBlank(accessKey, secretKey, region)) {
- logger.debug("AWS Credentials Location: Client Properties");
- try {
- final AwsBasicCredentials credentials = AwsBasicCredentials.create(accessKey, secretKey);
- return kmsClientBuilder
- .region(Region.of(region))
- .credentialsProvider(StaticCredentialsProvider.create(credentials))
- .build();
- } catch (final RuntimeException e) {
- throw new SensitivePropertyProtectionException("AWS KMS Client Builder Failed using Client Properties", e);
- }
- } else {
- logger.debug("AWS Credentials Location: Default Credentials Provider");
- try {
- final DefaultCredentialsProvider credentialsProvider = DefaultCredentialsProvider.builder().build();
- return kmsClientBuilder.credentialsProvider(credentialsProvider).build();
- } catch (final RuntimeException e) {
- throw new SensitivePropertyProtectionException("AWS KMS Client Builder Failed using Default Credentials Provider", e);
- }
- }
+ @Override
+ protected Set getRequiredPropertyNames() {
+ return Collections.singleton(KEY_ID_PROPERTY);
}
}
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/AwsSecretsManagerClientProvider.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/AwsSecretsManagerClientProvider.java
new file mode 100644
index 0000000000..d0863ddfda
--- /dev/null
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/AwsSecretsManagerClientProvider.java
@@ -0,0 +1,50 @@
+/*
+ * 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.properties.configuration;
+
+import software.amazon.awssdk.auth.credentials.AwsCredentials;
+import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
+import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Amazon Web Services Secrets Manager Client Provider
+ */
+public class AwsSecretsManagerClientProvider extends AbstractAwsClientProvider {
+
+ @Override
+ protected SecretsManagerClient createClient(final AwsCredentials credentials, final String region) {
+ return SecretsManagerClient.builder()
+ .credentialsProvider(StaticCredentialsProvider.create(credentials))
+ .region(Region.of(region))
+ .build();
+ }
+
+ @Override
+ protected SecretsManagerClient createDefaultClient(final AwsCredentialsProvider credentialsProvider) {
+ return SecretsManagerClient.builder().credentialsProvider(credentialsProvider).build();
+ }
+
+ @Override
+ protected Set getRequiredPropertyNames() {
+ return Collections.emptySet();
+ }
+}
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/AzureClientProvider.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/AzureClientProvider.java
index 61561e7864..bc48e45bbe 100644
--- a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/AzureClientProvider.java
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/AzureClientProvider.java
@@ -19,11 +19,6 @@ package org.apache.nifi.properties.configuration;
import com.azure.core.credential.TokenCredential;
import com.azure.identity.DefaultAzureCredentialBuilder;
import org.apache.nifi.properties.BootstrapProperties;
-import org.apache.nifi.util.StringUtils;
-
-import java.util.Optional;
-import java.util.Properties;
-import java.util.Set;
/**
* Abstract Microsoft Azure Client Provider
@@ -33,17 +28,6 @@ public abstract class AzureClientProvider extends BootstrapPropertiesClientPr
super(BootstrapProperties.BootstrapPropertyKey.AZURE_KEYVAULT_SENSITIVE_PROPERTY_PROVIDER_CONF);
}
- /**
- * Get Client using Client Properties
- *
- * @param clientProperties Client Properties can be null
- * @return Configured Client or empty when Client Properties object is null
- */
- @Override
- public Optional getClient(final Properties clientProperties) {
- return isMissingProperties(clientProperties) ? Optional.empty() : Optional.of(getConfiguredClient(clientProperties));
- }
-
/**
* Get Default Azure Token Credential using Default Credentials Builder for environment variables and system properties
*
@@ -52,17 +36,4 @@ public abstract class AzureClientProvider extends BootstrapPropertiesClientPr
protected TokenCredential getDefaultTokenCredential() {
return new DefaultAzureCredentialBuilder().build();
}
-
- /**
- * Get Property Names required for initializing client in order to perform initial validation
- *
- * @return Set of required client property names
- */
- protected abstract Set getRequiredPropertyNames();
-
- private boolean isMissingProperties(final Properties clientProperties) {
- return clientProperties == null || getRequiredPropertyNames().stream().anyMatch(propertyName ->
- StringUtils.isBlank(clientProperties.getProperty(propertyName))
- );
- }
}
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/BootstrapPropertiesClientProvider.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/BootstrapPropertiesClientProvider.java
index 1bbb021a2a..b3bfbd7d56 100644
--- a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/BootstrapPropertiesClientProvider.java
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/BootstrapPropertiesClientProvider.java
@@ -30,6 +30,7 @@ import java.nio.file.Paths;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
+import java.util.Set;
/**
* Shared Client Provider for reading Client Properties from file referenced in configured Bootstrap Property Key
@@ -53,7 +54,7 @@ public abstract class BootstrapPropertiesClientProvider implements ClientProv
*/
@Override
public Optional getClient(final Properties clientProperties) {
- return clientProperties == null ? Optional.empty() : Optional.of(getConfiguredClient(clientProperties));
+ return isMissingProperties(clientProperties) ? Optional.empty() : Optional.of(getConfiguredClient(clientProperties));
}
/**
@@ -96,4 +97,17 @@ public abstract class BootstrapPropertiesClientProvider implements ClientProv
* @return Configured Client
*/
protected abstract T getConfiguredClient(final Properties clientProperties);
+
+ /**
+ * Get Property Names required for initializing client in order to perform initial validation
+ *
+ * @return Set of required client property names
+ */
+ protected abstract Set getRequiredPropertyNames();
+
+ private boolean isMissingProperties(final Properties clientProperties) {
+ return clientProperties == null || getRequiredPropertyNames().stream().anyMatch(propertyName ->
+ StringUtils.isBlank(clientProperties.getProperty(propertyName))
+ );
+ }
}
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/GoogleKeyManagementServiceClientProvider.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/GoogleKeyManagementServiceClientProvider.java
index 8f46edc06e..2bb707d09d 100644
--- a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/GoogleKeyManagementServiceClientProvider.java
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/configuration/GoogleKeyManagementServiceClientProvider.java
@@ -22,7 +22,9 @@ import org.apache.nifi.properties.BootstrapProperties;
import org.apache.nifi.properties.SensitivePropertyProtectionException;
import java.io.IOException;
+import java.util.Collections;
import java.util.Properties;
+import java.util.Set;
/**
* Google Key Management Service Client Provider
@@ -46,4 +48,9 @@ public class GoogleKeyManagementServiceClientProvider extends BootstrapPropertie
throw new SensitivePropertyProtectionException("Google Key Management Service Create Failed", e);
}
}
+
+ @Override
+ protected Set getRequiredPropertyNames() {
+ return Collections.emptySet();
+ }
}
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/test/java/org/apache/nifi/properties/AwsKmsSensitivePropertyProviderIT.java b/nifi-commons/nifi-sensitive-property-provider/src/test/java/org/apache/nifi/properties/AwsKmsSensitivePropertyProviderIT.java
index 708f54e1ab..30c183c8d0 100644
--- a/nifi-commons/nifi-sensitive-property-provider/src/test/java/org/apache/nifi/properties/AwsKmsSensitivePropertyProviderIT.java
+++ b/nifi-commons/nifi-sensitive-property-provider/src/test/java/org/apache/nifi/properties/AwsKmsSensitivePropertyProviderIT.java
@@ -61,7 +61,7 @@ public class AwsKmsSensitivePropertyProviderIT {
private static final String REGION_KEY_PROPS_NAME = "aws.region";
private static final String KMS_KEY_PROPS_NAME = "aws.kms.key.id";
- private static final String BOOTSTRAP_AWS_FILE_PROPS_NAME = "nifi.bootstrap.protection.aws.kms.conf";
+ private static final String BOOTSTRAP_AWS_FILE_PROPS_NAME = "nifi.bootstrap.protection.aws.conf";
private static final String EMPTY_PROPERTY = "";
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/test/java/org/apache/nifi/properties/AwsSecretsManagerSensitivePropertyProviderIT.java b/nifi-commons/nifi-sensitive-property-provider/src/test/java/org/apache/nifi/properties/AwsSecretsManagerSensitivePropertyProviderIT.java
new file mode 100644
index 0000000000..f60a80a747
--- /dev/null
+++ b/nifi-commons/nifi-sensitive-property-provider/src/test/java/org/apache/nifi/properties/AwsSecretsManagerSensitivePropertyProviderIT.java
@@ -0,0 +1,130 @@
+/*
+ * 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.properties;
+
+import org.apache.nifi.properties.configuration.AwsSecretsManagerClientProvider;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.internal.util.io.IOUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Properties;
+
+/**
+ * To run this test, make sure to first configure sensitive credential information as in the following link
+ * https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html
+ *
+ * The following settings are optional. If you have a default AWS configuration and credentials in ~/.aws then
+ * it will take that. Otherwise you can set all of the following:
+ * set the system property -Daws.access.key.id to the access key id
+ * set the system property -Daws.secret.access.key to the secret access key
+ * set the system property -Daws.region to the region
+ *
+ * After you are satisfied with the test, and you don't need the key, you may schedule secret deletion with:
+ * aws secretsmanager delete-secret --secret-id "default/property" --recovery-window-in-days 14
+ *
+ */
+
+public class AwsSecretsManagerSensitivePropertyProviderIT {
+ private static final String SAMPLE_PLAINTEXT = "AWSSecretsManagerSensitivePropertyProviderIT SAMPLE-PLAINTEXT";
+ private static final String ACCESS_KEY_PROPS_NAME = "aws.access.key.id";
+ private static final String SECRET_KEY_PROPS_NAME = "aws.secret.access.key";
+ private static final String REGION_KEY_PROPS_NAME = "aws.region";
+
+ private static final String BOOTSTRAP_AWS_FILE_PROPS_NAME = "nifi.bootstrap.protection.aws.conf";
+
+ private static final String EMPTY_PROPERTY = "";
+
+ private static AwsSecretsManagerSensitivePropertyProvider spp;
+
+ private static BootstrapProperties props;
+
+ private static Path mockBootstrapConf, mockAWSBootstrapConf;
+
+ private static final Logger logger = LoggerFactory.getLogger(AwsSecretsManagerSensitivePropertyProviderIT.class);
+
+ private static void initializeBootstrapProperties() throws IOException{
+ mockBootstrapConf = Files.createTempFile("bootstrap", ".conf").toAbsolutePath();
+ mockAWSBootstrapConf = Files.createTempFile("bootstrap-aws", ".conf").toAbsolutePath();
+ IOUtil.writeText(BOOTSTRAP_AWS_FILE_PROPS_NAME + "=" + mockAWSBootstrapConf.toAbsolutePath(), mockBootstrapConf.toFile());
+
+ final Properties bootstrapProperties = new Properties();
+ try (final InputStream inputStream = Files.newInputStream(mockBootstrapConf)) {
+ bootstrapProperties.load(inputStream);
+ props = new BootstrapProperties("nifi", bootstrapProperties, mockBootstrapConf);
+ }
+
+ String accessKey = System.getProperty(ACCESS_KEY_PROPS_NAME, EMPTY_PROPERTY);
+ String secretKey = System.getProperty(SECRET_KEY_PROPS_NAME, EMPTY_PROPERTY);
+ String region = System.getProperty(REGION_KEY_PROPS_NAME, EMPTY_PROPERTY);
+
+ StringBuilder bootstrapConfText = new StringBuilder();
+ String lineSeparator = System.getProperty("line.separator");
+ bootstrapConfText.append(ACCESS_KEY_PROPS_NAME + "=" + accessKey);
+ bootstrapConfText.append(lineSeparator + SECRET_KEY_PROPS_NAME + "=" + secretKey);
+ bootstrapConfText.append(lineSeparator + REGION_KEY_PROPS_NAME + "=" + region);
+
+ IOUtil.writeText(bootstrapConfText.toString(), mockAWSBootstrapConf.toFile());
+ }
+
+ @BeforeClass
+ public static void initOnce() throws IOException {
+ initializeBootstrapProperties();
+ Assert.assertNotNull(props);
+ final AwsSecretsManagerClientProvider provider = new AwsSecretsManagerClientProvider();
+ final Properties properties = provider.getClientProperties(props).orElse(null);
+ final SecretsManagerClient secretsManagerClient = provider.getClient(properties).orElse(null);
+ spp = new AwsSecretsManagerSensitivePropertyProvider(secretsManagerClient);
+ Assert.assertNotNull(spp);
+ }
+
+ @AfterClass
+ public static void tearDownOnce() throws IOException {
+ Files.deleteIfExists(mockBootstrapConf);
+ Files.deleteIfExists(mockAWSBootstrapConf);
+
+ spp.cleanUp();
+ }
+
+ @Test
+ public void testEncryptDecrypt() {
+ logger.info("Running testEncryptDecrypt of AWS Secrets Manager SPP integration test");
+ runEncryptDecryptTest();
+ logger.info("testEncryptDecrypt of AWS Secrets Manager SPP integration test completed");
+ }
+
+ private static void runEncryptDecryptTest() {
+ final String propertyName = "property2";
+ logger.info("Plaintext: " + SAMPLE_PLAINTEXT);
+ String protectedValue = spp.protect(SAMPLE_PLAINTEXT, ProtectedPropertyContext.defaultContext(propertyName));
+ logger.info("Protected Value: " + protectedValue);
+ String unprotectedValue = spp.unprotect(protectedValue, ProtectedPropertyContext.defaultContext(propertyName));
+ logger.info("Unprotected Value: " + unprotectedValue);
+
+ Assert.assertEquals(SAMPLE_PLAINTEXT, unprotectedValue);
+ Assert.assertNotEquals(SAMPLE_PLAINTEXT, protectedValue);
+ Assert.assertNotEquals(protectedValue, unprotectedValue);
+ }
+}
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/test/java/org/apache/nifi/properties/AwsSecretsManagerSensitivePropertyProviderTest.java b/nifi-commons/nifi-sensitive-property-provider/src/test/java/org/apache/nifi/properties/AwsSecretsManagerSensitivePropertyProviderTest.java
new file mode 100644
index 0000000000..93addaa269
--- /dev/null
+++ b/nifi-commons/nifi-sensitive-property-provider/src/test/java/org/apache/nifi/properties/AwsSecretsManagerSensitivePropertyProviderTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.properties;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import software.amazon.awssdk.core.SdkBytes;
+import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
+import software.amazon.awssdk.services.secretsmanager.model.CreateSecretResponse;
+import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse;
+import software.amazon.awssdk.services.secretsmanager.model.PutSecretValueResponse;
+import software.amazon.awssdk.services.secretsmanager.model.ResourceNotFoundException;
+
+import java.nio.charset.Charset;
+import java.util.function.Consumer;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class AwsSecretsManagerSensitivePropertyProviderTest {
+ private static final String PROPERTY_NAME = "propertyName";
+ private static final String PROPERTY_VALUE = "propertyValue";
+
+ private static final String SECRET_STRING = String.format("{ \"%s\": \"%s\" }", PROPERTY_NAME, PROPERTY_VALUE);
+
+ @Mock
+ private SecretsManagerClient secretsManagerClient;
+
+ private AwsSecretsManagerSensitivePropertyProvider provider;
+
+ @BeforeEach
+ public void setProvider() {
+ provider = new AwsSecretsManagerSensitivePropertyProvider(secretsManagerClient);
+ }
+
+ @Test
+ public void testValidateClientNull() {
+ final AwsSecretsManagerSensitivePropertyProvider provider = new AwsSecretsManagerSensitivePropertyProvider(null);
+ assertNotNull(provider);
+ }
+
+ @Test
+ public void testValidateKeyNoSecretString() {
+ final GetSecretValueResponse getSecretValueResponse = GetSecretValueResponse.builder()
+ .secretBinary(SdkBytes.fromString("binary", Charset.defaultCharset())).build();
+ when(secretsManagerClient.getSecretValue(any(Consumer.class))).thenReturn(getSecretValueResponse);
+
+ assertThrows(SensitivePropertyProtectionException.class, () ->
+ provider.unprotect("anyValue", ProtectedPropertyContext.defaultContext(PROPERTY_NAME)));
+ }
+
+ @Test
+ public void testCleanUp() {
+ provider.cleanUp();
+ verify(secretsManagerClient).close();
+ }
+
+ @Test
+ public void testProtectCreateSecret() {
+ final String secretName = ProtectedPropertyContext.defaultContext(PROPERTY_NAME).getContextKey();
+
+ when(secretsManagerClient.getSecretValue(any(Consumer.class))).thenThrow(ResourceNotFoundException.builder().message("Not found").build());
+
+ final CreateSecretResponse createSecretResponse = CreateSecretResponse.builder()
+ .name(secretName).build();
+ when(secretsManagerClient.createSecret(any(Consumer.class))).thenReturn(createSecretResponse);
+
+ final String protectedProperty = provider.protect(PROPERTY_VALUE, ProtectedPropertyContext.defaultContext(PROPERTY_NAME));
+ assertEquals(secretName, protectedProperty);
+ }
+
+ @Test
+ public void testProtectExistingSecret() {
+ final String secretName = ProtectedPropertyContext.defaultContext(PROPERTY_NAME).getContextKey();
+ final GetSecretValueResponse getSecretValueResponse = GetSecretValueResponse.builder().secretString(SECRET_STRING).build();
+ when(secretsManagerClient.getSecretValue(any(Consumer.class))).thenReturn(getSecretValueResponse);
+
+ final PutSecretValueResponse putSecretValueResponse = PutSecretValueResponse.builder()
+ .name(secretName).build();
+ when(secretsManagerClient.putSecretValue(any(Consumer.class))).thenReturn(putSecretValueResponse);
+
+ final String protectedProperty = provider.protect(PROPERTY_VALUE, ProtectedPropertyContext.defaultContext(PROPERTY_NAME));
+ assertEquals(secretName, protectedProperty);
+ }
+
+ @Test
+ public void testUnprotect() {
+ final GetSecretValueResponse getSecretValueResponse = GetSecretValueResponse.builder().secretString(SECRET_STRING).build();
+ when(secretsManagerClient.getSecretValue(any(Consumer.class))).thenReturn(getSecretValueResponse);
+
+ final String property = provider.unprotect("anyValue", ProtectedPropertyContext.defaultContext(PROPERTY_NAME));
+ assertEquals(PROPERTY_VALUE, property);
+ }
+}
diff --git a/nifi-docs/src/main/asciidoc/administration-guide.adoc b/nifi-docs/src/main/asciidoc/administration-guide.adoc
index 18b72470ac..6e02e5e363 100644
--- a/nifi-docs/src/main/asciidoc/administration-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/administration-guide.adoc
@@ -1797,6 +1797,7 @@ For more information, see the <` The _flow.xml.gz_ file currently protected with old password (will be overwritten unless `-g` is specified)
* `-g`,`--outputFlowXml ` The destination _flow.xml.gz_ file containing protected config values (will not modify input _flow.xml.gz_)
* `-b`,`--bootstrapConf ` The bootstrap.conf file to persist root key and to optionally provide any configuration for the protection scheme.
-* `-S`,`--protectionScheme ` Selects the protection scheme for encrypted properties. Valid values are: [<>, <>, <>, <>, <>, <>, <>] (default is AES_GCM)
+* `-S`,`--protectionScheme ` Selects the protection scheme for encrypted properties. Valid values are: [<>, <>, <>, <>, <>, <>, <>, <>] (default is AES_GCM)
* `-k`,`--key ` The raw hexadecimal key to use to encrypt the sensitive properties
* `-e`,`--oldKey ` The old raw hexadecimal key to use during key migration
* `-H`,`--oldProtectionScheme ` The old protection scheme to use during encryption migration (see --protectionScheme for possible values). Default is AES_GCM
@@ -456,7 +456,7 @@ The following are available options when targeting NiFi Registry using the `--ni
* `-v`,`--verbose` Sets verbose mode (default false)
* `-p`,`--password ` Protect the files using a password-derived key. If an argument is not provided to this flag, interactive mode will be triggered to prompt the user to enter the password.
* `-k`,`--key ` Protect the files using a raw hexadecimal key. If an argument is not provided to this flag, interactive mode will be triggered to prompt the user to enter the key.
-* `-S`,`--protectionScheme ` Selects the protection scheme for encrypted properties. Valid values are: [<>, <>, <>, <>, <>, <>, <>] (default is AES_GCM)
+* `-S`,`--protectionScheme ` Selects the protection scheme for encrypted properties. Valid values are: [<>, <>, <>, <>, <>, <>, <>, <>] (default is AES_GCM)
* `--oldPassword ` If the input files are already protected using a password-derived key, this specifies the old password so that the files can be unprotected before re-protecting.
* `--oldKey ` If the input files are already protected using a key, this specifies the raw hexadecimal key so that the files can be unprotected before re-protecting.
* `-H`,`--oldProtectionScheme `The old protection scheme to use during encryption migration (see --protectionScheme for possible values). Default is AES_GCM.
@@ -477,13 +477,16 @@ The protection scheme can be selected during encryption using the `--protectionS
The default protection scheme, `AES-G/CM` simply encrypts sensitive properties and marks their protection as either `aes/gcm/256` or `aes/gcm/256` as appropriate. This protection is all done within NiFi itself.
==== HASHICORP_VAULT_TRANSIT [[HASHICORP_VAULT_TRANSIT]]
-This protection scheme uses https://www.vaultproject.io/docs/secrets/transit[HashiCorp Vault Transit Secrets Engine] to outsource encryption to a configured Vault server. All HashiCorp Vault configuration is stored in the `bootstrap-hashicorp-vault.conf` file, as referenced in the `bootstrap.conf` of a NiFi or NiFi Registry instance. Therefore, when using the HASHICORP_VAULT_TRANSIT protection scheme, the `nifi(.registry)?.bootstrap.protection.hashicorp.vault.conf` property in the `bootstrap.conf` specified using the `-b` flag must be available to the Encrypt Configuration Tool and must be configured as described in the <> section in the link:administration-guide.html[NiFi Administration Guide].
+This protection scheme uses https://www.vaultproject.io/docs/secrets/transit[HashiCorp Vault Transit Secrets Engine] to outsource encryption to a configured Vault server. All HashiCorp Vault configuration is stored in the `bootstrap-hashicorp-vault.conf` file, as referenced in the `bootstrap.conf` of a NiFi or NiFi Registry instance. Therefore, when using the HASHICORP_VAULT_TRANSIT protection scheme, the `nifi(.registry)?.bootstrap.protection.hashicorp.vault.conf` property in the `bootstrap.conf` specified using the `-b` flag must be available to the Encrypt Configuration Tool and must be configured as described in the <> section in the link:administration-guide.html[NiFi Administration Guide].
==== HASHICORP_VAULT_KV [[HASHICORP_VAULT_KV]]
-This protection scheme uses https://www.vaultproject.io/docs/secrets/kv/kv-v1[HashiCorp Vault Key Value Secrets Engine Version 1] to store sensitive values as Vault Secrets. All HashiCorp Vault configuration is stored in the `bootstrap-hashicorp-vault.conf` file, as referenced in the `bootstrap.conf` of a NiFi or NiFi Registry instance. Therefore, when using the HASHICORP_VAULT_KV protection scheme, the `nifi(.registry)?.bootstrap.protection.hashicorp.vault.conf` property in the `bootstrap.conf` specified using the `-b` flag must be available to the Encrypt Configuration Tool and must be configured as described in the <> section in the link:administration-guide.html[NiFi Administration Guide].
+This protection scheme uses https://www.vaultproject.io/docs/secrets/kv/kv-v1[HashiCorp Vault Key Value Secrets Engine Version 1] to store sensitive values as Vault Secrets. All HashiCorp Vault configuration is stored in the `bootstrap-hashicorp-vault.conf` file, as referenced in the `bootstrap.conf` of a NiFi or NiFi Registry instance. Therefore, when using the HASHICORP_VAULT_KV protection scheme, the `nifi(.registry)?.bootstrap.protection.hashicorp.vault.conf` property in the `bootstrap.conf` specified using the `-b` flag must be available to the Encrypt Configuration Tool and must be configured as described in the <> section in the link:administration-guide.html[NiFi Administration Guide].
==== AWS_KMS [[AWS_KMS]]
-This protection scheme uses https://aws.amazon.com/kms/[AWS Key Management] Service for encryption and decryption. AWS KMS configuration properties can be stored in the `bootstrap-aws.conf` file, as referenced in the `bootstrap.conf` of NiFi or NiFi Registry. If the configuration properties are not specified in `bootstrap-aws.conf`, then the provider will attempt to use the AWS default credentials provider, which checks standard environment variables and system properties. Therefore, when using the AWS_KMS protection scheme, the `nifi(.registry)?.bootstrap.protection.aws.kms.conf` property in the `bootstrap.conf` specified using the `-b` flag must be available to the Encrypt Configuration Tool and must be configured as described in the <> section in the link:administration-guide.html[NiFi Administration Guide].
+This protection scheme uses https://aws.amazon.com/kms/[AWS Key Management] Service for encryption and decryption. AWS KMS configuration properties can be stored in the `bootstrap-aws.conf` file, as referenced in the `bootstrap.conf` of NiFi or NiFi Registry. If the configuration properties are not specified in `bootstrap-aws.conf`, then the provider will attempt to use the AWS default credentials provider, which checks standard environment variables and system properties. Therefore, when using the AWS_KMS protection scheme, the `nifi(.registry)?.bootstrap.protection.aws.conf` property in the `bootstrap.conf` specified using the `-b` flag must be available to the Encrypt Configuration Tool and must be configured as described in the <> section in the link:administration-guide.html[NiFi Administration Guide].
+
+==== AWS_SECRETSMANAGER [[AWS_SECRETSMANAGER]]
+This protection scheme uses https://aws.amazon.com/secrets-manager/[AWS Secrets Manager] Service to store sensitive values as AWS Secrets. AWS Secrets Manager configuration properties can be stored in the `bootstrap-aws.conf` file, as referenced in the `bootstrap.conf` of NiFi or NiFi Registry. If the configuration properties are not specified in `bootstrap-aws.conf`, then the provider will attempt to use the AWS default credentials provider, which checks standard environment variables and system properties. Therefore, when using the AWS_SECRETS_MANAGER protection scheme, the `nifi(.registry)?.bootstrap.protection.aws.conf` property in the `bootstrap.conf` specified using the `-b` flag must be available to the Encrypt Configuration Tool and must be configured as described in the <> section in the link:administration-guide.html[NiFi Administration Guide].
==== Microsoft Azure Key Vault Sensitive Property Providers
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/bootstrap.conf b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/bootstrap.conf
index 33843d7719..5664cfa0a0 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/bootstrap.conf
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/bootstrap.conf
@@ -63,8 +63,8 @@ nifi.bootstrap.sensitive.key=
# HashiCorp Vault Sensitive Property Providers
#nifi.bootstrap.protection.hashicorp.vault.conf=./conf/bootstrap-hashicorp-vault.conf
-# AWS KMS Sensitive Property Providers
-#nifi.bootstrap.protection.aws.kms.conf=./conf/bootstrap-aws.conf
+# AWS Sensitive Property Providers
+#nifi.bootstrap.protection.aws.conf=./conf/bootstrap-aws.conf
# Azure Key Vault Sensitive Property Providers
#nifi.bootstrap.protection.azure.keyvault.conf=./conf/bootstrap-azure.conf
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/bootstrap.conf b/nifi-registry/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/bootstrap.conf
index 997a05ee0d..0fc45d5d44 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/bootstrap.conf
+++ b/nifi-registry/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/bootstrap.conf
@@ -58,8 +58,8 @@ nifi.registry.bootstrap.sensitive.key=
# HashiCorp Vault Sensitive Property Providers
#nifi.registry.bootstrap.protection.hashicorp.vault.conf=./conf/bootstrap-hashicorp-vault.conf
-# AWS KMS Sensitive Property Providers
-#nifi.registry.bootstrap.protection.aws.kms.conf=./conf/bootstrap-aws.conf
+# AWS Sensitive Property Providers
+#nifi.registry.bootstrap.protection.aws.conf=./conf/bootstrap-aws.conf
# Azure Key Vault Sensitive Property Providers
#nifi.registry.bootstrap.protection.azure.keyvault.conf=./conf/bootstrap-azure.conf