Skip to content
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
6 changes: 6 additions & 0 deletions minifi/minifi-assembly/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,12 @@ limitations under the License.
<version>2.12.0-SNAPSHOT</version>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-password-based-property-encryption-nar</artifactId>
<version>2.12.0-SNAPSHOT</version>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-record-serialization-services-nar</artifactId>
Expand Down
5 changes: 0 additions & 5 deletions minifi/minifi-commons/minifi-commons-framework/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,6 @@ limitations under the License.
<artifactId>nifi-property-encryptor</artifactId>
<version>2.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-core</artifactId>
<version>2.12.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
import org.apache.nifi.c2.protocol.component.api.PropertyDescriptor;
import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
import org.apache.nifi.controller.flow.VersionedDataflow;
import org.apache.nifi.controller.serialization.FlowSerializer;
import org.apache.nifi.encrypt.PropertyEncryptor;
import org.apache.nifi.flow.VersionedConfigurableExtension;
import org.apache.nifi.flow.VersionedParameter;
import org.apache.nifi.flow.VersionedProcessGroup;
import org.apache.nifi.flow.VersionedPropertyDescriptor;
import org.apache.nifi.security.encryption.PropertyEncryptionEncoder;

import java.util.List;
import java.util.Map;
Expand All @@ -41,12 +41,9 @@
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import static java.util.stream.Stream.concat;
import static org.apache.commons.lang3.StringUtils.EMPTY;

public class StandardFlowPropertyEncryptor implements FlowPropertyEncryptor {

private static final String ENCRYPTED_FORMAT = "enc{%s}";

private final PropertyEncryptor propertyEncryptor;
private final RuntimeManifest runTimeManifest;

Expand All @@ -72,7 +69,7 @@ private void encryptParameterContextsProperties(VersionedDataflow flow) {
.forEach(parameterContext -> ofNullable(parameterContext.getParameters()).orElse(Set.of())
.stream()
.filter(VersionedParameter::isSensitive)
.filter(not(parameter -> ofNullable(parameter.getValue()).orElse(EMPTY).startsWith(FlowSerializer.ENC_PREFIX)))
.filter(not(parameter -> PropertyEncryptionEncoder.isEncrypted(parameter.getValue())))
.forEach(parameter -> parameter.setValue(encrypt(parameter.getValue()))));
}

Expand Down Expand Up @@ -150,13 +147,13 @@ private Set<String> mergeSets(Set<String> first, Set<String> second) {

private Function<Entry<String, String>, String> encryptPropertyIfNeeded(Set<String> sensitivePropertyNames) {
return entry ->
sensitivePropertyNames.contains(entry.getKey()) && !entry.getValue().startsWith(FlowSerializer.ENC_PREFIX)
sensitivePropertyNames.contains(entry.getKey()) && !PropertyEncryptionEncoder.isEncrypted(entry.getValue())
? encrypt(entry.getValue())
: entry.getValue();
}

private String encrypt(String parameter) {
return String.format(ENCRYPTED_FORMAT, propertyEncryptor.encrypt(parameter));
return PropertyEncryptionEncoder.getEncoded(propertyEncryptor.encrypt(parameter));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.apache.nifi.c2.protocol.component.api.PropertyDescriptor;
import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
import org.apache.nifi.controller.flow.VersionedDataflow;
import org.apache.nifi.controller.serialization.FlowSerializer;
import org.apache.nifi.encrypt.PropertyEncryptor;
import org.apache.nifi.flow.VersionedConfigurableExtension;
import org.apache.nifi.flow.VersionedControllerService;
Expand All @@ -34,6 +33,7 @@
import org.apache.nifi.flow.VersionedProcessGroup;
import org.apache.nifi.flow.VersionedProcessor;
import org.apache.nifi.flow.VersionedPropertyDescriptor;
import org.apache.nifi.security.encryption.PropertyEncryptionEncoder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand Down Expand Up @@ -126,9 +126,9 @@ public void shouldEncryptParameterContextsSensitiveVariables() {
.flatMap(context -> context.getParameters().stream())
.forEach(parameter -> {
if (parameter.isSensitive()) {
assertTrue(parameter.getValue().startsWith(FlowSerializer.ENC_PREFIX));
assertTrue(PropertyEncryptionEncoder.isEncrypted(parameter.getValue()));
} else {
assertFalse(parameter.getValue().startsWith(FlowSerializer.ENC_PREFIX));
assertFalse(PropertyEncryptionEncoder.isEncrypted(parameter.getValue()));
}
});
}
Expand Down Expand Up @@ -310,9 +310,9 @@ private void assertSensitiveFlowComponentPropertiesAreEncoded(VersionedDataflow
.flatMap(properties -> properties.entrySet().stream())
.forEach(propertyEntry -> {
if (propertyEntry.getKey().startsWith(SENSITIVE_PROPERTY_NAME_PREFIX)) {
assertTrue(propertyEntry.getValue().startsWith(FlowSerializer.ENC_PREFIX));
assertTrue(PropertyEncryptionEncoder.isEncrypted(propertyEntry.getValue()));
} else {
assertFalse(propertyEntry.getValue().startsWith(FlowSerializer.ENC_PREFIX));
assertFalse(PropertyEncryptionEncoder.isEncrypted(propertyEntry.getValue()));
}
});
}
Expand Down
5 changes: 0 additions & 5 deletions nifi-connector-mock-bundle/nifi-connector-mock-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,6 @@
<version>2.12.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-property-encryptor</artifactId>
<version>2.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-connector-mock-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,25 @@

package org.apache.nifi.mock.connector.server;

import org.apache.nifi.encrypt.PropertyEncryptor;
import org.apache.nifi.security.encryption.PropertyEncryptionProvider;
import org.apache.nifi.security.encryption.PropertyEncryptionProviderInitializationContext;
import org.apache.nifi.security.encryption.SensitivePropertyContext;

/**
* Property Encryption Provider that returns values unchanged, because the mock server does not persist the flow.
*/
public class MockPropertyEncryptionProvider implements PropertyEncryptionProvider {
@Override
public void initialize(final PropertyEncryptionProviderInitializationContext context) {
}

public class NopPropertyEncryptor implements PropertyEncryptor {
@Override
public String encrypt(final String property) {
public byte[] encrypt(final byte[] property, final SensitivePropertyContext context) {
return property;
}

@Override
public String decrypt(final String encryptedProperty) {
public byte[] decrypt(final byte[] encryptedProperty, final SensitivePropertyContext context) {
return encryptedProperty;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
import org.apache.nifi.controller.status.history.StatusHistoryRepository;
import org.apache.nifi.controller.status.history.VolatileComponentStatusRepository;
import org.apache.nifi.diagnostics.DiagnosticsFactory;
import org.apache.nifi.encrypt.PropertyEncryptor;
import org.apache.nifi.engine.FlowEngine;
import org.apache.nifi.events.VolatileBulletinRepository;
import org.apache.nifi.flow.VersionedExternalFlow;
Expand Down Expand Up @@ -137,7 +136,6 @@ public void start() {
final FlowFileEventRepository flowFileEventRepository = new RingBufferEventRepository(5);
final Authorizer authorizer = new PermitAllAuthorizer();
final AuditService auditService = new MockAuditService();
final PropertyEncryptor propertyEncryptor = new NopPropertyEncryptor();
final BulletinRepository bulletinRepository = new VolatileBulletinRepository();
final StatusHistoryRepository statusHistoryRepository = new VolatileComponentStatusRepository(nifiProperties);
final RuleViolationsManager ruleViolationManager = new MockRuleViolationsManager();
Expand All @@ -150,7 +148,7 @@ public void start() {
authorizer,
auditService,
new DefaultComponentMetricReporter(),
propertyEncryptor,
new MockPropertyEncryptionProvider(),
bulletinRepository,
extensionManager,
statusHistoryRepository,
Expand Down Expand Up @@ -515,7 +513,6 @@ private VersionedExternalFlow createFlowSnapshot(final FrameworkFlowContext flow
.mapSensitiveConfiguration(false)
.mapPropertyDescriptors(true)
.stateLookup(VersionedComponentStateLookup.ENABLED_OR_DISABLED)
.sensitiveValueEncryptor(value -> value)
.componentIdLookup(ComponentIdLookup.VERSIONED_OR_GENERATE)
.mapInstanceIdentifiers(true)
.mapControllerServiceReferencesToVersionedId(true)
Expand Down
37 changes: 32 additions & 5 deletions nifi-docs/src/main/asciidoc/administration-guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1928,23 +1928,50 @@ Clustered installations of NiFi require the same value to be configured on all n

A Property Encryption Provider is a framework extension, packaged in a NiFi Archive, for protecting sensitive values.
NiFi loads and initializes the provider during startup using the class name configured in
`nifi.security.property.encryption.provider.implementation`, and does not load a provider when the property is empty.
When the property is missing, NiFi loads the standard password-based implementation to support existing configurations.
`nifi.security.property.encryption.provider.implementation`. When the property is empty or missing, NiFi loads the
standard password-based implementation to support existing configurations.

The standard distribution includes `org.apache.nifi.security.encryption.password.PasswordBasedPropertyEncryptionProvider`,
which derives a secret key from `nifi.sensitive.props.key` using the Key Derivation Function selected in
`nifi.sensitive.props.algorithm`. This provider reads and writes the encrypted representation described in
<<property-encryption-algorithms>>.

Flow serialization continues to use `nifi.sensitive.props.key` and `nifi.sensitive.props.algorithm` directly.
Loading a Property Encryption Provider does not change the encryption used for persisted flow configuration.
The configured provider protects the following values:

* Sensitive component properties written to and read from the persisted flow configuration
* Sensitive Parameter values written to and read from the persisted flow configuration
* OpenID Connect authorized client tokens stored on behalf of authenticated users

Because the standard password-based provider produces the representation described in
<<property-encryption-algorithms>>, a flow configuration written by an earlier version of NiFi continues to load
without additional migration.

==== Sensitive Property Context

NiFi supplies a Sensitive Property Context with every encryption and decryption operation. The context carries a
category describing the kind of value being protected, together with attributes describing where the value is located.
Component properties supply the component identifier, the component type, and the property name. Parameters supply the
Parameter Context name and the Parameter name. Authorization tokens carry no attributes, because nothing describing the
authenticated user is known before the stored token is decrypted.

An implementation can supply the context to an external Key Management Service as encryption context, which binds the
encrypted value to its location in the flow and records the location for auditing. NiFi supplies the same context when
decrypting a value as it supplied when the value was encrypted. An implementation that binds the context to the
encrypted value must therefore be prepared for a value to become unreadable if the component or the Parameter that owns
it is renamed or recreated with a different identifier.

==== Provider Configuration

A provider can read additional configuration from properties that begin with
`nifi.security.property.encryption.provider.properties.`. NiFi removes the prefix before supplying the properties to the
provider, so `nifi.security.property.encryption.provider.properties.endpoint` is supplied to the provider as `endpoint`.
Providers that call remote services, such as implementations backed by an external Key Management Service, can also
use the TLS configuration that NiFi supplies during initialization.

Changing the configured provider, or changing the key material that a provider uses, makes existing encrypted values
unreadable. Sensitive values must be reentered after such a change unless the new configuration resolves to the same
key material.

[[admin-toolkit]]
== NiFi Toolkit Administrative Tools
The NiFi Toolkit also contains command line utilities for administrators to support NiFi maintenance in standalone and clustered environments.
Expand Down Expand Up @@ -3622,7 +3649,7 @@ These properties pertain to various security features in NiFi. Many of these pro
|*Property*|*Description*
|`nifi.sensitive.props.key`|This is the password used to encrypt any sensitive property values that are configured in processors. By default, it is blank, but the system administrator should provide a value for it. It can be a string of any length, although the recommended minimum length is 10 characters. Be aware that once this password is set and one or more sensitive processor properties have been configured, this password should not be changed.
|`nifi.sensitive.props.algorithm`|The algorithm used to encrypt sensitive properties. The default value is `NIFI_PBKDF2_AES_GCM_256`.
|`nifi.security.property.encryption.provider.implementation`|The class name of the Property Encryption Provider loaded and initialized during application startup. The default value is `org.apache.nifi.security.encryption.password.PasswordBasedPropertyEncryptionProvider`, which derives a secret key from `nifi.sensitive.props.key`. NiFi does not load a provider when the value is blank. See <<property-encryption-providers>> for additional information.
|`nifi.security.property.encryption.provider.implementation`|The class name of the Property Encryption Provider loaded and initialized during application startup. The provider protects sensitive component properties, sensitive Parameter values, and OpenID Connect authorized client tokens. The default value is `org.apache.nifi.security.encryption.password.PasswordBasedPropertyEncryptionProvider`, which derives a secret key from `nifi.sensitive.props.key`. NiFi loads the default implementation when the value is blank. See <<property-encryption-providers>> for additional information.
|`nifi.security.property.encryption.provider.properties.`|The prefix for properties supplied to the configured Property Encryption Provider. NiFi removes the prefix from the property name before supplying the property to the provider. No properties are configured by default.
|`nifi.security.autoreload.enabled`|Specifies whether the SSL context factory should be automatically reloaded if updates to the keystore and truststore are detected. By default, it is set to `false`.
|`nifi.security.autoreload.interval`|Specifies the interval at which the keystore and truststore are checked for updates. Only applies if `nifi.security.autoreload.enabled` is set to `true`. The default value is `10 secs`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,6 @@
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-nar-utils</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-property-encryptor</artifactId>
<version>2.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-xml-processing</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
import org.apache.nifi.registry.flow.mapping.FlowMappingOptions;
import org.apache.nifi.registry.flow.mapping.VersionedComponentFlowMapper;
import org.apache.nifi.registry.flow.mapping.VersionedComponentStateLookup;
import org.apache.nifi.security.encryption.PropertyEncryptionProvider;
import org.apache.nifi.security.encryption.PropertyEncryptionProviderInitializationContext;
import org.apache.nifi.security.encryption.SensitivePropertyContext;

import java.nio.charset.StandardCharsets;

public class FlowAnalysisUtil {
public static final String ENCRYPTED_SENSITIVE_VALUE_SUBSTITUTE = "*****";
Expand All @@ -32,7 +37,7 @@ public static VersionedComponentFlowMapper createMapper(ExtensionManager extensi
.stateLookup(VersionedComponentStateLookup.IDENTITY_LOOKUP)
.componentIdLookup(ComponentIdLookup.USE_COMPONENT_ID)
.mapSensitiveConfiguration(true)
.sensitiveValueEncryptor(value -> ENCRYPTED_SENSITIVE_VALUE_SUBSTITUTE)
.propertyEncryptionProvider(new PlaceholderPropertyEncryptionProvider())
.mapAssetReferences(true)
.build();

Expand All @@ -43,12 +48,29 @@ public String getGroupId(String groupId) {
}

@Override
protected String encrypt(String value) {
protected String encrypt(String value, SensitivePropertyContext context) {
return ENCRYPTED_SENSITIVE_VALUE_SUBSTITUTE;
}
};

return mapper;
}

private static class PlaceholderPropertyEncryptionProvider implements PropertyEncryptionProvider {

@Override
public void initialize(PropertyEncryptionProviderInitializationContext context) {

}

@Override
public byte[] encrypt(byte[] property, SensitivePropertyContext context) {
return ENCRYPTED_SENSITIVE_VALUE_SUBSTITUTE.getBytes(StandardCharsets.UTF_8);
}

@Override
public byte[] decrypt(byte[] encryptedProperty, SensitivePropertyContext context) {
return encryptedProperty;
}
}
}
Loading
Loading