Skip to content

Commit 9fb277a

Browse files
committed
feat: Support setting TLS certificate lifetimes
1 parent 593edf0 commit 9fb277a

File tree

7 files changed

+58
-25
lines changed

7 files changed

+58
-25
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,19 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
### Added
8+
9+
- The lifetime of auto generated TLS certificates is now configurable with the role and roleGroup
10+
config property `requestedSecretLifetime`. This helps reduce frequent Pod restarts ([#892]).
11+
712
### Fixed
813

914
- BREAKING: Use distinct ServiceAccounts for the Stacklets, so that multiple Stacklets can be
1015
deployed in one namespace. Existing Stacklets will use the newly created ServiceAccounts after
1116
restart ([#889]).
1217

1318
[#889]: https://github.com/stackabletech/zookeeper-operator/pull/889
19+
[#892]: https://github.com/stackabletech/zookeeper-operator/pull/892
1420

1521
## [24.11.0] - 2024-11-18
1622

Cargo.lock

Lines changed: 4 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ tokio = { version = "1.40", features = ["full"] }
2929
tokio-zookeeper = "0.4"
3030
tracing = "0.1"
3131

32-
# [patch."https://github.com/stackabletech/operator-rs.git"]
33-
# stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "main" }
32+
[patch."https://github.com/stackabletech/operator-rs.git"]
33+
stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "main" }
3434

3535
[patch.crates-io]
3636
# tokio-zookeeper = { path = "../tokio-zookeeper" }

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,10 @@ spec:
298298
minimum: 0.0
299299
nullable: true
300300
type: integer
301+
requestedSecretLifetime:
302+
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.
303+
nullable: true
304+
type: string
301305
resources:
302306
default:
303307
cpu:
@@ -581,6 +585,10 @@ spec:
581585
minimum: 0.0
582586
nullable: true
583587
type: integer
588+
requestedSecretLifetime:
589+
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.
590+
nullable: true
591+
type: string
584592
resources:
585593
default:
586594
cpu:

rust/crd/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,11 @@ pub struct ZookeeperConfig {
240240
/// Time period Pods have to gracefully shut down, e.g. `30m`, `1h` or `2d`. Consult the operator documentation for details.
241241
#[fragment_attrs(serde(default))]
242242
pub graceful_shutdown_timeout: Option<Duration>,
243+
244+
/// Request secret (currently only autoTls certificates) lifetime from the secret operator, e.g. `7d`, or `30d`.
245+
/// This can be shortened by the `maxCertificateLifetime` setting on the SecretClass issuing the TLS certificate.
246+
#[fragment_attrs(serde(default))]
247+
pub requested_secret_lifetime: Option<Duration>,
243248
}
244249

245250
#[derive(Clone, Debug, Default, JsonSchema, PartialEq, Fragment)]
@@ -291,6 +296,8 @@ impl ZookeeperConfig {
291296
pub const SERVER_JVMFLAGS: &'static str = "SERVER_JVMFLAGS";
292297
pub const ZK_SERVER_HEAP: &'static str = "ZK_SERVER_HEAP";
293298

299+
const DEFAULT_SECRET_LIFETIME: Duration = Duration::from_days_unchecked(7);
300+
294301
fn default_server_config(cluster_name: &str, role: &ZookeeperRole) -> ZookeeperConfigFragment {
295302
ZookeeperConfigFragment {
296303
init_limit: None,
@@ -317,6 +324,7 @@ impl ZookeeperConfig {
317324
logging: product_logging::spec::default_logging(),
318325
affinity: get_affinity(cluster_name, role),
319326
graceful_shutdown_timeout: Some(DEFAULT_SERVER_GRACEFUL_SHUTDOWN_TIMEOUT),
327+
requested_secret_lifetime: Some(Self::DEFAULT_SECRET_LIFETIME),
320328
}
321329
}
322330
}

rust/crd/src/security.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
//! This is required due to overlaps between TLS encryption and e.g. mTLS authentication or Kerberos
77
use std::collections::BTreeMap;
88

9+
use crate::{authentication, authentication::ResolvedAuthenticationClasses, tls, ZookeeperCluster};
910
use snafu::{ResultExt, Snafu};
11+
use stackable_operator::time::Duration;
1012
use stackable_operator::{
1113
builder::{
1214
self,
@@ -24,8 +26,6 @@ use stackable_operator::{
2426
k8s_openapi::api::core::v1::Volume,
2527
};
2628

27-
use crate::{authentication, authentication::ResolvedAuthenticationClasses, tls, ZookeeperCluster};
28-
2929
type Result<T, E = Error> = std::result::Result<T, E>;
3030

3131
#[derive(Snafu, Debug)]
@@ -147,6 +147,7 @@ impl ZookeeperSecurity {
147147
&self,
148148
pod_builder: &mut PodBuilder,
149149
cb_zookeeper: &mut ContainerBuilder,
150+
requested_secret_lifetime: &Duration,
150151
) -> Result<()> {
151152
let tls_secret_class = self.get_tls_secret_class();
152153

@@ -156,7 +157,11 @@ impl ZookeeperSecurity {
156157
.add_volume_mount(tls_volume_name, Self::SERVER_TLS_DIR)
157158
.context(AddVolumeMountSnafu)?;
158159
pod_builder
159-
.add_volume(Self::create_tls_volume(tls_volume_name, secret_class)?)
160+
.add_volume(Self::create_tls_volume(
161+
tls_volume_name,
162+
secret_class,
163+
requested_secret_lifetime,
164+
)?)
160165
.context(AddVolumeSnafu)?;
161166
}
162167

@@ -169,6 +174,7 @@ impl ZookeeperSecurity {
169174
.add_volume(Self::create_tls_volume(
170175
tls_volume_name,
171176
&self.quorum_secret_class,
177+
requested_secret_lifetime,
172178
)?)
173179
.context(AddVolumeSnafu)?;
174180

@@ -290,13 +296,18 @@ impl ZookeeperSecurity {
290296
}
291297

292298
/// Creates ephemeral volumes to mount the `SecretClass` into the Pods
293-
fn create_tls_volume(volume_name: &str, secret_class_name: &str) -> Result<Volume> {
299+
fn create_tls_volume(
300+
volume_name: &str,
301+
secret_class_name: &str,
302+
requested_secret_lifetime: &Duration,
303+
) -> Result<Volume> {
294304
let volume = VolumeBuilder::new(volume_name)
295305
.ephemeral(
296306
SecretOperatorVolumeSourceBuilder::new(secret_class_name)
297307
.with_pod_scope()
298308
.with_node_scope()
299309
.with_format(SecretFormat::TlsPkcs12)
310+
.with_auto_tls_cert_lifetime(*requested_secret_lifetime)
300311
.build()
301312
.context(BuildTlsVolumeSnafu { volume_name })?,
302313
)

rust/operator-binary/src/zk_controller.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ type Result<T, E = Error> = std::result::Result<T, E>;
9494
#[strum_discriminants(derive(IntoStaticStr))]
9595
#[allow(clippy::enum_variant_names)]
9696
pub enum Error {
97+
#[snafu(display("missing secret lifetime"))]
98+
MissingSecretLifetime,
99+
97100
#[snafu(display("ZookeeperCluster object is invalid"))]
98101
InvalidZookeeperCluster {
99102
source: error_boundary::InvalidObject,
@@ -279,6 +282,7 @@ impl ReconcilerError for Error {
279282
}
280283
fn secondary_object(&self) -> Option<ObjectRef<DynamicObject>> {
281284
match self {
285+
Error::MissingSecretLifetime => None,
282286
Error::InvalidZookeeperCluster { source: _ } => None,
283287
Error::CrdValidationFailure { .. } => None,
284288
Error::NoServerRole => None,
@@ -755,7 +759,7 @@ fn build_server_rolegroup_statefulset(
755759
server_config: &HashMap<PropertyNameKind, BTreeMap<String, String>>,
756760
zookeeper_security: &ZookeeperSecurity,
757761
resolved_product_image: &ResolvedProductImage,
758-
config: &ZookeeperConfig,
762+
merged_config: &ZookeeperConfig,
759763
service_account: &ServiceAccount,
760764
) -> Result<StatefulSet> {
761765
let role = zk.role(zk_role).context(InternalOperatorFailureSnafu)?;
@@ -799,9 +803,16 @@ fn build_server_rolegroup_statefulset(
799803
ContainerBuilder::new(APP_NAME).expect("invalid hard-coded container name");
800804
let mut pod_builder = PodBuilder::new();
801805

806+
let requested_secret_lifetime = merged_config
807+
.requested_secret_lifetime
808+
.context(MissingSecretLifetimeSnafu)?;
802809
// add volumes and mounts depending on tls / auth settings
803810
zookeeper_security
804-
.add_volume_mounts(&mut pod_builder, &mut cb_zookeeper)
811+
.add_volume_mounts(
812+
&mut pod_builder,
813+
&mut cb_zookeeper,
814+
&requested_secret_lifetime,
815+
)
805816
.context(AddTlsVolumeMountsSnafu)?;
806817

807818
let mut args = Vec::new();
@@ -932,7 +943,7 @@ fn build_server_rolegroup_statefulset(
932943
.image_pull_secrets_from_product_image(resolved_product_image)
933944
.add_init_container(container_prepare)
934945
.add_container(container_zk)
935-
.affinity(&config.affinity)
946+
.affinity(&merged_config.affinity)
936947
.add_volume(Volume {
937948
name: "config".to_string(),
938949
config_map: Some(ConfigMapVolumeSource {
@@ -1014,7 +1025,7 @@ fn build_server_rolegroup_statefulset(
10141025
);
10151026
}
10161027

1017-
add_graceful_shutdown_config(config, &mut pod_builder).context(GracefulShutdownSnafu)?;
1028+
add_graceful_shutdown_config(merged_config, &mut pod_builder).context(GracefulShutdownSnafu)?;
10181029

10191030
let mut pod_template = pod_builder.build_template();
10201031
pod_template.merge_from(role.config.pod_overrides.clone());

0 commit comments

Comments
 (0)