Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion deploy/helm/opensearch-operator/crds/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,24 @@ spec:
generates in the [operator documentation](https://docs.stackable.tech/home/nightly/opensearch/).
properties:
clusterConfig:
default: {}
default:
tls:
secretClass: null
description: Configuration that applies to all roles and role groups
properties:
tls:
properties:
secretClass:
description: |-
Affects client connections and internal transport connections.
This setting controls:
- If TLS encryption is used at all
- Which cert the servers should use to authenticate themselves against the client
maxLength: 223
minLength: 1
nullable: true
type: string
type: object
vectorAggregatorConfigMapName:
description: |-
Name of the Vector aggregator [discovery ConfigMap](https://docs.stackable.tech/home/nightly/concepts/service_discovery).
Expand All @@ -42,6 +57,8 @@ spec:
minLength: 1
nullable: true
type: string
required:
- tls
type: object
clusterOperation:
default:
Expand Down Expand Up @@ -303,6 +320,14 @@ spec:
type: string
nullable: true
type: array
requestedSecretLifetime:
description: |-
Request secret (currently only autoTls certificates) lifetime from the secret operator, e.g. `7d`, or `30d`.
This can be shortened by the `maxCertificateLifetime` setting on the SecretClass issuing the TLS certificate.

Defaults to 1d.
nullable: true
type: string
resources:
default:
cpu:
Expand Down Expand Up @@ -651,6 +676,14 @@ spec:
type: string
nullable: true
type: array
requestedSecretLifetime:
description: |-
Request secret (currently only autoTls certificates) lifetime from the secret operator, e.g. `7d`, or `30d`.
This can be shortened by the `maxCertificateLifetime` setting on the SecretClass issuing the TLS certificate.

Defaults to 1d.
nullable: true
type: string
resources:
default:
cpu:
Expand Down
16 changes: 14 additions & 2 deletions rust/operator-binary/src/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use validate::validate;
use crate::{
crd::{
NodeRoles,
v1alpha1::{self},
v1alpha1::{self, OpenSearchClusterConfig},
},
framework::{
ClusterName, ControllerName, HasName, HasUid, ListenerClassName, NameIsValidLabelValue,
Expand Down Expand Up @@ -131,6 +131,7 @@ pub struct ValidatedOpenSearchConfig {
pub listener_class: ListenerClassName,
pub logging: ValidatedLogging,
pub node_roles: NodeRoles,
pub requested_secret_lifetime: Duration,
pub resources: OpenSearchNodeResources,
pub termination_grace_period_seconds: i64,
}
Expand Down Expand Up @@ -164,17 +165,20 @@ pub struct ValidatedCluster {
pub name: ClusterName,
pub namespace: NamespaceName,
pub uid: Uid,
pub cluster_config: OpenSearchClusterConfig,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If OpenSearchClusterConfig is extended in the future, then it will probably contain unvalidated entries. If the whole structure is added now to ValidatedCluster, then it will have to be removed again and all the TLS related code has to be refactored.

Additionally, OpenSearchClusterConfig contains vector_aggregator_config_map_name which is already contained in role_group_configs.logging.vector_container.vector_aggregator_config_map_name.

I would directly add the OpenSearchTls structure here.

pub role_config: GenericRoleConfig,
pub role_group_configs: BTreeMap<RoleGroupName, OpenSearchRoleGroupConfig>,
}

impl ValidatedCluster {
#[allow(clippy::too_many_arguments)]
pub fn new(
image: ResolvedProductImage,
product_version: ProductVersion,
name: ClusterName,
namespace: NamespaceName,
uid: impl Into<Uid>,
cluster_config: OpenSearchClusterConfig,
role_config: GenericRoleConfig,
role_group_configs: BTreeMap<RoleGroupName, OpenSearchRoleGroupConfig>,
) -> Self {
Expand All @@ -191,6 +195,7 @@ impl ValidatedCluster {
name,
namespace,
uid,
cluster_config,
role_config,
role_group_configs,
}
Expand Down Expand Up @@ -372,13 +377,17 @@ mod tests {
kvp::LabelValue,
product_logging::spec::AutomaticContainerLogConfig,
role_utils::GenericRoleConfig,
shared::time::Duration,
};
use uuid::uuid;

use super::{Context, OpenSearchRoleGroupConfig, ValidatedCluster, ValidatedLogging};
use crate::{
controller::{OpenSearchNodeResources, ValidatedOpenSearchConfig},
crd::{NodeRoles, v1alpha1},
crd::{
NodeRoles,
v1alpha1::{self, OpenSearchClusterConfig},
},
framework::{
ClusterName, ListenerClassName, NamespaceName, OperatorName, ProductVersion,
RoleGroupName, builder::pod::container::EnvVarSet,
Expand Down Expand Up @@ -460,6 +469,7 @@ mod tests {
ClusterName::from_str_unsafe("my-opensearch"),
NamespaceName::from_str_unsafe("default"),
uuid!("e6ac237d-a6d4-43a1-8135-f36506110912"),
OpenSearchClusterConfig::default(),
GenericRoleConfig::default(),
[
(
Expand Down Expand Up @@ -513,6 +523,8 @@ mod tests {
vector_container: None,
},
node_roles: NodeRoles(node_roles.to_vec()),
requested_secret_lifetime: Duration::from_str("15d")
.expect("should be a valid duration"),
resources: OpenSearchNodeResources::default(),
termination_grace_period_seconds: 120,
},
Expand Down
9 changes: 8 additions & 1 deletion rust/operator-binary/src/controller/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ mod tests {
kvp::LabelValue,
product_logging::spec::AutomaticContainerLogConfig,
role_utils::GenericRoleConfig,
shared::time::Duration,
};
use uuid::uuid;

Expand All @@ -77,7 +78,10 @@ mod tests {
ContextNames, OpenSearchNodeResources, OpenSearchRoleGroupConfig, ValidatedCluster,
ValidatedContainerLogConfigChoice, ValidatedLogging, ValidatedOpenSearchConfig,
},
crd::{NodeRoles, v1alpha1},
crd::{
NodeRoles,
v1alpha1::{self, OpenSearchClusterConfig},
},
framework::{
ClusterName, ControllerName, ListenerClassName, NamespaceName, OperatorName,
ProductName, ProductVersion, RoleGroupName, builder::pod::container::EnvVarSet,
Expand Down Expand Up @@ -168,6 +172,7 @@ mod tests {
ClusterName::from_str_unsafe("my-opensearch"),
NamespaceName::from_str_unsafe("default"),
uuid!("e6ac237d-a6d4-43a1-8135-f36506110912"),
OpenSearchClusterConfig::default(),
GenericRoleConfig::default(),
[
(
Expand Down Expand Up @@ -210,6 +215,8 @@ mod tests {
vector_container: None,
},
node_roles: NodeRoles(node_roles.to_vec()),
requested_secret_lifetime: Duration::from_str("15d")
.expect("should be a valid duration"),
resources: OpenSearchNodeResources::default(),
termination_grace_period_seconds: 120,
},
Expand Down
122 changes: 106 additions & 16 deletions rust/operator-binary/src/controller/build/node_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,41 @@ pub const CONFIG_OPTION_PLUGINS_SECURITY_NODES_DN: &str = "plugins.security.node
pub const CONFIG_OPTION_PLUGINS_SECURITY_SSL_HTTP_ENABLED: &str =
"plugins.security.ssl.http.enabled";

/// Path to the cert PEM file used for TLS on the HTTP PORT.
/// type: string
pub const CONFIG_OPTION_PLUGINS_SECURITY_SSL_HTTP_PEMCERT_FILEPATH: &str =
"plugins.security.ssl.http.pemcert_filepath";

/// Path to the key PEM file used for TLS on the HTTP PORT.
/// type: string
pub const CONFIG_OPTION_PLUGINS_SECURITY_SSL_HTTP_PEMKEY_FILEPATH: &str =
"plugins.security.ssl.http.pemkey_filepath";

/// Path to the trusted CAs PEM file used for TLS on the HTTP PORT.
/// type: string
pub const CONFIG_OPTION_PLUGINS_SECURITY_SSL_HTTP_PEMTRUSTEDCAS_FILEPATH: &str =
"plugins.security.ssl.http.pemtrustedcas_filepath";

/// Whether to enable TLS on internal node-to-node communication using the transport port.
/// type: boolean
pub const CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_ENABLED: &str =
"plugins.security.ssl.transport.enabled";

/// Path to the cert PEM file used for TLS on the transport PORT.
/// type: string
pub const CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_PEMCERT_FILEPATH: &str =
"plugins.security.ssl.transport.pemcert_filepath";

/// Path to the key PEM file used for TLS on the transport PORT.
/// type: string
pub const CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_PEMKEY_FILEPATH: &str =
"plugins.security.ssl.transport.pemkey_filepath";

/// Path to the trusted CAs PEM file used for TLS on the transport PORT.
/// type: string
pub const CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_PEMTRUSTEDCAS_FILEPATH: &str =
"plugins.security.ssl.transport.pemtrustedcas_filepath";

/// Configuration of an OpenSearch node based on the cluster and role-group configuration
pub struct NodeConfig {
cluster: ValidatedCluster,
Expand All @@ -81,8 +116,31 @@ impl NodeConfig {
}

/// Creates the main OpenSearch configuration file in YAML format
pub fn static_opensearch_config_file_content(&self) -> String {
Self::to_yaml(self.static_opensearch_config())
pub fn opensearch_config_file_content(&self) -> String {
Self::to_yaml(self.opensearch_config())
}

pub fn opensearch_config(&self) -> serde_json::Map<String, Value> {
let mut config = self.static_opensearch_config();

if self.cluster.cluster_config.tls.secret_class.is_some() {
config.append(&mut self.tls_config());
}

for (setting, value) in self
.role_group_config
.config_overrides
.get(CONFIGURATION_FILE_OPENSEARCH_YML)
.into_iter()
.flatten()
{
config.insert(setting.to_owned(), json!(value));
}

// Ensure a deterministic result
config.sort_keys();

config
}

/// Creates the main OpenSearch configuration file as JSON map
Expand Down Expand Up @@ -112,25 +170,53 @@ impl NodeConfig {
json!(["CN=generated certificate for pod".to_owned()]),
);

for (setting, value) in self
.role_group_config
.config_overrides
.get(CONFIGURATION_FILE_OPENSEARCH_YML)
.into_iter()
.flatten()
{
config.insert(setting.to_owned(), json!(value));
}
config
}

// Ensure a deterministic result
config.sort_keys();
pub fn tls_config(&self) -> serde_json::Map<String, Value> {
let mut config = serde_json::Map::new();

// TLS config for HTTP port
config.insert(
CONFIG_OPTION_PLUGINS_SECURITY_SSL_HTTP_ENABLED.to_owned(),
json!("true".to_string()),
);
config.insert(
CONFIG_OPTION_PLUGINS_SECURITY_SSL_HTTP_PEMCERT_FILEPATH.to_owned(),
json!("${OPENSEARCH_PATH_CONF}/tls/tls.crt".to_string()),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the value of $OPENSEARCH_PATH_CONF is correct in my opinion but if it is not set, the value of $OPENSEARCH_HOME/config is the correct one.

Currently, this exception is thrown in the integration tests:

Exception in thread "main" java.lang.IllegalArgumentException: Could not resolve placeholder 'OPENSEARCH_PATH_CONF'

The resolution of the actual path is already done in

// Use `OPENSEARCH_HOME` from envOverrides or default to `DEFAULT_OPENSEARCH_HOME`.
let opensearch_home = env_vars
.get(&EnvVarName::from_str_unsafe("OPENSEARCH_HOME"))
.and_then(|env_var| env_var.value.clone())
.unwrap_or(DEFAULT_OPENSEARCH_HOME.to_owned());
// Use `OPENSEARCH_PATH_CONF` from envOverrides or default to `OPENSEARCH_HOME/config`,
// i.e. depend on `OPENSEARCH_HOME`.
let opensearch_path_conf = env_vars
.get(&EnvVarName::from_str_unsafe("OPENSEARCH_PATH_CONF"))
.and_then(|env_var| env_var.value.clone())
.unwrap_or(format!("{opensearch_home}/config"));
, because there it is used for VolumeMounts which cannot depend on environment variables.

But if the location is decided in the volume mount then it is wrong to depend on an environment variable here. Therefore, it would be better to move the mentioned code to this module. The code depends anyway only on the environment variables in this module. Then the resolved path can be used here.

);
config.insert(
CONFIG_OPTION_PLUGINS_SECURITY_SSL_HTTP_PEMKEY_FILEPATH.to_owned(),
json!("${OPENSEARCH_PATH_CONF}/tls/tls.key".to_string()),
);
config.insert(
CONFIG_OPTION_PLUGINS_SECURITY_SSL_HTTP_PEMTRUSTEDCAS_FILEPATH.to_owned(),
json!("${OPENSEARCH_PATH_CONF}/tls/ca.crt".to_string()),
);
// TLS config for TRANSPORT port
config.insert(
CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_ENABLED.to_owned(),
json!("true".to_string()),
);
config.insert(
CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_PEMCERT_FILEPATH.to_owned(),
json!("${OPENSEARCH_PATH_CONF}/tls/tls.crt".to_string()),
);
config.insert(
CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_PEMKEY_FILEPATH.to_owned(),
json!("${OPENSEARCH_PATH_CONF}/tls/tls.key".to_string()),
);
config.insert(
CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_PEMTRUSTEDCAS_FILEPATH.to_owned(),
json!("${OPENSEARCH_PATH_CONF}/tls/ca.crt".to_string()),
);

config
}

/// Returns `true` if TLS is enabled on the HTTP port
pub fn tls_on_http_port_enabled(&self) -> bool {
self.static_opensearch_config()
self.opensearch_config()
.get(CONFIG_OPTION_PLUGINS_SECURITY_SSL_HTTP_ENABLED)
.and_then(Self::value_as_bool)
== Some(true)
Expand Down Expand Up @@ -277,13 +363,14 @@ mod tests {
kvp::LabelValue,
product_logging::spec::AutomaticContainerLogConfig,
role_utils::GenericRoleConfig,
shared::time::Duration,
};
use uuid::uuid;

use super::*;
use crate::{
controller::{ValidatedLogging, ValidatedOpenSearchConfig},
crd::NodeRoles,
crd::{NodeRoles, v1alpha1::OpenSearchClusterConfig},
framework::{
ClusterName, ListenerClassName, NamespaceName, ProductVersion, RoleGroupName,
product_logging::framework::ValidatedContainerLogConfigChoice,
Expand Down Expand Up @@ -328,6 +415,8 @@ mod tests {
v1alpha1::NodeRole::Ingest,
v1alpha1::NodeRole::RemoteClusterClient,
]),
requested_secret_lifetime: Duration::from_str("15d")
.expect("should be a valid duration"),
resources: Resources::default(),
termination_grace_period_seconds: 30,
},
Expand Down Expand Up @@ -364,6 +453,7 @@ mod tests {
ClusterName::from_str_unsafe("my-opensearch-cluster"),
NamespaceName::from_str_unsafe("default"),
uuid!("0b1e30e6-326e-4c1a-868d-ad6598b49e8b"),
OpenSearchClusterConfig::default(),
GenericRoleConfig::default(),
[(
RoleGroupName::from_str_unsafe("default"),
Expand Down Expand Up @@ -395,7 +485,7 @@ mod tests {
"test: \"value\""
)
.to_owned(),
node_config.static_opensearch_config_file_content()
node_config.opensearch_config_file_content()
);
}

Expand Down
Loading