Skip to content

Fix and document changing sensitive properties algorithm #799

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
May 28, 2025
Merged
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ All notable changes to this project will be documented in this file.

- Use `json` file extension for log files ([#774]).
- Fix a bug where changes to ConfigMaps that are referenced in the NifiCluster spec didn't trigger a reconciliation ([#772]).
- The operator now emits a warning (1.x.x) or errors out (2.x.x) if a deprecated or unsupported sensitive properties algorithm is used ([#799]).

### Removed

Expand All @@ -45,6 +46,7 @@ All notable changes to this project will be documented in this file.
[#785]: https://github.com/stackabletech/nifi-operator/pull/785
[#787]: https://github.com/stackabletech/nifi-operator/pull/787
[#789]: https://github.com/stackabletech/nifi-operator/pull/789
[#799]: https://github.com/stackabletech/nifi-operator/pull/799

## [25.3.0] - 2025-03-21

Expand Down
4 changes: 2 additions & 2 deletions deploy/helm/nifi-operator/crds/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,12 @@ spec:

Learn more about the specifics of the algorithm parameters in the [NiFi documentation](https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#property-encryption-algorithms).
enum:
- nifiArgon2AesGcm128
- nifiPbkdf2AesGcm256
- nifiArgon2AesGcm256
- nifiBcryptAesGcm128
- nifiBcryptAesGcm256
- nifiPbkdf2AesGcm128
- nifiPbkdf2AesGcm256
- nifiArgon2AesGcm128
- nifiScryptAesGcm128
- nifiScryptAesGcm256
nullable: true
Expand Down
64 changes: 61 additions & 3 deletions docs/modules/nifi/pages/usage_guide/security.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ If `autoGenerate` is false and no Secret with the given name in `keySecret` is f
The `algorithm` property configures the encryption algorithm used to encrypt the sensitive properties.
Consult the {crd-docs}/nifi.stackable.tech/nificluster/v1alpha1/#spec-clusterConfig-sensitiveProperties-algorithm[reference documentation {external-link-icon}^] for a list of supported algorithms.

=== Autogenerated key example
=== Autogenerated sensitive properties key

Let the operator generate a Secret with the name `nifi-sensitive-property-key`:

Expand All @@ -385,7 +385,7 @@ sensitiveProperties:
autoGenerate: true
----

=== Custom key and encryption algorithm example
=== Custom sensitive properties key and sensitive properties algorithm

Create the Secret yourself:

Expand All @@ -399,7 +399,7 @@ stringData:
nifiSensitivePropsKey: my-encryption-key
----

Configure the Secret and a different encryption algrithm:
Configure the Secret and a different sensitive properties algorithm:

[source,yaml]
----
Expand All @@ -408,6 +408,64 @@ sensitiveProperties:
algorithm: nifiArgon2AesGcm256
----

=== Upgrading sensitive properties algorithm

WARNING: Please make sure to backup any flows before upgrading the sensitive properties algorithm!

The sensitive properties algorithm can be changed via the `nifi.sh` CLI tool as described in the https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#updating-the-sensitive-properties-algorithm[Apache NiFi documentation].

Assuming that you deployed a cluster with this:

[source,yaml]
----
sensitiveProperties:
keySecret: nifi-sensitive-property-key
algorithm: nifiArgon2AesGcm256
----

If you want to change the algorithm to `nifiPbkdf2AesGcm256`, you have to run the following command on each NiFi Pod:

[source,bash]
----
/stackable/nifi/bin/nifi.sh set-sensitive-properties-algorithm NIFI_PBKDF2_AES_GCM_256
----

NOTE: Be careful with the notation used in the NiFiCluster `nifiPbkdf2AesGcm256` versus the setting in the NiFi CLI `NIFI_PBKDF2_AES_GCM_256`!

Alternatively, you can use this shell script to automatically execute this in each pod via `kubectl` (make sure to edit the `NAMESPACE` and `STATEFULSET_NAME` accordingly):

[source,bash]
----
NAMESPACE="default"
STATEFULSET_NAME="simple-nifi-node-default"
COMMAND="/stackable/nifi/bin/nifi.sh set-sensitive-properties-algorithm NIFI_PBKDF2_AES_GCM_256"

kubectl get pods -n "$NAMESPACE" --no-headers -o custom-columns=":metadata.name" | grep "^$STATEFULSET_NAME" | \
while read pod; do
echo "Running on $pod"
kubectl exec -n "$NAMESPACE" -c "nifi" "$pod" -- sh -c "$COMMAND"
done
----

Afterwards, update your NiFiCluster to the required algorithm `nifiPbkdf2AesGcm256`:

[source,yaml]
----
sensitiveProperties:
keySecret: nifi-sensitive-property-key
algorithm: nifiPbkdf2AesGcm256
----

Finally, apply the updated NiFiCluster and restart / delete the StatefulSet:

[source,bash]
----
kubectl apply -n "$NAMESPACE" -f <nifi-yaml>
kubectl delete -n "$NAMESPACE" statefulsets ${STATEFULSET_NAME}
----



[#host-header-check]
== Host Header Check
NiFi checks the host header of incoming requests and rejects them if they are passing through a proxy that is not on an allow-list configured in the `nifi.web.proxy.host` property.
Expand Down
40 changes: 29 additions & 11 deletions rust/operator-binary/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use strum::{Display, EnumIter};
use crate::{
crd::{
HTTPS_PORT, NifiConfig, NifiConfigFragment, NifiRole, NifiStorageConfig, PROTOCOL_PORT,
v1alpha1::{self, NifiClusteringBackend},
sensitive_properties, v1alpha1, v1alpha1::NifiClusteringBackend,
},
operations::graceful_shutdown::graceful_shutdown_config_properties,
security::{
Expand Down Expand Up @@ -101,6 +101,9 @@ pub enum Error {
"NiFi 1.x requires ZooKeeper (hint: upgrade to NiFi 2.x or set .spec.clusterConfig.zookeeperConfigMapName)"
))]
Nifi1RequiresZookeeper,

#[snafu(display("failed to configure sensitive properties"))]
ConfigureSensitiveProperties { source: sensitive_properties::Error },
}

/// Create the NiFi bootstrap.conf
Expand Down Expand Up @@ -156,15 +159,25 @@ pub fn build_nifi_properties(
// According to https://cwiki.apache.org/confluence/display/NIFI/Migration+Guidance#MigrationGuidance-Migratingto2.0.0-M1
// The nifi.flow.configuration.file property in nifi.properties must be changed to reference
// "flow.json.gz" instead of "flow.xml.gz"
let flow_file_name = if is_nifi_1 {
"flow.xml.gz"
// TODO: Remove once we dropped support for all 1.x.x versions
// TODO(malte): In order to use CLI tools like: ./bin/nifi.sh set-sensitive-properties-algorithm NIFI_PBKDF2_AES_GCM_256
// we have to set both "nifi.flow.configuration.file" and "nifi.flow.configuration.json.file" in NiFi 1.x.x.
if is_nifi_1 {
properties.insert(
"nifi.flow.configuration.file".to_string(),
NifiRepository::Database.mount_path() + "/flow.xml.gz",
);
properties.insert(
"nifi.flow.configuration.json.file".to_string(),
NifiRepository::Database.mount_path() + "/flow.json.gz",
);
} else {
"flow.json.gz"
};
properties.insert(
"nifi.flow.configuration.file".to_string(),
NifiRepository::Database.mount_path() + "/" + flow_file_name,
);
properties.insert(
"nifi.flow.configuration.file".to_string(),
NifiRepository::Database.mount_path() + "/flow.json.gz",
);
}

properties.insert(
"nifi.flow.configuration.archive.enabled".to_string(),
"true".to_string(),
Expand Down Expand Up @@ -483,15 +496,20 @@ pub fn build_nifi_properties(
"".to_string(),
);

let algorithm = &spec
let sensitive_properties_algorithm = &spec
.cluster_config
.sensitive_properties
.algorithm
.clone()
.unwrap_or_default();

sensitive_properties_algorithm
.check_for_nifi_version(spec.image.product_version())
.context(ConfigureSensitivePropertiesSnafu)?;

properties.insert(
"nifi.sensitive.props.algorithm".to_string(),
algorithm.to_string(),
sensitive_properties_algorithm.to_string(),
);

// key and trust store
Expand Down
67 changes: 2 additions & 65 deletions rust/operator-binary/src/crd/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
pub mod affinity;
pub mod authentication;
pub mod sensitive_properties;
pub mod tls;

use std::collections::BTreeMap;

use affinity::get_affinity;
use sensitive_properties::NifiSensitivePropertiesConfig;
use serde::{Deserialize, Serialize};
use snafu::{OptionExt, ResultExt, Snafu};
use stackable_operator::{
Expand Down Expand Up @@ -356,71 +358,6 @@ impl CurrentlySupportedListenerClasses {
}
}

/// These settings configure the encryption of sensitive properties in NiFi processors.
/// NiFi supports encrypting sensitive properties in processors as they are written to disk.
/// You can configure the encryption algorithm and the key to use.
/// You can also let the operator generate an encryption key for you.
#[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NifiSensitivePropertiesConfig {
/// A reference to a Secret. The Secret needs to contain a key `nifiSensitivePropsKey`.
/// If `autoGenerate` is false and this object is missing, the Operator will raise an error.
/// The encryption key needs to be at least 12 characters long.
pub key_secret: String,

/// Whether to generate the `keySecret` if it is missing.
/// Defaults to `false`.
#[serde(default)]
pub auto_generate: bool,

/// This is setting the `nifi.sensitive.props.algorithm` property in NiFi.
/// This setting configures the encryption algorithm to use to encrypt sensitive properties.
/// Valid values are:
///
/// `nifiPbkdf2AesGcm256` (the default value),
/// `nifiArgon2AesGcm256`,
///
/// The following algorithms are deprecated and will be removed in future versions:
///
/// `nifiArgon2AesGcm128`,
/// `nifiBcryptAesGcm128`,
/// `nifiBcryptAesGcm256`,
/// `nifiPbkdf2AesGcm128`,
/// `nifiScryptAesGcm128`,
/// `nifiScryptAesGcm256`.
///
/// Learn more about the specifics of the algorithm parameters in the
/// [NiFi documentation](https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#property-encryption-algorithms).
pub algorithm: Option<NifiSensitiveKeyAlgorithm>,
}

#[derive(strum::Display, Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum NifiSensitiveKeyAlgorithm {
#[strum(serialize = "NIFI_ARGON2_AES_GCM_128")]
NifiArgon2AesGcm128,
#[strum(serialize = "NIFI_ARGON2_AES_GCM_256")]
NifiArgon2AesGcm256,
#[strum(serialize = "NIFI_BCRYPT_AES_GCM_128")]
NifiBcryptAesGcm128,
#[strum(serialize = "NIFI_BCRYPT_AES_GCM_256")]
NifiBcryptAesGcm256,
#[strum(serialize = "NIFI_PBKDF2_AES_GCM_128")]
NifiPbkdf2AesGcm128,
#[strum(serialize = "NIFI_PBKDF2_AES_GCM_256")]
NifiPbkdf2AesGcm256,
#[strum(serialize = "NIFI_SCRYPT_AES_GCM_128")]
NifiScryptAesGcm128,
#[strum(serialize = "NIFI_SCRYPT_AES_GCM_256")]
NifiScryptAesGcm256,
}

impl Default for NifiSensitiveKeyAlgorithm {
fn default() -> Self {
Self::NifiArgon2AesGcm256
}
}

#[derive(strum::Display, Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum StoreType {
Expand Down
113 changes: 113 additions & 0 deletions rust/operator-binary/src/crd/sensitive_properties.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use serde::{Deserialize, Serialize};
use snafu::Snafu;
use stackable_operator::schemars::{self, JsonSchema};

#[derive(Snafu, Debug)]
pub enum Error {
#[snafu(display(
"The sensitive properties algorithm '{algorithm}' is not supported in NiFi 2.X.X. Please see https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#updating-the-sensitive-properties-algorithm on how to upgrade the algorithm."
))]
UnsupportedSensitivePropertiesAlgorithm { algorithm: String },
}

/// These settings configure the encryption of sensitive properties in NiFi processors.
/// NiFi supports encrypting sensitive properties in processors as they are written to disk.
/// You can configure the encryption algorithm and the key to use.
/// You can also let the operator generate an encryption key for you.
#[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NifiSensitivePropertiesConfig {
/// A reference to a Secret. The Secret needs to contain a key `nifiSensitivePropsKey`.
/// If `autoGenerate` is false and this object is missing, the Operator will raise an error.
/// The encryption key needs to be at least 12 characters long.
pub key_secret: String,

/// Whether to generate the `keySecret` if it is missing.
/// Defaults to `false`.
#[serde(default)]
pub auto_generate: bool,

/// This is setting the `nifi.sensitive.props.algorithm` property in NiFi.
/// This setting configures the encryption algorithm to use to encrypt sensitive properties.
/// Valid values are:
///
/// `nifiPbkdf2AesGcm256` (the default value),
/// `nifiArgon2AesGcm256`,
///
/// The following algorithms are deprecated and will be removed in future versions:
///
/// `nifiArgon2AesGcm128`,
/// `nifiBcryptAesGcm128`,
/// `nifiBcryptAesGcm256`,
/// `nifiPbkdf2AesGcm128`,
/// `nifiScryptAesGcm128`,
/// `nifiScryptAesGcm256`.
///
/// Learn more about the specifics of the algorithm parameters in the
/// [NiFi documentation](https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#property-encryption-algorithms).
pub algorithm: Option<NifiSensitiveKeyAlgorithm>,
}

#[derive(strum::Display, Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum NifiSensitiveKeyAlgorithm {
// supported in v2
#[strum(serialize = "NIFI_PBKDF2_AES_GCM_256")]
NifiPbkdf2AesGcm256,
// supported in v2
#[strum(serialize = "NIFI_ARGON2_AES_GCM_256")]
NifiArgon2AesGcm256,
// Deprecated in v1 -> can be removed when 1.x.x is no longer supported
#[strum(serialize = "NIFI_BCRYPT_AES_GCM_128")]
NifiBcryptAesGcm128,
// Deprecated in v1 -> can be removed when 1.x.x is no longer supported
#[strum(serialize = "NIFI_BCRYPT_AES_GCM_256")]
NifiBcryptAesGcm256,
// Deprecated in v1 -> can be removed when 1.x.x is no longer supported
#[strum(serialize = "NIFI_PBKDF2_AES_GCM_128")]
NifiPbkdf2AesGcm128,
// Deprecated in v1 -> can be removed when 1.x.x is no longer supported
#[strum(serialize = "NIFI_ARGON2_AES_GCM_128")]
NifiArgon2AesGcm128,
// Deprecated in v1 -> can be removed when 1.x.x is no longer supported
#[strum(serialize = "NIFI_SCRYPT_AES_GCM_128")]
NifiScryptAesGcm128,
// Deprecated in v1 -> can be removed when 1.x.x is no longer supported
#[strum(serialize = "NIFI_SCRYPT_AES_GCM_256")]
NifiScryptAesGcm256,
}

impl NifiSensitiveKeyAlgorithm {
/// Checks if the used encryption algorithm is supported or deprecated.
/// Will warn for deprecation and error out for missing support.
pub fn check_for_nifi_version(&self, product_version: &str) -> Result<(), Error> {
let algorithm = self.to_string();

match self {
// Allowed and supported in NiFi 1.x.x and 2.x.x
NifiSensitiveKeyAlgorithm::NifiPbkdf2AesGcm256
| NifiSensitiveKeyAlgorithm::NifiArgon2AesGcm256 => {}
// All others are deprecated in 1.x.x and removed in 2.x.x
// see https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#property-encryption-algorithms
_ => {
if product_version.starts_with("1.") {
tracing::warn!(
"You are using a deprecated sensitive properties algorithm '{algorithm}'. Please update to '{pbkd}' or '{argon}'.",
pbkd = NifiSensitiveKeyAlgorithm::NifiPbkdf2AesGcm256,
argon = NifiSensitiveKeyAlgorithm::NifiArgon2AesGcm256
)
} else {
return Err(Error::UnsupportedSensitivePropertiesAlgorithm { algorithm });
}
}
}

Ok(())
}
}

impl Default for NifiSensitiveKeyAlgorithm {
fn default() -> Self {
Self::NifiArgon2AesGcm256
}
}
Loading