Skip to content

Commit 8257b95

Browse files
committed
address feedback on PR
1 parent bcb5542 commit 8257b95

24 files changed

Lines changed: 351 additions & 163 deletions

deploy/helm/opensearch-operator/crds/crds.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,17 @@ spec:
3131
clusterConfig:
3232
default:
3333
tls:
34-
restSecretClass: tls
34+
httpSecretClass: tls
3535
transportSecretClass: tls
3636
description: Configuration that applies to all roles and role groups
3737
properties:
3838
tls:
3939
default:
40-
restSecretClass: tls
40+
httpSecretClass: tls
4141
transportSecretClass: tls
4242
description: TLS configuration options for the REST API and internal communication (transport).
4343
properties:
44-
restSecretClass:
44+
httpSecretClass:
4545
default: tls
4646
description: |-
4747
Only affects client connections to the REST API.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
= Security
2+
:description: Configure TLS encryption, authentication, and Open Policy Agent (OPA) authorization for Kafka with the Stackable Operator.
3+
4+
== TLS
5+
6+
The internal and client communication at the REST API can be encrypted with TLS. This requires the xref:secret-operator:index.adoc[Secret Operator] to be running in the Kubernetes cluster providing certificates.
7+
The used certificates can be changed in a cluster-wide config. TLS encryption on the REST API may be disabled, while it is always enabled for the internal communication between nodes using the `transport` port.
8+
9+
[source,yaml]
10+
----
11+
---
12+
apiVersion: opensearch.stackable.tech/v1alpha1
13+
kind: OpenSearchCluster
14+
metadata:
15+
name: opensearch
16+
spec:
17+
image:
18+
productVersion: 3.1.0
19+
clusterConfig:
20+
tls:
21+
httpSecretClass: tls # <1>
22+
transportSecretClass: opensearch-transport-tls # <2>
23+
nodes:
24+
config:
25+
requestedSecretLifetime: 7d # <3>
26+
roleGroups:
27+
default:
28+
replicas: 3
29+
----
30+
<1> The `spec.clusterConfig.tls.httpSecretClass` refers to the client-to-server encryption at the REST API. Defaults to the `tls` SecretClass and can be disabled by setting `httpSecretClass` to `null`.
31+
<2> The `spec.clusterConfig.tls.transportSecretClass` refers to the internal encryption between OpenSearch nodes using mTLS (transport). Defaults to the `tls` SecretClass` and can't be disabled.
32+
<3> The lifetime for autoTls certificates generated by the secret operator.
33+
Only a lifetime up to the `maxCertificateLifetime` setting in the SecretClass is applied.
34+
35+
The `tls` secret is deployed from the xref:secret-operator:index.adoc[Secret Operator] and looks like this:
36+
37+
[source,yaml]
38+
----
39+
---
40+
apiVersion: secrets.stackable.tech/v1alpha1
41+
kind: SecretClass
42+
metadata:
43+
name: tls
44+
spec:
45+
backend:
46+
autoTls:
47+
ca:
48+
secret:
49+
name: secret-provisioner-tls-ca
50+
namespace: default
51+
autoGenerate: true
52+
maxCertificateLifetime: 15d
53+
----
54+
55+
You can create your own secrets and reference them e.g. in the `spec.clusterConfig.tls.httpSecretClass` or `spec.clusterConfig.tls.transportSecretClass` to use different certificates.

rust/operator-binary/src/controller.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use validate::validate;
3030
use crate::{
3131
crd::{
3232
NodeRoles,
33-
v1alpha1::{self, OpenSearchClusterConfig},
33+
v1alpha1::{self, OpenSearchTls},
3434
},
3535
framework::{
3636
ClusterName, ControllerName, HasName, HasUid, ListenerClassName, NameIsValidLabelValue,
@@ -165,9 +165,9 @@ pub struct ValidatedCluster {
165165
pub name: ClusterName,
166166
pub namespace: NamespaceName,
167167
pub uid: Uid,
168-
pub cluster_config: OpenSearchClusterConfig,
169168
pub role_config: GenericRoleConfig,
170169
pub role_group_configs: BTreeMap<RoleGroupName, OpenSearchRoleGroupConfig>,
170+
pub tls_config: OpenSearchTls,
171171
}
172172

173173
impl ValidatedCluster {
@@ -178,9 +178,9 @@ impl ValidatedCluster {
178178
name: ClusterName,
179179
namespace: NamespaceName,
180180
uid: impl Into<Uid>,
181-
cluster_config: OpenSearchClusterConfig,
182181
role_config: GenericRoleConfig,
183182
role_group_configs: BTreeMap<RoleGroupName, OpenSearchRoleGroupConfig>,
183+
tls_config: OpenSearchTls,
184184
) -> Self {
185185
let uid = uid.into();
186186
ValidatedCluster {
@@ -195,9 +195,9 @@ impl ValidatedCluster {
195195
name,
196196
namespace,
197197
uid,
198-
cluster_config,
199198
role_config,
200199
role_group_configs,
200+
tls_config,
201201
}
202202
}
203203

@@ -386,7 +386,7 @@ mod tests {
386386
controller::{OpenSearchNodeResources, ValidatedOpenSearchConfig},
387387
crd::{
388388
NodeRoles,
389-
v1alpha1::{self, OpenSearchClusterConfig},
389+
v1alpha1::{self, OpenSearchTls},
390390
},
391391
framework::{
392392
ClusterName, ListenerClassName, NamespaceName, OperatorName, ProductVersion,
@@ -469,7 +469,6 @@ mod tests {
469469
ClusterName::from_str_unsafe("my-opensearch"),
470470
NamespaceName::from_str_unsafe("default"),
471471
uuid!("e6ac237d-a6d4-43a1-8135-f36506110912"),
472-
OpenSearchClusterConfig::default(),
473472
GenericRoleConfig::default(),
474473
[
475474
(
@@ -504,6 +503,7 @@ mod tests {
504503
),
505504
]
506505
.into(),
506+
OpenSearchTls::default(),
507507
)
508508
}
509509

@@ -523,7 +523,7 @@ mod tests {
523523
vector_container: None,
524524
},
525525
node_roles: NodeRoles(node_roles.to_vec()),
526-
requested_secret_lifetime: Duration::from_str("15d")
526+
requested_secret_lifetime: Duration::from_str("1d")
527527
.expect("should be a valid duration"),
528528
resources: OpenSearchNodeResources::default(),
529529
termination_grace_period_seconds: 120,

rust/operator-binary/src/controller/build.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ mod tests {
8080
},
8181
crd::{
8282
NodeRoles,
83-
v1alpha1::{self, OpenSearchClusterConfig},
83+
v1alpha1::{self, OpenSearchTls},
8484
},
8585
framework::{
8686
ClusterName, ControllerName, ListenerClassName, NamespaceName, OperatorName,
@@ -172,7 +172,6 @@ mod tests {
172172
ClusterName::from_str_unsafe("my-opensearch"),
173173
NamespaceName::from_str_unsafe("default"),
174174
uuid!("e6ac237d-a6d4-43a1-8135-f36506110912"),
175-
OpenSearchClusterConfig::default(),
176175
GenericRoleConfig::default(),
177176
[
178177
(
@@ -196,6 +195,7 @@ mod tests {
196195
),
197196
]
198197
.into(),
198+
OpenSearchTls::default(),
199199
)
200200
}
201201

@@ -215,7 +215,7 @@ mod tests {
215215
vector_container: None,
216216
},
217217
node_roles: NodeRoles(node_roles.to_vec()),
218-
requested_secret_lifetime: Duration::from_str("15d")
218+
requested_secret_lifetime: Duration::from_str("1d")
219219
.expect("should be a valid duration"),
220220
resources: OpenSearchNodeResources::default(),
221221
termination_grace_period_seconds: 120,

rust/operator-binary/src/controller/build/node_config.rs

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ pub const CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_PEMKEY_FILEPATH: &str =
9393
pub const CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_PEMTRUSTEDCAS_FILEPATH: &str =
9494
"plugins.security.ssl.transport.pemtrustedcas_filepath";
9595

96+
const DEFAULT_OPENSEARCH_HOME: &str = "/stackable/opensearch";
97+
9698
/// Configuration of an OpenSearch node based on the cluster and role-group configuration
9799
pub struct NodeConfig {
98100
cluster: ValidatedCluster,
@@ -173,6 +175,7 @@ impl NodeConfig {
173175

174176
pub fn tls_config(&self) -> serde_json::Map<String, Value> {
175177
let mut config = serde_json::Map::new();
178+
let opensearch_path_conf = self.opensearch_path_conf();
176179

177180
// TLS config for TRANSPORT port which is always enabled.
178181
config.insert(
@@ -181,34 +184,34 @@ impl NodeConfig {
181184
);
182185
config.insert(
183186
CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_PEMCERT_FILEPATH.to_owned(),
184-
json!("${OPENSEARCH_PATH_CONF}/tls/transport/tls.crt".to_string()),
187+
json!(format!("{opensearch_path_conf}/tls/transport/tls.crt")),
185188
);
186189
config.insert(
187190
CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_PEMKEY_FILEPATH.to_owned(),
188-
json!("${OPENSEARCH_PATH_CONF}/tls/transport/tls.key".to_string()),
191+
json!(format!("{opensearch_path_conf}/tls/transport/tls.key")),
189192
);
190193
config.insert(
191194
CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_PEMTRUSTEDCAS_FILEPATH.to_owned(),
192-
json!("${OPENSEARCH_PATH_CONF}/tls/transport/ca.crt".to_string()),
195+
json!(format!("{opensearch_path_conf}/tls/transport/ca.crt")),
193196
);
194197

195198
// TLS config for HTTP port which is optional.
196-
if self.cluster.cluster_config.tls.rest_secret_class.is_some() {
199+
if self.cluster.tls_config.http_secret_class.is_some() {
197200
config.insert(
198201
CONFIG_OPTION_PLUGINS_SECURITY_SSL_HTTP_ENABLED.to_owned(),
199202
json!("true".to_string()),
200203
);
201204
config.insert(
202205
CONFIG_OPTION_PLUGINS_SECURITY_SSL_HTTP_PEMCERT_FILEPATH.to_owned(),
203-
json!("${OPENSEARCH_PATH_CONF}/tls/rest/tls.crt".to_string()),
206+
json!(format!("{opensearch_path_conf}/tls/http/tls.crt")),
204207
);
205208
config.insert(
206209
CONFIG_OPTION_PLUGINS_SECURITY_SSL_HTTP_PEMKEY_FILEPATH.to_owned(),
207-
json!("${OPENSEARCH_PATH_CONF}/tls/rest/tls.key".to_string()),
210+
json!(format!("{opensearch_path_conf}/tls/http/tls.key")),
208211
);
209212
config.insert(
210213
CONFIG_OPTION_PLUGINS_SECURITY_SSL_HTTP_PEMTRUSTEDCAS_FILEPATH.to_owned(),
211-
json!("${OPENSEARCH_PATH_CONF}/tls/rest/ca.crt".to_string()),
214+
json!(format!("{opensearch_path_conf}/tls/http/ca.crt")),
212215
);
213216
} else {
214217
config.insert(
@@ -353,6 +356,23 @@ impl NodeConfig {
353356
String::new()
354357
}
355358
}
359+
360+
/// Return content of the `OPENSEARCH_HOME` environment variable from envOverrides or default to `DEFAULT_OPENSEARCH_HOME`
361+
pub fn opensearch_home(&self) -> String {
362+
self.environment_variables()
363+
.get(&EnvVarName::from_str_unsafe("OPENSEARCH_HOME"))
364+
.and_then(|env_var| env_var.value.clone())
365+
.unwrap_or(DEFAULT_OPENSEARCH_HOME.to_owned())
366+
}
367+
368+
/// Return content of the `OPENSEARCH_PATH_CONF` environment variable from envOverrides or default to `OPENSEARCH_HOME/config`
369+
pub fn opensearch_path_conf(&self) -> String {
370+
let opensearch_home = self.opensearch_home();
371+
self.environment_variables()
372+
.get(&EnvVarName::from_str_unsafe("OPENSEARCH_PATH_CONF"))
373+
.and_then(|env_var| env_var.value.clone())
374+
.unwrap_or(format!("{opensearch_home}/config"))
375+
}
356376
}
357377

358378
#[cfg(test)]
@@ -376,7 +396,7 @@ mod tests {
376396
use super::*;
377397
use crate::{
378398
controller::{ValidatedLogging, ValidatedOpenSearchConfig},
379-
crd::{NodeRoles, v1alpha1::OpenSearchClusterConfig},
399+
crd::{NodeRoles, v1alpha1::OpenSearchTls},
380400
framework::{
381401
ClusterName, ListenerClassName, NamespaceName, ProductVersion, RoleGroupName,
382402
product_logging::framework::ValidatedContainerLogConfigChoice,
@@ -421,7 +441,7 @@ mod tests {
421441
v1alpha1::NodeRole::Ingest,
422442
v1alpha1::NodeRole::RemoteClusterClient,
423443
]),
424-
requested_secret_lifetime: Duration::from_str("15d")
444+
requested_secret_lifetime: Duration::from_str("1d")
425445
.expect("should be a valid duration"),
426446
resources: Resources::default(),
427447
termination_grace_period_seconds: 30,
@@ -459,13 +479,13 @@ mod tests {
459479
ClusterName::from_str_unsafe("my-opensearch-cluster"),
460480
NamespaceName::from_str_unsafe("default"),
461481
uuid!("0b1e30e6-326e-4c1a-868d-ad6598b49e8b"),
462-
OpenSearchClusterConfig::default(),
463482
GenericRoleConfig::default(),
464483
[(
465484
RoleGroupName::from_str_unsafe("default"),
466485
role_group_config.clone(),
467486
)]
468487
.into(),
488+
OpenSearchTls::default(),
469489
);
470490

471491
NodeConfig::new(
@@ -488,7 +508,15 @@ mod tests {
488508
"discovery.type: \"zen\"\n",
489509
"network.host: \"0.0.0.0\"\n",
490510
"plugins.security.nodes_dn: [\"CN=generated certificate for pod\"]\n",
491-
"test: \"value\""
511+
"plugins.security.ssl.http.enabled: \"true\"\n",
512+
"plugins.security.ssl.http.pemcert_filepath: \"/stackable/opensearch/config/tls/http/tls.crt\"\n",
513+
"plugins.security.ssl.http.pemkey_filepath: \"/stackable/opensearch/config/tls/http/tls.key\"\n",
514+
"plugins.security.ssl.http.pemtrustedcas_filepath: \"/stackable/opensearch/config/tls/http/ca.crt\"\n",
515+
"plugins.security.ssl.transport.enabled: \"true\"\n",
516+
"plugins.security.ssl.transport.pemcert_filepath: \"/stackable/opensearch/config/tls/transport/tls.crt\"\n",
517+
"plugins.security.ssl.transport.pemkey_filepath: \"/stackable/opensearch/config/tls/transport/tls.key\"\n",
518+
"plugins.security.ssl.transport.pemtrustedcas_filepath: \"/stackable/opensearch/config/tls/transport/ca.crt\"\n",
519+
"test: \"value\"",
492520
)
493521
.to_owned(),
494522
node_config.opensearch_config_file_content()
@@ -509,7 +537,7 @@ mod tests {
509537
..TestConfig::default()
510538
});
511539

512-
assert!(!node_config_tls_undefined.tls_on_http_port_enabled());
540+
assert!(node_config_tls_undefined.tls_on_http_port_enabled());
513541
assert!(node_config_tls_enabled.tls_on_http_port_enabled());
514542
assert!(!node_config_tls_disabled.tls_on_http_port_enabled());
515543
}

rust/operator-binary/src/controller/build/role_builder.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ mod tests {
242242
},
243243
crd::{
244244
NodeRoles,
245-
v1alpha1::{self, OpenSearchClusterConfig},
245+
v1alpha1::{self, OpenSearchTls},
246246
},
247247
framework::{
248248
ClusterName, ControllerName, ListenerClassName, NamespaceName, OperatorName,
@@ -280,7 +280,7 @@ mod tests {
280280
v1alpha1::NodeRole::Ingest,
281281
v1alpha1::NodeRole::RemoteClusterClient,
282282
]),
283-
requested_secret_lifetime: Duration::from_str("15d")
283+
requested_secret_lifetime: Duration::from_str("1d")
284284
.expect("should be a valid duration"),
285285
resources: Resources::default(),
286286
termination_grace_period_seconds: 30,
@@ -305,13 +305,13 @@ mod tests {
305305
ClusterName::from_str_unsafe("my-opensearch-cluster"),
306306
NamespaceName::from_str_unsafe("default"),
307307
uuid!("0b1e30e6-326e-4c1a-868d-ad6598b49e8b"),
308-
OpenSearchClusterConfig::default(),
309308
GenericRoleConfig::default(),
310309
[(
311310
RoleGroupName::from_str_unsafe("default"),
312311
role_group_config.clone(),
313312
)]
314313
.into(),
314+
OpenSearchTls::default(),
315315
);
316316

317317
RoleBuilder::new(cluster, context_names)

0 commit comments

Comments
 (0)