NIFI-3024 Added key migration for sensitive processor properties contained in flow.xml.gz. (nifi.sensitive.props.key)

This closes #1261.

Signed-off-by: Andy LoPresto <alopresto@apache.org>
This commit is contained in:
Andy LoPresto 2016-11-21 21:19:18 -08:00
parent cdb9b81f42
commit 2c3714536f
No known key found for this signature in database
GPG Key ID: 3C6EF65B2F7DEF69
13 changed files with 1960 additions and 83 deletions

View File

@ -1002,19 +1002,25 @@ The default encryption algorithm utilized is AES/GCM 128/256-bit. 128-bit is use
You can use the following command line options with the `encrypt-config` tool:
* `-b,--bootstrapConf <arg>` The bootstrap.conf file to persist master key
* `-e,--oldKey <arg>` The old raw hexadecimal key to use during key migration
* `-h,--help` Prints this usage message
* `-k,--key <arg>` The raw hexadecimal key to use to encrypt the sensitive properties
* `-m,--migrate` If provided, the sensitive properties will be re-encrypted with a new key
* `-n,--niFiProperties <arg>` The nifi.properties file containing unprotected config values (will be overwritten)
* `-o,--outputNiFiProperties <arg>` The destination nifi.properties file containing protected config values (will not modify input nifi.properties)
* `-p,--password <arg>` The password from which to derive the key to use to encrypt the sensitive properties
* `-r,--useRawKey` If provided, the secure console will prompt for the raw key value in hexadecimal form
* `-v,--verbose` Sets verbose mode (default false)
* `-w,--oldPassword <arg>` The old password from which to derive the key during migration
* `-l,--loginIdentityProviders <arg>` The login-identity-providers.xml file containing unprotected config values (will be overwritten)
* `-i,--outputLoginIdentityProviders <arg>` The destination login-identity-providers.xml file containing protected config values (will not modify input login-identity-providers.xml)
* `-A`,`--newFlowAlgorithm <arg>` The algorithm to use to encrypt the sensitive processor properties in flow.xml.gz
* `-b`,`--bootstrapConf <arg>` The bootstrap.conf file to persist master key
* `-e`,`--oldKey <arg>` The old raw hexadecimal key to use during key migration
* `-f`,`--flowXml <arg>` The flow.xml.gz file currently protected with old password (will be overwritten)
* `-g`,`--outputFlowXml <arg>` The destination flow.xml.gz file containing protected config values (will not modify input flow.xml.gz)
* `-h`,`--help` Prints this usage message
* `-i`,`--outputLoginIdentityProviders <arg>` The destination login-identity-providers.xml file containing protected config values (will not modify input login-identity-providers.xml)
* `-k`,`--key <arg>` The raw hexadecimal key to use to encrypt the sensitive properties
* `-l`,`--loginIdentityProviders <arg>` The login-identity-providers.xml file containing unprotected config values (will be overwritten)
* `-m`,`--migrate` If provided, the nifi.properties and/or login-identity-providers.xml sensitive properties will be re-encrypted with a new key
* `-n`,`--niFiProperties <arg>` The nifi.properties file containing unprotected config values (will be overwritten)
* `-o`,`--outputNiFiProperties <arg>` The destination nifi.properties file containing protected config values (will not modify input nifi.properties)
* `-p`,`--password <arg>` The password from which to derive the key to use to encrypt the sensitive properties
* `-P`,`--newFlowProvider <arg>` The security provider to use to encrypt the sensitive processor properties in flow.xml.gz
* `-r`,`--useRawKey` If provided, the secure console will prompt for the raw key value in hexadecimal form
* `-s`,`--propsKey <arg>` The password or key to use to encrypt the sensitive processor properties in flow.xml.gz
* `-v`,`--verbose` Sets verbose mode (default false)
* `-w`,`--oldPassword <arg>` The old password from which to derive the key during migration
* `-x`,`--encryptFlowXmlOnly` If provided, the properties in flow.xml.gz will be re-encrypted with a new key but the nifi.properties and/or login-identity-providers.xml files will not be modified
As an example of how the tool works, assume that you have installed the tool on a machine supporting 256-bit encryption and with the following existing values in the 'nifi.properties' file:

View File

@ -29,6 +29,7 @@ import java.util.Optional;
import java.util.Properties;
import java.util.stream.Stream;
import javax.crypto.Cipher;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.util.NiFiProperties;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
@ -53,7 +54,7 @@ public class NiFiPropertiesLoader {
/**
* Returns an instance of the loader configured with the key.
*
* <p>
* <p>
* NOTE: This method is used reflectively by the process which starts NiFi
* so changes to it must be made in conjunction with that mechanism.</p>
@ -109,31 +110,48 @@ public class NiFiPropertiesLoader {
* @throws IOException if the file is not readable
*/
public static String extractKeyFromBootstrapFile() throws IOException {
// Guess at location of bootstrap.conf file from nifi.properties file
String defaultNiFiPropertiesPath = getDefaultFilePath();
File propertiesFile = new File(defaultNiFiPropertiesPath);
File confDir = new File(propertiesFile.getParent());
if (confDir.exists() && confDir.canRead()) {
File expectedBootstrapFile = new File(confDir, "bootstrap.conf");
if (expectedBootstrapFile.exists() && expectedBootstrapFile.canRead()) {
try (Stream<String> stream = Files.lines(Paths.get(expectedBootstrapFile.getAbsolutePath()))) {
Optional<String> keyLine = stream.filter(l -> l.startsWith(BOOTSTRAP_KEY_PREFIX)).findFirst();
if (keyLine.isPresent()) {
return keyLine.get().split("=", 2)[1];
} else {
logger.warn("No encryption key present in the bootstrap.conf file at {}", expectedBootstrapFile.getAbsolutePath());
return "";
}
} catch (IOException e) {
logger.error("Cannot read from bootstrap.conf file at {} to extract encryption key", expectedBootstrapFile.getAbsolutePath());
throw new IOException("Cannot read from bootstrap.conf", e);
}
return extractKeyFromBootstrapFile("");
}
/**
* Returns the key (if any) used to encrypt sensitive properties, extracted from {@code $NIFI_HOME/conf/bootstrap.conf}.
*
* @param bootstrapPath the path to the bootstrap file
* @return the key in hexadecimal format
* @throws IOException if the file is not readable
*/
public static String extractKeyFromBootstrapFile(String bootstrapPath) throws IOException {
File expectedBootstrapFile;
if (StringUtils.isBlank(bootstrapPath)) {
// Guess at location of bootstrap.conf file from nifi.properties file
String defaultNiFiPropertiesPath = getDefaultFilePath();
File propertiesFile = new File(defaultNiFiPropertiesPath);
File confDir = new File(propertiesFile.getParent());
if (confDir.exists() && confDir.canRead()) {
expectedBootstrapFile = new File(confDir, "bootstrap.conf");
} else {
logger.error("Cannot read from bootstrap.conf file at {} to extract encryption key -- file is missing or permissions are incorrect", expectedBootstrapFile.getAbsolutePath());
logger.error("Cannot read from bootstrap.conf file at {} to extract encryption key -- conf/ directory is missing or permissions are incorrect", confDir.getAbsolutePath());
throw new IOException("Cannot read from bootstrap.conf");
}
} else {
logger.error("Cannot read from bootstrap.conf file at {} to extract encryption key -- conf/ directory is missing or permissions are incorrect", confDir.getAbsolutePath());
expectedBootstrapFile = new File(bootstrapPath);
}
if (expectedBootstrapFile.exists() && expectedBootstrapFile.canRead()) {
try (Stream<String> stream = Files.lines(Paths.get(expectedBootstrapFile.getAbsolutePath()))) {
Optional<String> keyLine = stream.filter(l -> l.startsWith(BOOTSTRAP_KEY_PREFIX)).findFirst();
if (keyLine.isPresent()) {
return keyLine.get().split("=", 2)[1];
} else {
logger.warn("No encryption key present in the bootstrap.conf file at {}", expectedBootstrapFile.getAbsolutePath());
return "";
}
} catch (IOException e) {
logger.error("Cannot read from bootstrap.conf file at {} to extract encryption key", expectedBootstrapFile.getAbsolutePath());
throw new IOException("Cannot read from bootstrap.conf", e);
}
} else {
logger.error("Cannot read from bootstrap.conf file at {} to extract encryption key -- file is missing or permissions are incorrect", expectedBootstrapFile.getAbsolutePath());
throw new IOException("Cannot read from bootstrap.conf");
}
}
@ -254,7 +272,7 @@ public class NiFiPropertiesLoader {
/**
* Returns the loaded {@link NiFiProperties} instance. If none is currently
* loaded, attempts to load the default instance.
*
* <p>
* <p>
* NOTE: This method is used reflectively by the process which starts NiFi
* so changes to it must be made in conjunction with that mechanism.</p>

View File

@ -284,7 +284,7 @@ class ProtectedNiFiProperties extends StandardNiFiProperties {
* @param key the key identifying the sensitive property
* @return the key identifying the protection scheme for the sensitive property
*/
public String getProtectionKey(String key) {
public static String getProtectionKey(String key) {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("Cannot find protection key for null key");
}

View File

@ -447,7 +447,7 @@ class AESSensitivePropertyProviderTest extends GroovyTestCase {
@Test
public void testShouldEncryptArbitraryValues() {
// Arrange
def values = ["thisIsABadPassword", "thisIsABadSensitiveKeyPassword", "thisIsABadKeystorePassword", "thisIsABadKeyPassword", "thisIsABadTruststorePassword", "This is an encrypted banner message"]
def values = ["thisIsABadPassword", "thisIsABadSensitiveKeyPassword", "thisIsABadKeystorePassword", "thisIsABadKeyPassword", "thisIsABadTruststorePassword", "This is an encrypted banner message", "nififtw!"]
String key = "2C576A9585DB862F5ECBEE5B4FFFCCA1" //getKeyOfSize(128)
// key = "0" * 64

View File

@ -58,6 +58,18 @@
<version>1.16.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-core</artifactId>
<version>1.1.0-SNAPSHOT</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>

View File

@ -25,6 +25,7 @@ import org.apache.commons.cli.HelpFormatter
import org.apache.commons.cli.Options
import org.apache.commons.cli.ParseException
import org.apache.commons.codec.binary.Hex
import org.apache.commons.io.IOUtils
import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException
import org.apache.nifi.toolkit.tls.commandLine.ExitCode
import org.apache.nifi.util.NiFiProperties
@ -37,9 +38,16 @@ import org.slf4j.LoggerFactory
import org.xml.sax.SAXException
import javax.crypto.Cipher
import javax.crypto.SecretKey
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec
import javax.crypto.spec.PBEParameterSpec
import java.nio.charset.StandardCharsets
import java.security.KeyException
import java.security.SecureRandom
import java.security.Security
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
class ConfigEncryptionTool {
private static final Logger logger = LoggerFactory.getLogger(ConfigEncryptionTool.class)
@ -49,14 +57,23 @@ class ConfigEncryptionTool {
public String outputNiFiPropertiesPath
public String loginIdentityProvidersPath
public String outputLoginIdentityProvidersPath
public String flowXmlPath
public String outputFlowXmlPath
private String keyHex
private String migrationKeyHex
private String password
private String migrationPassword
// This is the raw value used in nifi.sensitive.props.key
private String flowPropertiesPassword
private String newFlowAlgorithm
private String newFlowProvider
private NiFiProperties niFiProperties
private String loginIdentityProviders
private String flowXml
private boolean usingPassword = true
private boolean usingPasswordMigration = true
@ -64,6 +81,8 @@ class ConfigEncryptionTool {
private boolean isVerbose = false
private boolean handlingNiFiProperties = false
private boolean handlingLoginIdentityProviders = false
private boolean handlingFlowXml = false
private boolean ignorePropertiesFiles = false
private static final String HELP_ARG = "help"
private static final String VERBOSE_ARG = "verbose"
@ -72,13 +91,21 @@ class ConfigEncryptionTool {
private static final String LOGIN_IDENTITY_PROVIDERS_ARG = "loginIdentityProviders"
private static final String OUTPUT_NIFI_PROPERTIES_ARG = "outputNiFiProperties"
private static final String OUTPUT_LOGIN_IDENTITY_PROVIDERS_ARG = "outputLoginIdentityProviders"
private static final String FLOW_XML_ARG = "flowXml"
private static final String OUTPUT_FLOW_XML_ARG = "outputFlowXml"
private static final String KEY_ARG = "key"
private static final String PASSWORD_ARG = "password"
private static final String KEY_MIGRATION_ARG = "oldKey"
private static final String PASSWORD_MIGRATION_ARG = "oldPassword"
private static final String USE_KEY_ARG = "useRawKey"
private static final String MIGRATION_ARG = "migrate"
private static final String PROPS_KEY_ARG = "propsKey"
private static final String DO_NOT_ENCRYPT_NIFI_PROPERTIES_ARG = "encryptFlowXmlOnly"
private static final String NEW_FLOW_ALGORITHM_ARG = "newFlowAlgorithm"
private static final String NEW_FLOW_PROVIDER_ARG = "newFlowProvider"
// Hard-coded fallback value from {@link org.apache.nifi.encrypt.StringEncryptor}
private static final String DEFAULT_NIFI_SENSITIVE_PROPS_KEY = "nififtw!"
private static final int MIN_PASSWORD_LENGTH = 12
// Strong parameters as of 12 Aug 2016
@ -86,6 +113,10 @@ class ConfigEncryptionTool {
private static final int SCRYPT_R = 8
private static final int SCRYPT_P = 1
// Hard-coded values from StandardPBEByteEncryptor which will be removed during refactor of all flow encryption code in NIFI-1465
private static final int DEFAULT_KDF_ITERATIONS = 1000
private static final int DEFAULT_SALT_SIZE_BYTES = 16
private static
final String BOOTSTRAP_KEY_COMMENT = "# Master key in hexadecimal format for encrypted sensitive configuration values"
private static final String BOOTSTRAP_KEY_PREFIX = "nifi.bootstrap.sensitive.key="
@ -96,10 +127,20 @@ class ConfigEncryptionTool {
private static final String FOOTER = buildFooter()
private static
final String DEFAULT_DESCRIPTION = "This tool reads from a nifi.properties and/or login-identity-providers.xml file with plain sensitive configuration values, prompts the user for a master key, and encrypts each value. It will replace the plain value with the protected value in the same file (or write to a new file if specified)."
final String DEFAULT_DESCRIPTION = "This tool reads from a nifi.properties and/or " +
"login-identity-providers.xml file with plain sensitive configuration values, " +
"prompts the user for a master key, and encrypts each value. It will replace the " +
"plain value with the protected value in the same file (or write to a new file if " +
"specified). It can also be used to migrate already-encrypted values in those " +
"files or in flow.xml.gz to be encrypted with a new key."
private static final String LDAP_PROVIDER_CLASS = "org.apache.nifi.ldap.LdapProvider"
static private final String LDAP_PROVIDER_REGEX = /<provider>[\s\S]*?<class>\s*org\.apache\.nifi\.ldap\.LdapProvider[\s\S]*?<\/provider>/
static private final String XML_DECLARATION_REGEX = /<\?xml version="1.0" encoding="UTF-8"\?>/
private static
final String LDAP_PROVIDER_REGEX = /<provider>[\s\S]*?<class>\s*org\.apache\.nifi\.ldap\.LdapProvider[\s\S]*?<\/provider>/
private static final String XML_DECLARATION_REGEX = /<\?xml version="1.0" encoding="UTF-8"\?>/
private static final String WRAPPED_FLOW_XML_CIPHER_TEXT_REGEX = /enc\{[a-fA-F0-9]+?\}/
private static final String DEFAULT_PROVIDER = BouncyCastleProvider.PROVIDER_NAME
private static final String DEFAULT_FLOW_ALGORITHM = "PBEWITHMD5AND256BITAES-CBC-OPENSSL"
private static String buildHeader(String description = DEFAULT_DESCRIPTION) {
"${SEP}${description}${SEP * 2}"
@ -109,8 +150,8 @@ class ConfigEncryptionTool {
"${SEP}Java home: ${System.getenv(JAVA_HOME)}${SEP}NiFi Toolkit home: ${System.getenv(NIFI_TOOLKIT_HOME)}"
}
private final Options options;
private final String header;
private final Options options
private final String header
public ConfigEncryptionTool() {
@ -124,15 +165,21 @@ class ConfigEncryptionTool {
options.addOption("v", VERBOSE_ARG, false, "Sets verbose mode (default false)")
options.addOption("n", NIFI_PROPERTIES_ARG, true, "The nifi.properties file containing unprotected config values (will be overwritten)")
options.addOption("l", LOGIN_IDENTITY_PROVIDERS_ARG, true, "The login-identity-providers.xml file containing unprotected config values (will be overwritten)")
options.addOption("f", FLOW_XML_ARG, true, "The flow.xml.gz file currently protected with old password (will be overwritten)")
options.addOption("b", BOOTSTRAP_CONF_ARG, true, "The bootstrap.conf file to persist master key")
options.addOption("o", OUTPUT_NIFI_PROPERTIES_ARG, true, "The destination nifi.properties file containing protected config values (will not modify input nifi.properties)")
options.addOption("i", OUTPUT_LOGIN_IDENTITY_PROVIDERS_ARG, true, "The destination login-identity-providers.xml file containing protected config values (will not modify input login-identity-providers.xml)")
options.addOption("g", OUTPUT_FLOW_XML_ARG, true, "The destination flow.xml.gz file containing protected config values (will not modify input flow.xml.gz)")
options.addOption("k", KEY_ARG, true, "The raw hexadecimal key to use to encrypt the sensitive properties")
options.addOption("e", KEY_MIGRATION_ARG, true, "The old raw hexadecimal key to use during key migration")
options.addOption("p", PASSWORD_ARG, true, "The password from which to derive the key to use to encrypt the sensitive properties")
options.addOption("w", PASSWORD_MIGRATION_ARG, true, "The old password from which to derive the key during migration")
options.addOption("r", USE_KEY_ARG, false, "If provided, the secure console will prompt for the raw key value in hexadecimal form")
options.addOption("m", MIGRATION_ARG, false, "If provided, the sensitive properties will be re-encrypted with a new key")
options.addOption("m", MIGRATION_ARG, false, "If provided, the nifi.properties and/or login-identity-providers.xml sensitive properties will be re-encrypted with a new key")
options.addOption("x", DO_NOT_ENCRYPT_NIFI_PROPERTIES_ARG, false, "If provided, the properties in flow.xml.gz will be re-encrypted with a new key but the nifi.properties and/or login-identity-providers.xml files will not be modified")
options.addOption("s", PROPS_KEY_ARG, true, "The password or key to use to encrypt the sensitive processor properties in flow.xml.gz")
options.addOption("A", NEW_FLOW_ALGORITHM_ARG, true, "The algorithm to use to encrypt the sensitive processor properties in flow.xml.gz")
options.addOption("P", NEW_FLOW_PROVIDER_ARG, true, "The security provider to use to encrypt the sensitive processor properties in flow.xml.gz")
}
/**
@ -169,13 +216,36 @@ class ConfigEncryptionTool {
bootstrapConfPath = commandLine.getOptionValue(BOOTSTRAP_CONF_ARG)
// If this flag is provided, the nifi.properties is necessary to read/write the flow encryption key, but the encryption process will not actually be applied to nifi.properties / login-identity-providers.xml
if (commandLine.hasOption(DO_NOT_ENCRYPT_NIFI_PROPERTIES_ARG)) {
handlingNiFiProperties = false
handlingLoginIdentityProviders = false
ignorePropertiesFiles = true
} else {
if (commandLine.hasOption(LOGIN_IDENTITY_PROVIDERS_ARG)) {
if (isVerbose) {
logger.info("Handling encryption of login-identity-providers.xml")
}
loginIdentityProvidersPath = commandLine.getOptionValue(LOGIN_IDENTITY_PROVIDERS_ARG)
outputLoginIdentityProvidersPath = commandLine.getOptionValue(OUTPUT_LOGIN_IDENTITY_PROVIDERS_ARG, loginIdentityProvidersPath)
handlingLoginIdentityProviders = true
if (loginIdentityProvidersPath == outputLoginIdentityProvidersPath) {
// TODO: Add confirmation pause and provide -y flag to offer no-interaction mode?
logger.warn("The source login-identity-providers.xml and destination login-identity-providers.xml are identical [${outputLoginIdentityProvidersPath}] so the original will be overwritten")
}
}
}
// This needs to occur even if the nifi.properties won't be encrypted
if (commandLine.hasOption(NIFI_PROPERTIES_ARG)) {
if (isVerbose) {
boolean ignoreFlagPresent = commandLine.hasOption(DO_NOT_ENCRYPT_NIFI_PROPERTIES_ARG)
if (isVerbose && !ignoreFlagPresent) {
logger.info("Handling encryption of nifi.properties")
}
niFiPropertiesPath = commandLine.getOptionValue(NIFI_PROPERTIES_ARG)
outputNiFiPropertiesPath = commandLine.getOptionValue(OUTPUT_NIFI_PROPERTIES_ARG, niFiPropertiesPath)
handlingNiFiProperties = true
handlingNiFiProperties = !ignoreFlagPresent
if (niFiPropertiesPath == outputNiFiPropertiesPath) {
// TODO: Add confirmation pause and provide -y flag to offer no-interaction mode?
@ -183,17 +253,24 @@ class ConfigEncryptionTool {
}
}
if (commandLine.hasOption(LOGIN_IDENTITY_PROVIDERS_ARG)) {
if (commandLine.hasOption(FLOW_XML_ARG)) {
if (isVerbose) {
logger.info("Handling encryption of login-identity-providers.xml")
logger.info("Handling encryption of flow.xml.gz")
}
loginIdentityProvidersPath = commandLine.getOptionValue(LOGIN_IDENTITY_PROVIDERS_ARG)
outputLoginIdentityProvidersPath = commandLine.getOptionValue(OUTPUT_LOGIN_IDENTITY_PROVIDERS_ARG, loginIdentityProvidersPath)
handlingLoginIdentityProviders = true
flowXmlPath = commandLine.getOptionValue(FLOW_XML_ARG)
outputFlowXmlPath = commandLine.getOptionValue(OUTPUT_FLOW_XML_ARG, flowXmlPath)
handlingFlowXml = true
if (loginIdentityProvidersPath == outputLoginIdentityProvidersPath) {
newFlowAlgorithm = commandLine.getOptionValue(NEW_FLOW_ALGORITHM_ARG)
newFlowProvider = commandLine.getOptionValue(NEW_FLOW_PROVIDER_ARG)
if (flowXmlPath == outputFlowXmlPath) {
// TODO: Add confirmation pause and provide -y flag to offer no-interaction mode?
logger.warn("The source login-identity-providers.xml and destination login-identity-providers.xml are identical [${outputLoginIdentityProvidersPath}] so the original will be overwritten")
logger.warn("The source flow.xml.gz and destination flow.xml.gz are identical [${outputFlowXmlPath}] so the original will be overwritten")
}
if (!commandLine.hasOption(NIFI_PROPERTIES_ARG)) {
printUsageAndThrow("In order to migrate a flow.xml.gz, a nifi.properties file must also be specified via '-n'/'--${NIFI_PROPERTIES_ARG}'.", ExitCode.INVALID_ARGS)
}
}
@ -203,6 +280,8 @@ class ConfigEncryptionTool {
logger.info("(dest) nifi.properties: \t${outputNiFiPropertiesPath}")
logger.info("(src) login-identity-providers.xml: \t${loginIdentityProvidersPath}")
logger.info("(dest) login-identity-providers.xml: \t${outputLoginIdentityProvidersPath}")
logger.info("(src) flow.xml.gz: \t\t\t\t\t${flowXmlPath}")
logger.info("(dest) flow.xml.gz: \t\t\t\t\t${outputFlowXmlPath}")
}
// TODO: Implement in NIFI-2655
@ -251,6 +330,10 @@ class ConfigEncryptionTool {
usingPassword = false
}
}
if (commandLine.hasOption(PROPS_KEY_ARG)) {
flowPropertiesPassword = commandLine.getOptionValue(PROPS_KEY_ARG)
}
} catch (ParseException e) {
if (isVerbose) {
logger.error("Encountered an error", e)
@ -299,6 +382,10 @@ class ConfigEncryptionTool {
getKeyInternal(TextDevices.defaultTextDevice(), migrationKeyHex, migrationPassword, usingPasswordMigration)
}
private String getFlowPassword(TextDevice textDevice = TextDevices.defaultTextDevice()) {
readPasswordFromConsole(textDevice)
}
private static String readKeyFromConsole(TextDevice textDevice) {
textDevice.printf("Enter the master key in hexadecimal format (spaces acceptable): ")
new String(textDevice.readPassword())
@ -387,6 +474,227 @@ class ConfigEncryptionTool {
}
}
/**
* Loads the flow definition from the provided file path, handling the GZIP file compression. Unlike {@link #loadLoginIdentityProviders()} this method does not decrypt the content (for performance and separation of concern reasons).
*
* @return the file content
* @throw IOException if the flow.xml.gz file cannot be read
*/
private String loadFlowXml() throws IOException {
File flowXmlFile
if (flowXmlPath && (flowXmlFile = new File(flowXmlPath)).exists()) {
try {
new FileInputStream(flowXmlPath).withCloseable {
new GZIPInputStream(it).withCloseable {
String xmlContent = IOUtils.toString(it, StandardCharsets.UTF_8)
return xmlContent
}
}
} catch (RuntimeException e) {
if (isVerbose) {
logger.error("Encountered an error", e)
}
throw new IOException("Cannot load flow from [${flowXmlPath}]", e)
}
} else {
printUsageAndThrow("Cannot load flow from [${flowXmlPath}]", ExitCode.ERROR_READING_NIFI_PROPERTIES)
}
}
/**
* Decrypts a single element encrypted in the flow.xml.gz style (hex-encoded and wrapped with "enc{" and "}").
*
* Example:
* {@code enc{0123456789ABCDEF} } -> "some text"
*
* @param wrappedCipherText the wrapped and hex-encoded cipher text
* @param password the password used to encrypt the content (UTF-8 encoded)
* @param algorithm the encryption and KDF algorithm (defaults to PBEWITHMD5AND256BITAES-CBC-OPENSSL)
* @param provider the security provider (defaults to BC)
* @return the plaintext in UTF-8 encoding
*/
private
static String decryptFlowElement(String wrappedCipherText, String password, String algorithm = DEFAULT_FLOW_ALGORITHM, String provider = DEFAULT_PROVIDER) {
// Drop the "enc{" and closing "}"
if (!(wrappedCipherText =~ WRAPPED_FLOW_XML_CIPHER_TEXT_REGEX)) {
throw new SensitivePropertyProtectionException("The provided cipher text does not match the expected format 'enc{0123456789ABCDEF...}'")
}
String unwrappedCipherText = wrappedCipherText.replaceAll(/enc\{/, "")[0..<-1]
if (unwrappedCipherText.length() % 2 == 1 || unwrappedCipherText.length() == 0) {
throw new SensitivePropertyProtectionException("The provided cipher text must have an even number of hex characters")
}
// Decode the hex
byte[] cipherBytes = Hex.decodeHex(unwrappedCipherText.chars)
/* The structure of each cipher text is 16 bytes of salt || actual cipher text,
* so extract the salt (32 bytes encoded as hex, 16 bytes raw) and combine that
* with the default (and unchanged) iteration count that is hardcoded in
* {@link StandardPBEByteEncryptor}. I am extracting
* these values to magic numbers here so when the refactoring is performed,
* stronger decisions can be implemented here
*/
byte[] saltBytes = cipherBytes[0..<DEFAULT_SALT_SIZE_BYTES]
cipherBytes = cipherBytes[DEFAULT_SALT_SIZE_BYTES..-1]
Cipher decryptionCipher = generateFlowDecryptionCipher(password, saltBytes, algorithm, provider)
byte[] plainBytes = decryptionCipher.doFinal(cipherBytes)
new String(plainBytes, StandardCharsets.UTF_8)
}
/**
* Returns an initialized {@link javax.crypto.Cipher} instance with the extracted salt.
*
* @param password the password (UTF-8 encoding)
* @param saltBytes the salt (raw bytes)
* @param algorithm the KDF/encryption algorithm
* @param provider the security provider
* @return the initialized {@link javax.crypto.Cipher}
*/
private
static Cipher generateFlowDecryptionCipher(String password, byte[] saltBytes, String algorithm = DEFAULT_FLOW_ALGORITHM, String provider = DEFAULT_PROVIDER) {
Cipher decryptCipher = Cipher.getInstance(algorithm, provider)
PBEKeySpec keySpec = new PBEKeySpec(password.chars)
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm, provider)
SecretKey pbeKey = keyFactory.generateSecret(keySpec)
PBEParameterSpec parameterSpec = new PBEParameterSpec(saltBytes, DEFAULT_KDF_ITERATIONS)
decryptCipher.init(Cipher.DECRYPT_MODE, pbeKey, parameterSpec)
decryptCipher
}
/**
* Encrypts a single element in the flow.xml.gz style (hex-encoded and wrapped with "enc{" and "}").
*
* Example:
* "some text" -> {@code enc{0123456789ABCDEF} }
*
* @param plaintext the plaintext in UTF-8 encoding
* @param saltBytes the salt to embed in the cipher text to allow key derivation and decryption later in raw format
* @param encryptCipher the configured Cipher instance
* @return the wrapped and hex-encoded cipher text
*/
private static String encryptFlowElement(String plaintext, byte[] saltBytes, Cipher encryptCipher) {
byte[] plainBytes = plaintext?.getBytes(StandardCharsets.UTF_8) ?: new byte[0]
/* The structure of each cipher text is 16 bytes of salt || actual cipher text,
* so extract the salt (32 bytes encoded as hex, 16 bytes raw) and combine that
* with the default (and unchanged) iteration count that is hardcoded in
* {@link StandardPBEByteEncryptor}. I am extracting
* these values to magic numbers here so when the refactoring is performed,
* stronger decisions can be implemented here
*/
if (saltBytes.length != DEFAULT_SALT_SIZE_BYTES) {
throw new SensitivePropertyProtectionException("The salt must be ${DEFAULT_SALT_SIZE_BYTES} bytes")
}
byte[] cipherBytes = encryptCipher.doFinal(plainBytes)
byte[] saltAndCipherBytes = concatByteArrays(saltBytes, cipherBytes)
// Encode the hex
String hexEncodedCipherText = Hex.encodeHexString(saltAndCipherBytes)
"enc{${hexEncodedCipherText}}"
}
/**
* Utility method to quickly concatenate an arbitrary number of byte[].
*
* @param arrays the byte[] arrays
* @returna single byte[] containing the values concatenated
*/
private static byte[] concatByteArrays(byte[] ... arrays) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()
arrays.each { byte[] it -> outputStream.write(it) }
outputStream.toByteArray()
}
/**
* Scans XML content and decrypts each encrypted element, then re-encrypts it with the new key, and returns the final XML content.
*
* @param flowXmlContent the original flow.xml.gz content
* @param existingFlowPassword the existing value of nifi.sensitive.props.key (not a raw key, but rather a password)
* @param newFlowPassword the password to use to for encryption (not a raw key, but rather a password)
* @param existingAlgorithm the KDF algorithm to use (defaults to PBEWITHMD5AND256BITAES-CBC-OPENSSL)
* @param existingProvider the {@link java.security.Provider} to use (defaults to BC)
* @return the encrypted XML content
*/
private String migrateFlowXmlContent(String flowXmlContent, String existingFlowPassword, String newFlowPassword, String existingAlgorithm = DEFAULT_FLOW_ALGORITHM, String existingProvider = DEFAULT_PROVIDER, String newAlgorithm = DEFAULT_FLOW_ALGORITHM, String newProvider = DEFAULT_PROVIDER) {
/* For re-encryption, for performance reasons, we will use a fixed salt for all of
* the operations. These values are stored in the same file and the default key is in the
* source code (see NIFI-1465 and NIFI-1277), so the security trade-off is minimal
* but the performance hit is substantial. We can't make this decision for
* decryption because the FlowSerializer still uses StringEncryptor which does not
* follow this pattern
*/
byte[] encryptionSalt = new byte[DEFAULT_SALT_SIZE_BYTES]
new SecureRandom().nextBytes(encryptionSalt)
Cipher encryptCipher = generateFlowEncryptionCipher(newFlowPassword, encryptionSalt, newAlgorithm, newProvider)
int elementCount = 0
// Scan the XML content and identify every encrypted element, decrypt it, and replace it with the re-encrypted value
String migratedFlowXmlContent = flowXmlContent.replaceAll(WRAPPED_FLOW_XML_CIPHER_TEXT_REGEX) { String wrappedCipherText ->
String plaintext = decryptFlowElement(wrappedCipherText, existingFlowPassword, existingAlgorithm, existingProvider)
byte[] cipherBytes = encryptCipher.doFinal(plaintext.bytes)
byte[] saltAndCipherBytes = concatByteArrays(encryptionSalt, cipherBytes)
elementCount++
"enc{${Hex.encodeHex(saltAndCipherBytes)}}"
}
if (isVerbose) {
logger.info("Decrypted and re-encrypted ${elementCount} elements for flow.xml.gz")
}
migratedFlowXmlContent
}
/**
* Returns an initialized encryption cipher for the flow.xml.gz content.
*
* @param newFlowPassword the new encryption password
* @param saltBytes the salt [16 bytes in raw format]
* @param algorithm the KDF/encryption algorithm
* @param provider the security provider
* @return the initialized cipher instance
*/
private
static Cipher generateFlowEncryptionCipher(String newFlowPassword, byte[] saltBytes, String algorithm = DEFAULT_FLOW_ALGORITHM, String provider = DEFAULT_PROVIDER) {
/* The Jasypt StringEncryptor implementation is final and has some design decisions
* that will pollute this code (i.e. using a random salt on every encrypt operation
* rather than a unique IV, so the derived key for every encrypt/decrypt operation is
* different, which is very wasteful), so just use the standard JCE ciphers with the
* password derived using the prescribed algorithm
*/
Cipher encryptCipher = Cipher.getInstance(algorithm, provider)
/* For re-encryption, for performance reasons, we will use a fixed salt for all of
* the operations. These values are stored in the same file and the default key is in the
* source code (see NIFI-1465 and NIFI-1277), so the security trade-off is minimal
* but the performance hit is substantial. We can't make this decision for
* decryption because the FlowSerializer still uses StringEncryptor which does not
* follow this pattern
*/
PBEKeySpec keySpec = new PBEKeySpec(newFlowPassword.chars)
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm, provider)
SecretKey pbeKey = keyFactory.generateSecret(keySpec)
PBEParameterSpec parameterSpec = new PBEParameterSpec(saltBytes, DEFAULT_KDF_ITERATIONS)
encryptCipher.init(Cipher.ENCRYPT_MODE, pbeKey, parameterSpec)
encryptCipher
}
/**
* Writes the XML content to the {@link .outputFlowXmlPath} location, handling the GZIP file compression.
*
* @param flowXmlContent the XML content to write
*/
private void writeFlowXmlToFile(String flowXmlContent) {
new FileOutputStream(outputFlowXmlPath).withCloseable {
new GZIPOutputStream(it).withCloseable {
IOUtils.write(flowXmlContent, it, StandardCharsets.UTF_8)
}
}
}
String decryptLoginIdentityProviders(String encryptedXml, String existingKeyHex = keyHex) {
AESSensitivePropertyProvider sensitivePropertyProvider = new AESSensitivePropertyProvider(existingKeyHex)
@ -654,8 +962,11 @@ class ConfigEncryptionTool {
List<String> lines = originalPropertiesFile.readLines()
ProtectedNiFiProperties protectedNiFiProperties = new ProtectedNiFiProperties(niFiProperties)
// Only need to replace the keys that have been protected
// Only need to replace the keys that have been protected AND nifi.sensitive.props.key
Map<String, String> protectedKeys = protectedNiFiProperties.getProtectedPropertyKeys()
if (!protectedKeys.containsKey(NiFiProperties.SENSITIVE_PROPS_KEY)) {
protectedKeys.put(NiFiProperties.SENSITIVE_PROPS_KEY, protectedNiFiProperties.getProperty(ProtectedNiFiProperties.getProtectionKey(NiFiProperties.SENSITIVE_PROPS_KEY)))
}
protectedKeys.each { String key, String protectionScheme ->
int l = lines.findIndexOf { it.startsWith(key) }
@ -664,7 +975,7 @@ class ConfigEncryptionTool {
}
// Get the index of the following line (or cap at max)
int p = l + 1 > lines.size() ? lines.size() : l + 1
String protectionLine = "${protectedNiFiProperties.getProtectionKey(key)}=${protectionScheme}"
String protectionLine = "${protectedNiFiProperties.getProtectionKey(key)}=${protectionScheme ?: ""}"
if (p < lines.size() && lines.get(p).startsWith("${protectedNiFiProperties.getProtectionKey(key)}=")) {
lines.set(p, protectionLine)
} else {
@ -765,6 +1076,28 @@ class ConfigEncryptionTool {
"NIFI_SCRYPT_SALT".getBytes(StandardCharsets.UTF_8)
}
private String getExistingFlowPassword() {
return niFiProperties.getProperty(NiFiProperties.SENSITIVE_PROPS_KEY) as String ?: DEFAULT_NIFI_SENSITIVE_PROPS_KEY
}
/**
* Utility method which returns true if the {@link org.apache.nifi.util.NiFiProperties} instance has encrypted properties.
*
* @return true if the properties instance will require a key to access
*/
boolean niFiPropertiesAreEncrypted() {
if (niFiPropertiesPath) {
try {
def nfp = NiFiPropertiesLoader.withKey(keyHex).readProtectedPropertiesFromDisk(new File(niFiPropertiesPath))
return nfp.hasProtectedKeys()
} catch (SensitivePropertyProtectionException | IOException e) {
return true
}
} else {
return false
}
}
/**
* Runs main tool logic (parsing arguments, reading files, protecting properties, and writing key and properties out to destination files).
*
@ -779,48 +1112,56 @@ class ConfigEncryptionTool {
try {
tool.parse(args)
tool.keyHex = tool.getKey()
if (!tool.keyHex) {
tool.printUsageAndThrow("Hex key must be provided", ExitCode.INVALID_ARGS)
}
try {
// Validate the length and format
tool.keyHex = parseKey(tool.keyHex)
} catch (KeyException e) {
if (tool.isVerbose) {
logger.error("Encountered an error", e)
boolean existingNiFiPropertiesAreEncrypted = tool.niFiPropertiesAreEncrypted()
if (!tool.ignorePropertiesFiles || (tool.handlingFlowXml && existingNiFiPropertiesAreEncrypted)) {
// If we are handling the flow.xml.gz and nifi.properties is already encrypted, try getting the key from bootstrap.conf rather than the console
if (tool.ignorePropertiesFiles) {
tool.keyHex = NiFiPropertiesLoader.extractKeyFromBootstrapFile(tool.bootstrapConfPath)
} else {
tool.keyHex = tool.getKey()
}
tool.printUsageAndThrow(e.getMessage(), ExitCode.INVALID_ARGS)
}
if (tool.migration) {
String migrationKeyHex = tool.getMigrationKey()
if (!migrationKeyHex) {
tool.printUsageAndThrow("Original hex key must be provided for migration", ExitCode.INVALID_ARGS)
if (!tool.keyHex) {
tool.printUsageAndThrow("Hex key must be provided", ExitCode.INVALID_ARGS)
}
try {
// Validate the length and format
tool.migrationKeyHex = parseKey(migrationKeyHex)
tool.keyHex = parseKey(tool.keyHex)
} catch (KeyException e) {
if (tool.isVerbose) {
logger.error("Encountered an error", e)
}
tool.printUsageAndThrow(e.getMessage(), ExitCode.INVALID_ARGS)
}
if (tool.migration) {
String migrationKeyHex = tool.getMigrationKey()
if (!migrationKeyHex) {
tool.printUsageAndThrow("Original hex key must be provided for migration", ExitCode.INVALID_ARGS)
}
try {
// Validate the length and format
tool.migrationKeyHex = parseKey(migrationKeyHex)
} catch (KeyException e) {
if (tool.isVerbose) {
logger.error("Encountered an error", e)
}
tool.printUsageAndThrow(e.getMessage(), ExitCode.INVALID_ARGS)
}
}
}
String existingKeyHex = tool.migrationKeyHex ?: tool.keyHex
if (tool.handlingNiFiProperties) {
// Load NiFiProperties for either scenario; only encrypt if "handling" (see after flow XML)
if (tool.handlingNiFiProperties || tool.handlingFlowXml) {
try {
tool.niFiProperties = tool.loadNiFiProperties(existingKeyHex)
} catch (Exception e) {
tool.printUsageAndThrow("Cannot migrate key if no previous encryption occurred", ExitCode.ERROR_READING_NIFI_PROPERTIES)
}
tool.niFiProperties = tool.encryptSensitiveProperties(tool.niFiProperties)
}
if (tool.handlingLoginIdentityProviders) {
@ -831,6 +1172,61 @@ class ConfigEncryptionTool {
}
tool.loginIdentityProviders = tool.encryptLoginIdentityProviders(tool.loginIdentityProviders)
}
if (tool.handlingFlowXml) {
try {
tool.flowXml = tool.loadFlowXml()
} catch (Exception e) {
tool.printUsageAndThrow("Cannot load flow.xml.gz", ExitCode.ERROR_READING_NIFI_PROPERTIES)
}
// If the flow password was not set in nifi.properties, use the hard-coded default
String existingFlowPassword = tool.getExistingFlowPassword()
// If the new password was not provided in the arguments, read from the console. If that is empty, use the same value (essentially a copy no-op)
String newFlowPassword = tool.flowPropertiesPassword ?: tool.getFlowPassword()
if (!newFlowPassword) {
newFlowPassword = existingFlowPassword
}
// Get the algorithms and providers
NiFiProperties nfp = tool.niFiProperties
String existingAlgorithm = nfp?.getProperty(NiFiProperties.SENSITIVE_PROPS_ALGORITHM) ?: DEFAULT_FLOW_ALGORITHM
String existingProvider = nfp?.getProperty(NiFiProperties.SENSITIVE_PROPS_PROVIDER) ?: DEFAULT_PROVIDER
String newAlgorithm = tool.newFlowAlgorithm ?: existingAlgorithm
String newProvider = tool.newFlowProvider ?: existingProvider
tool.flowXml = tool.migrateFlowXmlContent(tool.flowXml, existingFlowPassword, newFlowPassword, existingAlgorithm, existingProvider, newAlgorithm, newProvider)
// If the new key is the hard-coded internal value, don't persist it to nifi.properties
if (newFlowPassword != DEFAULT_NIFI_SENSITIVE_PROPS_KEY && newFlowPassword != existingFlowPassword) {
// Update the NiFiProperties object with the new flow password before it gets encrypted (wasteful, but NiFiProperties instances are immutable)
Properties rawProperties = new Properties()
nfp.getPropertyKeys().each { String k ->
rawProperties.put(k, nfp.getProperty(k))
}
// If the tool is not going to encrypt NiFiProperties and the existing file is already encrypted, encrypt and update the new sensitive props key
if (!tool.handlingNiFiProperties && existingNiFiPropertiesAreEncrypted) {
AESSensitivePropertyProvider spp = new AESSensitivePropertyProvider(tool.keyHex)
String encryptedSPK = spp.protect(newFlowPassword)
rawProperties.put(NiFiProperties.SENSITIVE_PROPS_KEY, encryptedSPK)
// Manually update the protection scheme or it will be lost
rawProperties.put(ProtectedNiFiProperties.getProtectionKey(NiFiProperties.SENSITIVE_PROPS_KEY), spp.getIdentifierKey())
if (tool.isVerbose) {
logger.info("Tool is not configured to encrypt nifi.properties, but the existing nifi.properties is encrypted and flow.xml.gz was migrated, so manually persisting the new encrypted value to nifi.properties")
}
} else {
rawProperties.put(NiFiProperties.SENSITIVE_PROPS_KEY, newFlowPassword)
}
tool.niFiProperties = new StandardNiFiProperties(rawProperties)
}
}
if (tool.handlingNiFiProperties) {
tool.niFiProperties = tool.encryptSensitiveProperties(tool.niFiProperties)
}
} catch (CommandLineParseException e) {
if (e.exitCode == ExitCode.HELP) {
System.exit(ExitCode.HELP.ordinal())
@ -846,8 +1242,13 @@ class ConfigEncryptionTool {
try {
// Do this as part of a transaction?
synchronized (this) {
tool.writeKeyToBootstrapConf()
if (tool.handlingNiFiProperties) {
if (!tool.ignorePropertiesFiles) {
tool.writeKeyToBootstrapConf()
}
if (tool.handlingFlowXml) {
tool.writeFlowXmlToFile(tool.flowXml)
}
if (tool.handlingNiFiProperties || tool.handlingFlowXml) {
tool.writeNiFiProperties()
}
if (tool.handlingLoginIdentityProviders) {

View File

@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<flowController encoding-version="1.0">
<maxTimerDrivenThreadCount>10</maxTimerDrivenThreadCount>
<maxEventDrivenThreadCount>5</maxEventDrivenThreadCount>
<rootGroup>
<id>fcf146b2-0157-1000-7850-7adf1d31e3fa</id>
<name>NiFi Flow</name>
<position x="0.0" y="0.0"/>
<comment/>
<processGroup>
<id>8a61ec1d-0158-1000-3a1a-12c54fe77838</id>
<name>EncryptedProperties Example</name>
<position x="1119.0" y="295.0"/>
<comment/>
<processor>
<id>8a621f0b-0158-1000-b5c2-92a09a124501</id>
<name>Encrypt</name>
<position x="626.0" y="237.0"/>
<styles/>
<comment/>
<class>org.apache.nifi.processors.standard.EncryptContent</class>
<maxConcurrentTasks>1</maxConcurrentTasks>
<schedulingPeriod>0 sec</schedulingPeriod>
<penalizationPeriod>30 sec</penalizationPeriod>
<yieldPeriod>1 sec</yieldPeriod>
<bulletinLevel>WARN</bulletinLevel>
<lossTolerant>false</lossTolerant>
<scheduledState>STOPPED</scheduledState>
<schedulingStrategy>TIMER_DRIVEN</schedulingStrategy>
<runDurationNanos>0</runDurationNanos>
<property>
<name>Mode</name>
<value>Encrypt</value>
</property>
<property>
<name>key-derivation-function</name>
<value>NIFI_LEGACY</value>
</property>
<property>
<name>Encryption Algorithm</name>
<value>MD5_128AES</value>
</property>
<property>
<name>allow-weak-crypto</name>
<value>not-allowed</value>
</property>
<property>
<name>Password</name>
<value>enc{2032416987A00D9FCD757528D7AE609D7E793CA5F956641DB53E14CDB9BFCD4037B73AC705CD3F5C1C1BDE18B8D7B281}</value>
</property>
<property>
<name>raw-key-hex</name>
</property>
<property>
<name>public-keyring-file</name>
</property>
<property>
<name>public-key-user-id</name>
</property>
<property>
<name>private-keyring-file</name>
</property>
<property>
<name>private-keyring-passphrase</name>
</property>
</processor>
<processor>
<id>8a6314ee-0158-1000-6dd0-60f153db26c1</id>
<name>Decrypt</name>
<position x="630.0" y="482.0"/>
<styles/>
<comment/>
<class>org.apache.nifi.processors.standard.EncryptContent</class>
<maxConcurrentTasks>1</maxConcurrentTasks>
<schedulingPeriod>0 sec</schedulingPeriod>
<penalizationPeriod>30 sec</penalizationPeriod>
<yieldPeriod>1 sec</yieldPeriod>
<bulletinLevel>WARN</bulletinLevel>
<lossTolerant>false</lossTolerant>
<scheduledState>STOPPED</scheduledState>
<schedulingStrategy>TIMER_DRIVEN</schedulingStrategy>
<runDurationNanos>0</runDurationNanos>
<property>
<name>Mode</name>
<value>Decrypt</value>
</property>
<property>
<name>key-derivation-function</name>
<value>NIFI_LEGACY</value>
</property>
<property>
<name>Encryption Algorithm</name>
<value>MD5_128AES</value>
</property>
<property>
<name>allow-weak-crypto</name>
<value>not-allowed</value>
</property>
<property>
<name>Password</name>
<value>enc{4B580C55B8355FE57A599B31B3B2ACA77429DBF6887C177417624026469E895F0C89FB0C9D9C64C5B2AD943035689C9C}</value>
</property>
<property>
<name>raw-key-hex</name>
</property>
<property>
<name>public-keyring-file</name>
</property>
<property>
<name>public-key-user-id</name>
</property>
<property>
<name>private-keyring-file</name>
</property>
<property>
<name>private-keyring-passphrase</name>
</property>
</processor>
<connection>
<id>8a636069-0158-1000-50ff-244f2a8eeb7a</id>
<name/>
<bendPoints/>
<labelIndex>1</labelIndex>
<zIndex>0</zIndex>
<sourceId>8a621f0b-0158-1000-b5c2-92a09a124501</sourceId>
<sourceGroupId>8a61ec1d-0158-1000-3a1a-12c54fe77838</sourceGroupId>
<sourceType>PROCESSOR</sourceType>
<destinationId>8a6314ee-0158-1000-6dd0-60f153db26c1</destinationId>
<destinationGroupId>8a61ec1d-0158-1000-3a1a-12c54fe77838</destinationGroupId>
<destinationType>PROCESSOR</destinationType>
<relationship>success</relationship>
<maxWorkQueueSize>10000</maxWorkQueueSize>
<maxWorkQueueDataSize>1 GB</maxWorkQueueDataSize>
<flowFileExpiration>0 sec</flowFileExpiration>
</connection>
</processGroup>
</rootGroup>
<controllerServices/>
<reportingTasks/>
</flowController>

View File

@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<flowController encoding-version="1.0">
<maxTimerDrivenThreadCount>10</maxTimerDrivenThreadCount>
<maxEventDrivenThreadCount>5</maxEventDrivenThreadCount>
<rootGroup>
<id>fcf146b2-0157-1000-7850-7adf1d31e3fa</id>
<name>NiFi Flow</name>
<position x="0.0" y="0.0"/>
<comment/>
<processGroup>
<id>8a61ec1d-0158-1000-3a1a-12c54fe77838</id>
<name>EncryptedProperties Example</name>
<position x="1119.0" y="295.0"/>
<comment/>
<processor>
<id>8a621f0b-0158-1000-b5c2-92a09a124501</id>
<name>Encrypt</name>
<position x="626.0" y="237.0"/>
<styles/>
<comment/>
<class>org.apache.nifi.processors.standard.EncryptContent</class>
<maxConcurrentTasks>1</maxConcurrentTasks>
<schedulingPeriod>0 sec</schedulingPeriod>
<penalizationPeriod>30 sec</penalizationPeriod>
<yieldPeriod>1 sec</yieldPeriod>
<bulletinLevel>WARN</bulletinLevel>
<lossTolerant>false</lossTolerant>
<scheduledState>STOPPED</scheduledState>
<schedulingStrategy>TIMER_DRIVEN</schedulingStrategy>
<runDurationNanos>0</runDurationNanos>
<property>
<name>Mode</name>
<value>Encrypt</value>
</property>
<property>
<name>key-derivation-function</name>
<value>NIFI_LEGACY</value>
</property>
<property>
<name>Encryption Algorithm</name>
<value>MD5_128AES</value>
</property>
<property>
<name>allow-weak-crypto</name>
<value>not-allowed</value>
</property>
<property>
<name>Password</name>
<value>enc{2032416987A00D9FCD757528D7AE609D7E793CA5F956641DB53E14CDB9BFCD4037B73AC705CD3F5C1C1BDE18B8D7B281}</value>
</property>
<property>
<name>raw-key-hex</name>
</property>
<property>
<name>public-keyring-file</name>
</property>
<property>
<name>public-key-user-id</name>
</property>
<property>
<name>private-keyring-file</name>
</property>
<property>
<name>private-keyring-passphrase</name>
</property>
</processor>
<processor>
<id>8a6314ee-0158-1000-6dd0-60f153db26c1</id>
<name>Decrypt</name>
<position x="630.0" y="482.0"/>
<styles/>
<comment/>
<class>org.apache.nifi.processors.standard.EncryptContent</class>
<maxConcurrentTasks>1</maxConcurrentTasks>
<schedulingPeriod>0 sec</schedulingPeriod>
<penalizationPeriod>30 sec</penalizationPeriod>
<yieldPeriod>1 sec</yieldPeriod>
<bulletinLevel>WARN</bulletinLevel>
<lossTolerant>false</lossTolerant>
<scheduledState>STOPPED</scheduledState>
<schedulingStrategy>TIMER_DRIVEN</schedulingStrategy>
<runDurationNanos>0</runDurationNanos>
<property>
<name>Mode</name>
<value>Decrypt</value>
</property>
<property>
<name>key-derivation-function</name>
<value>NIFI_LEGACY</value>
</property>
<property>
<name>Encryption Algorithm</name>
<value>MD5_128AES</value>
</property>
<property>
<name>allow-weak-crypto</name>
<value>not-allowed</value>
</property>
<property>
<name>Password</name>
<value>enc{4B580C55B8355FE57A599B31B3B2ACA77429DBF6887C177417624026469E895F0C89FB0C9D9C64C5B2AD943035689C9C}</value>
</property>
<property>
<name>raw-key-hex</name>
</property>
<property>
<name>public-keyring-file</name>
</property>
<property>
<name>public-key-user-id</name>
</property>
<property>
<name>private-keyring-file</name>
</property>
<property>
<name>private-keyring-passphrase</name>
</property>
</processor>
<connection>
<id>8a636069-0158-1000-50ff-244f2a8eeb7a</id>
<name/>
<bendPoints/>
<labelIndex>1</labelIndex>
<zIndex>0</zIndex>
<sourceId>8a621f0b-0158-1000-b5c2-92a09a124501</sourceId>
<sourceGroupId>8a61ec1d-0158-1000-3a1a-12c54fe77838</sourceGroupId>
<sourceType>PROCESSOR</sourceType>
<destinationId>8a6314ee-0158-1000-6dd0-60f153db26c1</destinationId>
<destinationGroupId>8a61ec1d-0158-1000-3a1a-12c54fe77838</destinationGroupId>
<destinationType>PROCESSOR</destinationType>
<relationship>success</relationship>
<maxWorkQueueSize>10000</maxWorkQueueSize>
<maxWorkQueueDataSize>1 GB</maxWorkQueueDataSize>
<flowFileExpiration>0 sec</flowFileExpiration>
</connection>
</processGroup>
</rootGroup>
<controllerServices/>
<reportingTasks/>
</flowController>

View File

@ -0,0 +1,125 @@
# 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.
# Core Properties #
nifi.version=nifi-test 3.0.0
nifi.flow.configuration.file=./target/flow.xml.gz
nifi.flow.configuration.archive.dir=./target/archive/
nifi.flowcontroller.autoResumeState=true
nifi.flowcontroller.graceful.shutdown.period=10 sec
nifi.flowservice.writedelay.interval=2 sec
nifi.administrative.yield.duration=30 sec
nifi.reporting.task.configuration.file=./target/reporting-tasks.xml
nifi.controller.service.configuration.file=./target/controller-services.xml
nifi.templates.directory=./target/templates
nifi.ui.banner.text=UI Banner Text
nifi.ui.autorefresh.interval=30 sec
nifi.nar.library.directory=./target/resources/NiFiProperties/lib/
nifi.nar.library.directory.alt=./target/resources/NiFiProperties/lib2/
nifi.nar.working.directory=./target/work/nar/
# H2 Settings
nifi.database.directory=./target/database_repository
nifi.h2.url.append=;LOCK_TIMEOUT=25000;WRITE_DELAY=0;AUTO_SERVER=FALSE
# FlowFile Repository
nifi.flowfile.repository.directory=./target/test-repo
nifi.flowfile.repository.partitions=1
nifi.flowfile.repository.checkpoint.interval=2 mins
nifi.queue.swap.threshold=20000
nifi.swap.storage.directory=./target/test-repo/swap
nifi.swap.in.period=5 sec
nifi.swap.in.threads=1
nifi.swap.out.period=5 sec
nifi.swap.out.threads=4
# Content Repository
nifi.content.claim.max.appendable.size=10 MB
nifi.content.claim.max.flow.files=100
nifi.content.repository.directory.default=./target/content_repository
# Provenance Repository Properties
nifi.provenance.repository.storage.directory=./target/provenance_repository
nifi.provenance.repository.max.storage.time=24 hours
nifi.provenance.repository.max.storage.size=1 GB
nifi.provenance.repository.rollover.time=30 secs
nifi.provenance.repository.rollover.size=100 MB
# Site to Site properties
nifi.remote.input.socket.port=9990
nifi.remote.input.secure=true
# web properties #
nifi.web.war.directory=./target/lib
nifi.web.http.host=
nifi.web.http.port=
nifi.web.https.host=nifi.nifi.apache.org
nifi.web.https.port=8443
nifi.web.jetty.working.directory=./target/work/jetty
# security properties #
nifi.sensitive.props.key=
nifi.sensitive.props.algorithm=PBEWITHMD5AND256BITAES-CBC-OPENSSL
nifi.sensitive.props.provider=BC
nifi.sensitive.props.additional.keys=
nifi.security.keystore=
nifi.security.keystoreType=
nifi.security.keystorePasswd=
nifi.security.keyPasswd=
nifi.security.truststore=
nifi.security.truststoreType=
nifi.security.truststorePasswd=
nifi.security.needClientAuth=
nifi.security.user.authorizer=
# cluster common properties (cluster manager and nodes must have same values) #
nifi.cluster.protocol.heartbeat.interval=5 sec
nifi.cluster.protocol.is.secure=false
nifi.cluster.protocol.socket.timeout=30 sec
nifi.cluster.protocol.connection.handshake.timeout=45 sec
# if multicast is used, then nifi.cluster.protocol.multicast.xxx properties must be configured #
nifi.cluster.protocol.use.multicast=false
nifi.cluster.protocol.multicast.address=
nifi.cluster.protocol.multicast.port=
nifi.cluster.protocol.multicast.service.broadcast.delay=500 ms
nifi.cluster.protocol.multicast.service.locator.attempts=3
nifi.cluster.protocol.multicast.service.locator.attempts.delay=1 sec
# cluster node properties (only configure for cluster nodes) #
nifi.cluster.is.node=false
nifi.cluster.node.address=
nifi.cluster.node.protocol.port=
nifi.cluster.node.protocol.threads=2
# if multicast is not used, nifi.cluster.node.unicast.xxx must have same values as nifi.cluster.manager.xxx #
nifi.cluster.node.unicast.manager.address=
nifi.cluster.node.unicast.manager.protocol.port=
nifi.cluster.node.unicast.manager.authority.provider.port=
# cluster manager properties (only configure for cluster manager) #
nifi.cluster.is.manager=false
nifi.cluster.manager.address=
nifi.cluster.manager.protocol.port=
nifi.cluster.manager.authority.provider.port=
nifi.cluster.manager.authority.provider.threads=10
nifi.cluster.manager.node.firewall.file=
nifi.cluster.manager.node.event.history.size=10
nifi.cluster.manager.node.api.connection.timeout=30 sec
nifi.cluster.manager.node.api.read.timeout=30 sec
nifi.cluster.manager.node.api.request.threads=10
nifi.cluster.manager.flow.retrieval.delay=5 sec
nifi.cluster.manager.protocol.threads=10
nifi.cluster.manager.safemode.duration=0 sec

View File

@ -0,0 +1,34 @@
# 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.
# Core Properties #
nifi.version=nifi-test 3.0.0
# security properties #
nifi.sensitive.props.key=UXcrW8T1UKAPJeun||ezUJSp30AvKGsRxJOOXoPUtZonv56Lx1
nifi.sensitive.props.key.protected=aes/gcm/128
nifi.sensitive.props.algorithm=PBEWITHMD5AND256BITAES-CBC-OPENSSL
nifi.sensitive.props.provider=BC
nifi.sensitive.props.additional.keys=
nifi.security.keystore=/path/to/keystore.jks
nifi.security.keystoreType=JKS
nifi.security.keystorePasswd=vp9C9a8KbSYZFdUM||RoZHB1J+sRgCKG2vKBviOn71tdhsDYH42No+VmIaFMolrTMD/zmwcKev
nifi.security.keystorePasswd.protected=aes/gcm/128
nifi.security.keyPasswd=ttiSNTC7PUf2Hla7||W2OmVFn4bfB2ZJNBG55SaLneQeZahhF6GIaLZU+i4zRFfxKnlQ
nifi.security.keyPasswd.protected=aes/gcm/128
nifi.security.truststore=
nifi.security.truststoreType=
nifi.security.truststorePasswd=