Skip to content

Commit cfc730a

Browse files
committed
address feedback on PR
1 parent 7c6e1d9 commit cfc730a

12 files changed

Lines changed: 80 additions & 120 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
99
- Add the role group as a node attribute ([#63]).
1010
- Allow the configuration of TLS for the HTTP and TRANSPORT ports with the operator ([#55]).
1111

12+
[#55]: https://github.com/stackabletech/opensearch-operator/pull/55
1213
[#63]: https://github.com/stackabletech/opensearch-operator/pull/63
1314

1415
## [25.11.0] - 2025-11-07
Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
= Security
2-
:description: Configure TLS encryption, authentication, and Open Policy Agent (OPA) authorization for Kafka with the Stackable Operator.
2+
:description: Configure TLS encryption for OpenSearch with the Stackable Operator.
33

44
== TLS
55

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.
6+
The internal and client communication at the REST API can be encrypted with TLS.
7+
This requires the xref:secret-operator:index.adoc[Secret Operator] to be running in the Kubernetes cluster providing certificates.
8+
The used certificates can be changed in a cluster-wide config and are configured using xref:secret-operator:secretclass.adoc[SecretClasses].
9+
TLS encryption on the REST API may be disabled, while it is always enabled for the internal communication between nodes using the `transport` port.
810

911
[source,yaml]
1012
----
@@ -27,29 +29,12 @@ spec:
2729
default:
2830
replicas: 3
2931
----
30-
<1> The `spec.clusterConfig.tls.serverSecretClass` refers to the client-to-server encryption at the REST API. Defaults to the `tls` SecretClass and can be disabled by setting `serverSecretClass` to `null`.
31-
<2> The `spec.clusterConfig.tls.internalSecretClass` refers to the internal encryption between OpenSearch nodes using mTLS (transport). Defaults to the `tls` SecretClass` and can't be disabled.
32+
<1> The `spec.clusterConfig.tls.serverSecretClass` refers to the client-to-server encryption at the REST API.
33+
Defaults to the `tls` SecretClass and can be disabled by setting `serverSecretClass` to `null`.
34+
<2> The `spec.clusterConfig.tls.internalSecretClass` refers to the internal encryption between OpenSearch nodes using mTLS (transport).
35+
Defaults to the `tls` SecretClass and can't be disabled.
3236
<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.
37+
Only a lifetime up to the `maxCertificateLifetime` setting in the SecretClass is applied.
3438

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.serverSecretClass` or `spec.clusterConfig.tls.internalSecretClass` to use different certificates.
39+
Important: The operator sets the configuration `plugins.security.nodes_dn` to `["CN=generated certificate for pod"]` which provides weak authentication between nodes.
40+
If you want to increase security and use certificates which identify the OpenSearch nodes specifically, you must also adapt the `plugins.security.nodes_dn` setting via configOverrides.

docs/modules/opensearch/partials/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
** xref:opensearch:usage-guide/logging.adoc[]
1111
** xref:opensearch:usage-guide/opensearch-dashboards.adoc[]
1212
** xref:opensearch:usage-guide/scaling.adoc[]
13+
** xref:opensearch:usage-guide/security.adoc[]
1314
** xref:opensearch:usage-guide/operations/index.adoc[]
1415
*** xref:opensearch:usage-guide/operations/cluster-operations.adoc[]
1516
*** xref:opensearch:usage-guide/operations/pod-placement.adoc[]

rust/operator-binary/src/controller.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,7 @@ use update_status::update_status;
2828
use validate::validate;
2929

3030
use crate::{
31-
crd::{
32-
NodeRoles,
33-
v1alpha1::{self, OpenSearchTls},
34-
},
31+
crd::{NodeRoles, v1alpha1},
3532
framework::{
3633
ClusterName, ControllerName, HasName, HasUid, ListenerClassName, NameIsValidLabelValue,
3734
NamespaceName, OperatorName, ProductName, ProductVersion, RoleGroupName, RoleName, Uid,
@@ -167,7 +164,7 @@ pub struct ValidatedCluster {
167164
pub uid: Uid,
168165
pub role_config: GenericRoleConfig,
169166
pub role_group_configs: BTreeMap<RoleGroupName, OpenSearchRoleGroupConfig>,
170-
pub tls_config: OpenSearchTls,
167+
pub tls_config: v1alpha1::OpenSearchTls,
171168
}
172169

173170
impl ValidatedCluster {
@@ -180,7 +177,7 @@ impl ValidatedCluster {
180177
uid: impl Into<Uid>,
181178
role_config: GenericRoleConfig,
182179
role_group_configs: BTreeMap<RoleGroupName, OpenSearchRoleGroupConfig>,
183-
tls_config: OpenSearchTls,
180+
tls_config: v1alpha1::OpenSearchTls,
184181
) -> Self {
185182
let uid = uid.into();
186183
ValidatedCluster {
@@ -384,10 +381,7 @@ mod tests {
384381
use super::{Context, OpenSearchRoleGroupConfig, ValidatedCluster, ValidatedLogging};
385382
use crate::{
386383
controller::{OpenSearchNodeResources, ValidatedOpenSearchConfig},
387-
crd::{
388-
NodeRoles,
389-
v1alpha1::{self, OpenSearchTls},
390-
},
384+
crd::{NodeRoles, v1alpha1},
391385
framework::{
392386
ClusterName, ListenerClassName, NamespaceName, OperatorName, ProductVersion,
393387
RoleGroupName, builder::pod::container::EnvVarSet,
@@ -503,7 +497,7 @@ mod tests {
503497
),
504498
]
505499
.into(),
506-
OpenSearchTls::default(),
500+
v1alpha1::OpenSearchTls::default(),
507501
)
508502
}
509503

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ impl NodeConfig {
191191
// TLS config for TRANSPORT port which is always enabled.
192192
config.insert(
193193
CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_ENABLED.to_owned(),
194-
json!("true".to_string()),
194+
json!(true),
195195
);
196196
config.insert(
197197
CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_PEMCERT_FILEPATH.to_owned(),
@@ -210,7 +210,7 @@ impl NodeConfig {
210210
if self.cluster.tls_config.server_secret_class.is_some() {
211211
config.insert(
212212
CONFIG_OPTION_PLUGINS_SECURITY_SSL_HTTP_ENABLED.to_owned(),
213-
json!("true".to_string()),
213+
json!(true),
214214
);
215215
config.insert(
216216
CONFIG_OPTION_PLUGINS_SECURITY_SSL_HTTP_PEMCERT_FILEPATH.to_owned(),
@@ -227,7 +227,7 @@ impl NodeConfig {
227227
} else {
228228
config.insert(
229229
CONFIG_OPTION_PLUGINS_SECURITY_SSL_HTTP_ENABLED.to_owned(),
230-
json!("false".to_string()),
230+
json!(false),
231231
);
232232
}
233233

@@ -407,7 +407,7 @@ mod tests {
407407
use super::*;
408408
use crate::{
409409
controller::{ValidatedLogging, ValidatedOpenSearchConfig},
410-
crd::{NodeRoles, v1alpha1::OpenSearchTls},
410+
crd::{NodeRoles, v1alpha1},
411411
framework::{
412412
ClusterName, ListenerClassName, NamespaceName, ProductVersion, RoleGroupName,
413413
product_logging::framework::ValidatedContainerLogConfigChoice,
@@ -498,7 +498,7 @@ mod tests {
498498
role_group_config.clone(),
499499
)]
500500
.into(),
501-
OpenSearchTls::default(),
501+
v1alpha1::OpenSearchTls::default(),
502502
);
503503

504504
NodeConfig::new(
@@ -523,11 +523,11 @@ mod tests {
523523
"network.host: \"0.0.0.0\"\n",
524524
"node.attr.role-group: \"data\"\n",
525525
"plugins.security.nodes_dn: [\"CN=generated certificate for pod\"]\n",
526-
"plugins.security.ssl.http.enabled: \"true\"\n",
526+
"plugins.security.ssl.http.enabled: true\n",
527527
"plugins.security.ssl.http.pemcert_filepath: \"/stackable/opensearch/config/tls/server/tls.crt\"\n",
528528
"plugins.security.ssl.http.pemkey_filepath: \"/stackable/opensearch/config/tls/server/tls.key\"\n",
529529
"plugins.security.ssl.http.pemtrustedcas_filepath: \"/stackable/opensearch/config/tls/server/ca.crt\"\n",
530-
"plugins.security.ssl.transport.enabled: \"true\"\n",
530+
"plugins.security.ssl.transport.enabled: true\n",
531531
"plugins.security.ssl.transport.pemcert_filepath: \"/stackable/opensearch/config/tls/internal/tls.crt\"\n",
532532
"plugins.security.ssl.transport.pemkey_filepath: \"/stackable/opensearch/config/tls/internal/tls.key\"\n",
533533
"plugins.security.ssl.transport.pemtrustedcas_filepath: \"/stackable/opensearch/config/tls/internal/ca.crt\"\n",

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,7 @@ mod tests {
240240
ContextNames, OpenSearchRoleGroupConfig, ValidatedCluster,
241241
ValidatedContainerLogConfigChoice, ValidatedLogging, ValidatedOpenSearchConfig,
242242
},
243-
crd::{
244-
NodeRoles,
245-
v1alpha1::{self, OpenSearchTls},
246-
},
243+
crd::{NodeRoles, v1alpha1},
247244
framework::{
248245
ClusterName, ControllerName, ListenerClassName, NamespaceName, OperatorName,
249246
ProductName, ProductVersion, RoleGroupName, builder::pod::container::EnvVarSet,
@@ -311,7 +308,7 @@ mod tests {
311308
role_group_config.clone(),
312309
)]
313310
.into(),
314-
OpenSearchTls::default(),
311+
v1alpha1::OpenSearchTls::default(),
315312
);
316313

317314
RoleBuilder::new(cluster, context_names)

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

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -295,23 +295,23 @@ impl<'a> RoleGroupBuilder<'a> {
295295
..Volume::default()
296296
},
297297
self.build_tls_volume(
298-
&TLS_INTERNAL_VOLUME_NAME.to_string(),
298+
&TLS_INTERNAL_VOLUME_NAME,
299299
&self.cluster.tls_config.internal_secret_class,
300300
vec![],
301301
SecretFormat::TlsPem,
302302
&self.role_group_config.config.requested_secret_lifetime,
303-
Some(&LISTENER_VOLUME_NAME.to_string()),
303+
&LISTENER_VOLUME_NAME,
304304
),
305305
];
306306

307307
if let Some(tls_http_secret_class_name) = &self.cluster.tls_config.server_secret_class {
308308
volumes.push(self.build_tls_volume(
309-
&TLS_SERVER_VOLUME_NAME.to_string(),
309+
&TLS_SERVER_VOLUME_NAME,
310310
tls_http_secret_class_name,
311311
service_scopes,
312312
SecretFormat::TlsPem,
313313
&self.role_group_config.config.requested_secret_lifetime,
314-
Some(&LISTENER_VOLUME_NAME.to_string()),
314+
&LISTENER_VOLUME_NAME,
315315
))
316316
};
317317

@@ -660,26 +660,24 @@ impl<'a> RoleGroupBuilder<'a> {
660660

661661
fn build_tls_volume(
662662
&self,
663-
volume_name: &String,
663+
volume_name: &VolumeName,
664664
tls_secret_class_name: &SecretClassName,
665665
service_scopes: Vec<ServiceName>,
666666
secret_format: SecretFormat,
667667
requested_secret_lifetime: &Duration,
668-
listener_scope: Option<&str>,
668+
listener_scope: &PersistentVolumeClaimName,
669669
) -> Volume {
670670
let mut secret_volume_source_builder =
671671
SecretOperatorVolumeSourceBuilder::new(tls_secret_class_name);
672672

673673
for scope in service_scopes {
674674
secret_volume_source_builder.with_service_scope(scope);
675675
}
676-
if let Some(listener_scope) = listener_scope {
677-
secret_volume_source_builder.with_listener_volume_scope(listener_scope);
678-
}
679676

680-
VolumeBuilder::new(volume_name)
677+
VolumeBuilder::new(volume_name.to_string())
681678
.ephemeral(
682679
secret_volume_source_builder
680+
.with_listener_volume_scope(listener_scope)
683681
.with_pod_scope()
684682
.with_format(secret_format)
685683
.with_auto_tls_cert_lifetime(*requested_secret_lifetime)
@@ -1214,38 +1212,38 @@ mod tests {
12141212
"name": "my-opensearch-cluster-nodes-default"
12151213
},
12161214
"name": "log-config"
1217-
},
1218-
{
1215+
},
1216+
{
12191217
"emptyDir": {
12201218
"sizeLimit": "30Mi"
12211219
},
12221220
"name": "log"
1223-
},
1224-
{
1225-
"ephemeral": {
1226-
"volumeClaimTemplate": {
1227-
"metadata": {
1228-
"annotations": {
1229-
"secrets.stackable.tech/backend.autotls.cert.lifetime": "1d",
1230-
"secrets.stackable.tech/class": "tls",
1231-
"secrets.stackable.tech/format": "tls-pem",
1232-
"secrets.stackable.tech/scope": "listener-volume=listener,pod"
1233-
}
1234-
},
1235-
"spec": {
1236-
"accessModes": [
1237-
"ReadWriteOnce"
1238-
],
1239-
"resources": {
1240-
"requests": {
1241-
"storage": "1"
1221+
},
1222+
{
1223+
"ephemeral": {
1224+
"volumeClaimTemplate": {
1225+
"metadata": {
1226+
"annotations": {
1227+
"secrets.stackable.tech/backend.autotls.cert.lifetime": "1d",
1228+
"secrets.stackable.tech/class": "tls",
1229+
"secrets.stackable.tech/format": "tls-pem",
1230+
"secrets.stackable.tech/scope": "listener-volume=listener,pod"
12421231
}
12431232
},
1244-
"storageClassName": "secrets.stackable.tech"
1233+
"spec": {
1234+
"accessModes": [
1235+
"ReadWriteOnce"
1236+
],
1237+
"resources": {
1238+
"requests": {
1239+
"storage": "1"
1240+
}
1241+
},
1242+
"storageClassName": "secrets.stackable.tech"
12451243
}
12461244
}
12471245
},
1248-
"name": "tls-internal"
1246+
"name": "tls-internal"
12491247
},
12501248
{
12511249
"ephemeral": {
@@ -1268,11 +1266,11 @@ mod tests {
12681266
}
12691267
},
12701268
"storageClassName": "secrets.stackable.tech"
1271-
}
12721269
}
1273-
},
1270+
}
1271+
},
12741272
"name": "tls-server"
1275-
},
1273+
}
12761274
]
12771275
}
12781276
},

rust/operator-binary/src/crd/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ pub mod versioned {
8484
/// TLS configuration options for the server (REST API) and internal communication (transport).
8585
#[serde(default)]
8686
pub tls: OpenSearchTls,
87+
8788
/// Name of the Vector aggregator [discovery ConfigMap](DOCS_BASE_URL_PLACEHOLDER/concepts/service_discovery).
8889
/// It must contain the key `ADDRESS` with the address of the Vector aggregator.
8990
/// Follow the [logging tutorial](DOCS_BASE_URL_PLACEHOLDER/tutorials/logging-vector-aggregator)
@@ -104,6 +105,7 @@ pub mod versioned {
104105
skip_serializing_if = "Option::is_none"
105106
)]
106107
pub server_secret_class: Option<SecretClassName>,
108+
107109
/// Only affects internal communication (transport). Used for mutual verification between OpenSearch nodes.
108110
/// This setting controls:
109111
/// - Which cert the servers should use to authenticate themselves against other servers

tests/templates/kuttl/metrics/20-install-opensearch.yaml.j2

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,8 @@ spec:
1010
{% endif %}
1111
productVersion: "{{ test_scenario['values']['opensearch'].split(',')[0] }}"
1212
pullPolicy: IfNotPresent
13-
clusterConfig:
14-
{% if test_scenario['values']['server-use-tls'] == 'false' %}
15-
tls:
16-
serverSecretClass: null
17-
{% endif %}
1813
{% if lookup('env', 'VECTOR_AGGREGATOR') %}
14+
clusterConfig:
1915
vectorAggregatorConfigMapName: vector-aggregator-discovery
2016
{% endif %}
2117
nodes:

0 commit comments

Comments
 (0)