Skip to content
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