Skip to content

Commit 7694293

Browse files
feat: TLS support (#55)
* wip: add run-securityadmin job * configure tls on run-securityadmin * add tls secret class to crd * add tls volume to sts * add tls config to opensearch.yml * disable security demo install * wip amend integration tests * fix smoke test * restore properties file * mount tls volume in default directory of official image * use tls feature in all integration tests * use OPENSEARCH_PATH_CONF for tls config * fix incorrectly resolved merge conflict * wip: adress feedback on pr * address feedback on PR * add changelog entry * rename CRD fields based on decision * address feedback on PR * address more feedback on PR * remove duplicate SecretClassName length check --------- Co-authored-by: Siegfried Weber <mail@siegfriedweber.net>
1 parent 3f57fd1 commit 7694293

34 files changed

+619
-407
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ All notable changes to this project will be documented in this file.
77
### Added
88

99
- Add the role group as a node attribute ([#63]).
10+
- Allow the configuration of TLS for the HTTP and TRANSPORT ports with the operator ([#55]).
1011

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

1315
## [25.11.0] - 2025-11-07

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

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,42 @@ spec:
2929
generates in the [operator documentation](https://docs.stackable.tech/home/nightly/opensearch/).
3030
properties:
3131
clusterConfig:
32-
default: {}
32+
default:
33+
tls:
34+
internalSecretClass: tls
35+
serverSecretClass: tls
3336
description: Configuration that applies to all roles and role groups
3437
properties:
38+
tls:
39+
default:
40+
internalSecretClass: tls
41+
serverSecretClass: tls
42+
description: TLS configuration options for the server (REST API) and internal communication (transport).
43+
properties:
44+
internalSecretClass:
45+
default: tls
46+
description: |-
47+
Only affects internal communication (transport). Used for mutual verification between OpenSearch nodes.
48+
This setting controls:
49+
- Which cert the servers should use to authenticate themselves against other servers
50+
- Which ca.crt to use when validating the other server
51+
maxLength: 253
52+
minLength: 1
53+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
54+
type: string
55+
serverSecretClass:
56+
default: tls
57+
description: |-
58+
Only affects client connections to the REST API.
59+
This setting controls:
60+
- If TLS encryption is used at all
61+
- Which cert the servers should use to authenticate themselves against the client
62+
maxLength: 253
63+
minLength: 1
64+
nullable: true
65+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
66+
type: string
67+
type: object
3568
vectorAggregatorConfigMapName:
3669
description: |-
3770
Name of the Vector aggregator [discovery ConfigMap](https://docs.stackable.tech/home/nightly/concepts/service_discovery).
@@ -305,6 +338,14 @@ spec:
305338
type: string
306339
nullable: true
307340
type: array
341+
requestedSecretLifetime:
342+
description: |-
343+
Request secret (currently only autoTls certificates) lifetime from the secret operator, e.g. `7d`, or `30d`.
344+
This can be shortened by the `maxCertificateLifetime` setting on the SecretClass issuing the TLS certificate.
345+
346+
Defaults to 1d.
347+
nullable: true
348+
type: string
308349
resources:
309350
default:
310351
cpu:
@@ -654,6 +695,14 @@ spec:
654695
type: string
655696
nullable: true
656697
type: array
698+
requestedSecretLifetime:
699+
description: |-
700+
Request secret (currently only autoTls certificates) lifetime from the secret operator, e.g. `7d`, or `30d`.
701+
This can be shortened by the `maxCertificateLifetime` setting on the SecretClass issuing the TLS certificate.
702+
703+
Defaults to 1d.
704+
nullable: true
705+
type: string
657706
resources:
658707
default:
659708
cpu:

docs/modules/opensearch/examples/getting_started/opensearch.yaml

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,6 @@ spec:
1414
opensearch.yml:
1515
plugins.security.allow_default_init_securityindex: "true"
1616
plugins.security.restapi.roles_enabled: all_access
17-
plugins.security.ssl.transport.enabled: "true"
18-
plugins.security.ssl.transport.pemcert_filepath: /stackable/opensearch/config/tls/tls.crt
19-
plugins.security.ssl.transport.pemkey_filepath: /stackable/opensearch/config/tls/tls.key
20-
plugins.security.ssl.transport.pemtrustedcas_filepath: /stackable/opensearch/config/tls/ca.crt
21-
plugins.security.ssl.http.enabled: "true"
22-
plugins.security.ssl.http.pemcert_filepath: /stackable/opensearch/config/tls/tls.crt
23-
plugins.security.ssl.http.pemkey_filepath: /stackable/opensearch/config/tls/tls.key
24-
plugins.security.ssl.http.pemtrustedcas_filepath: /stackable/opensearch/config/tls/ca.crt
2517
podOverrides:
2618
spec:
2719
containers:
@@ -30,25 +22,8 @@ spec:
3022
- name: security-config
3123
mountPath: /stackable/opensearch/config/opensearch-security
3224
readOnly: true
33-
- name: tls
34-
mountPath: /stackable/opensearch/config/tls
35-
readOnly: true
3625
volumes:
3726
- name: security-config
3827
secret:
3928
secretName: opensearch-security-config
4029
defaultMode: 0o660
41-
- name: tls
42-
ephemeral:
43-
volumeClaimTemplate:
44-
metadata:
45-
annotations:
46-
secrets.stackable.tech/class: tls
47-
secrets.stackable.tech/scope: node,pod,service=simple-opensearch,service=simple-opensearch-nodes-default,service=simple-opensearch-nodes-default-headless
48-
spec:
49-
storageClassName: secrets.stackable.tech
50-
accessModes:
51-
- ReadWriteOnce
52-
resources:
53-
requests:
54-
storage: "1"
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
= Security
2+
:description: Configure TLS encryption for OpenSearch with the Stackable Operator.
3+
4+
== TLS
5+
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.
10+
11+
[source,yaml]
12+
----
13+
---
14+
apiVersion: opensearch.stackable.tech/v1alpha1
15+
kind: OpenSearchCluster
16+
metadata:
17+
name: opensearch
18+
spec:
19+
image:
20+
productVersion: 3.1.0
21+
clusterConfig:
22+
tls:
23+
serverSecretClass: tls # <1>
24+
internalSecretClass: opensearch-internal-tls # <2>
25+
nodes:
26+
config:
27+
requestedSecretLifetime: 7d # <3>
28+
roleGroups:
29+
default:
30+
replicas: 3
31+
----
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.
36+
<3> The lifetime for autoTls certificates generated by the secret operator.
37+
Only a lifetime up to the `maxCertificateLifetime` setting in the SecretClass is applied.
38+
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: 10 additions & 4 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},
34-
},
31+
crd::{NodeRoles, v1alpha1},
3532
framework::{
3633
HasName, HasUid, NameIsValidLabelValue,
3734
product_logging::framework::{ValidatedContainerLogConfigChoice, VectorContainerLogConfig},
@@ -137,6 +134,7 @@ pub struct ValidatedOpenSearchConfig {
137134
pub listener_class: ListenerClassName,
138135
pub logging: ValidatedLogging,
139136
pub node_roles: NodeRoles,
137+
pub requested_secret_lifetime: Duration,
140138
pub resources: OpenSearchNodeResources,
141139
pub termination_grace_period_seconds: i64,
142140
}
@@ -172,9 +170,11 @@ pub struct ValidatedCluster {
172170
pub uid: Uid,
173171
pub role_config: GenericRoleConfig,
174172
pub role_group_configs: BTreeMap<RoleGroupName, OpenSearchRoleGroupConfig>,
173+
pub tls_config: v1alpha1::OpenSearchTls,
175174
}
176175

177176
impl ValidatedCluster {
177+
#[allow(clippy::too_many_arguments)]
178178
pub fn new(
179179
image: ResolvedProductImage,
180180
product_version: ProductVersion,
@@ -183,6 +183,7 @@ impl ValidatedCluster {
183183
uid: impl Into<Uid>,
184184
role_config: GenericRoleConfig,
185185
role_group_configs: BTreeMap<RoleGroupName, OpenSearchRoleGroupConfig>,
186+
tls_config: v1alpha1::OpenSearchTls,
186187
) -> Self {
187188
let uid = uid.into();
188189
ValidatedCluster {
@@ -199,6 +200,7 @@ impl ValidatedCluster {
199200
uid,
200201
role_config,
201202
role_group_configs,
203+
tls_config,
202204
}
203205
}
204206

@@ -378,6 +380,7 @@ mod tests {
378380
kvp::LabelValue,
379381
product_logging::spec::AutomaticContainerLogConfig,
380382
role_utils::GenericRoleConfig,
383+
shared::time::Duration,
381384
};
382385
use uuid::uuid;
383386

@@ -503,6 +506,7 @@ mod tests {
503506
),
504507
]
505508
.into(),
509+
v1alpha1::OpenSearchTls::default(),
506510
)
507511
}
508512

@@ -522,6 +526,8 @@ mod tests {
522526
vector_container: None,
523527
},
524528
node_roles: NodeRoles(node_roles.to_vec()),
529+
requested_secret_lifetime: Duration::from_str("1d")
530+
.expect("should be a valid duration"),
525531
resources: OpenSearchNodeResources::default(),
526532
termination_grace_period_seconds: 120,
527533
},

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ mod tests {
6868
kvp::LabelValue,
6969
product_logging::spec::AutomaticContainerLogConfig,
7070
role_utils::GenericRoleConfig,
71+
shared::time::Duration,
7172
};
7273
use uuid::uuid;
7374

@@ -197,6 +198,7 @@ mod tests {
197198
),
198199
]
199200
.into(),
201+
v1alpha1::OpenSearchTls::default(),
200202
)
201203
}
202204

@@ -216,6 +218,8 @@ mod tests {
216218
vector_container: None,
217219
},
218220
node_roles: NodeRoles(node_roles.to_vec()),
221+
requested_secret_lifetime: Duration::from_str("1d")
222+
.expect("should be a valid duration"),
219223
resources: OpenSearchNodeResources::default(),
220224
termination_grace_period_seconds: 120,
221225
},

0 commit comments

Comments
 (0)