diff --git a/CHANGELOG.md b/CHANGELOG.md index 125a279f..ace82fe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 @@ -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 diff --git a/deploy/helm/nifi-operator/crds/crds.yaml b/deploy/helm/nifi-operator/crds/crds.yaml index 6d4d1f3d..d01d7486 100644 --- a/deploy/helm/nifi-operator/crds/crds.yaml +++ b/deploy/helm/nifi-operator/crds/crds.yaml @@ -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 diff --git a/docs/modules/nifi/pages/usage_guide/security.adoc b/docs/modules/nifi/pages/usage_guide/security.adoc index 883ccebe..9cb85311 100644 --- a/docs/modules/nifi/pages/usage_guide/security.adoc +++ b/docs/modules/nifi/pages/usage_guide/security.adoc @@ -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`: @@ -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: @@ -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] ---- @@ -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 +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. diff --git a/rust/operator-binary/src/config/mod.rs b/rust/operator-binary/src/config/mod.rs index 477cd0f7..f01596b5 100644 --- a/rust/operator-binary/src/config/mod.rs +++ b/rust/operator-binary/src/config/mod.rs @@ -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::{ @@ -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 @@ -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(), @@ -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 diff --git a/rust/operator-binary/src/crd/mod.rs b/rust/operator-binary/src/crd/mod.rs index 85fdff38..1eda9314 100644 --- a/rust/operator-binary/src/crd/mod.rs +++ b/rust/operator-binary/src/crd/mod.rs @@ -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::{ @@ -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, -} - -#[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 { diff --git a/rust/operator-binary/src/crd/sensitive_properties.rs b/rust/operator-binary/src/crd/sensitive_properties.rs new file mode 100644 index 00000000..affc9c4f --- /dev/null +++ b/rust/operator-binary/src/crd/sensitive_properties.rs @@ -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, +} + +#[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 + } +}