Skip to content

Commit 0d217d0

Browse files
committed
refactor(webhook): Clean up constants and interval
1 parent cab32c0 commit 0d217d0

2 files changed

Lines changed: 29 additions & 25 deletions

File tree

crates/stackable-webhook/src/tls/cert_resolver.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,11 @@ pub struct CertificateResolver {
5757
/// Using a [`ArcSwap`] (over e.g. [`tokio::sync::RwLock`]), so that we can easily
5858
/// (and performant) bridge between async write and sync read.
5959
current_certified_key: ArcSwap<CertifiedKey>,
60+
6061
/// The wall-clock expiry time (`not_after`) of the current certificate.
6162
/// Used to detect clock drift between monotonic and wall-clock time.
6263
current_not_after: ArcSwap<SystemTime>,
64+
6365
subject_alterative_dns_names: Arc<Vec<String>>,
6466

6567
certificate_tx: mpsc::Sender<Certificate>,
@@ -109,11 +111,12 @@ impl CertificateResolver {
109111
.unwrap_or(SystemTime::UNIX_EPOCH);
110112

111113
tracing::debug!(
112-
subject_alterative_dns_names = ?self.subject_alterative_dns_names,
113-
not_after = %humantime::format_rfc3339(not_after),
114+
x509.subject_alterative_names = ?self.subject_alterative_dns_names,
115+
x509.not_after = %humantime::format_rfc3339(not_after),
114116
deadline = %humantime::format_rfc3339(deadline),
115117
"checking if certificate needs rotation"
116118
);
119+
117120
SystemTime::now() >= deadline
118121
}
119122

@@ -123,7 +126,7 @@ impl CertificateResolver {
123126
.await
124127
}
125128

126-
/// Creates a new certificate and returns the certified key.
129+
/// Creates a new certificate and returns the certified key as well as `notAfter` timestamp.
127130
///
128131
/// The certificate is send to the passed `cert_tx`.
129132
///

crates/stackable-webhook/src/tls/mod.rs

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,26 +38,30 @@ use crate::{
3838

3939
mod cert_resolver;
4040

41-
pub const WEBHOOK_CA_LIFETIME: Duration = Duration::from_hours_unchecked(24);
42-
43-
/// The wall-clock lifetime of generated webhook certificates. If this is ever
44-
/// reduced, ensure it stays well above [`CERTIFICATE_ROTATION_CHECK_INTERVAL`]
41+
/// All certificates (CAs and leaf certificates) are valid for this amount of time in hours. If this
42+
/// is ever reduced, ensure it stays well above [`CERTIFICATE_ROTATION_CHECK_INTERVAL`]
4543
/// (currently 5 minutes), otherwise the certificate could expire between checks.
46-
const WEBHOOK_CERTIFICATE_LIFETIME_HOURS: u64 = 24;
44+
const CERTIFICATE_LIFETIME_HOURS: u64 = 24;
45+
46+
/// The CA lifetime as a human-readable [`Duration`].
47+
pub const WEBHOOK_CA_LIFETIME: Duration =
48+
Duration::from_hours_unchecked(CERTIFICATE_LIFETIME_HOURS);
49+
50+
/// The leaf certificate lifetime as a human-readable [`Duration`].
4751
pub const WEBHOOK_CERTIFICATE_LIFETIME: Duration =
48-
Duration::from_hours_unchecked(WEBHOOK_CERTIFICATE_LIFETIME_HOURS);
52+
Duration::from_hours_unchecked(CERTIFICATE_LIFETIME_HOURS);
4953

50-
/// How often to check whether the certificate needs rotation. This is
51-
/// intentionally independent of the certificate lifetime — it controls how
52-
/// quickly we detect wall-clock drift (from hibernation, VM migration, etc.),
53-
/// not how long the certificate lives.
54-
const CERTIFICATE_ROTATION_CHECK_INTERVAL: Duration = Duration::from_minutes_unchecked(5);
54+
/// Rotate the certificate when less than 1/6 of its lifetime remains (4 hours for the current 24h
55+
/// lifetime). Derived from [`CERTIFICATE_LIFETIME_HOURS`] so it scales if the lifetime changes.
56+
const CERTIFICATE_EXPIRY_BUFFER_HOURS: u64 = CERTIFICATE_LIFETIME_HOURS * 60 / 6;
5557

56-
/// Rotate the certificate when less than 1/6 of its lifetime remains
57-
/// (4 hours for the current 24h lifetime). Derived from
58-
/// [`WEBHOOK_CERTIFICATE_LIFETIME`] so it scales if the lifetime changes.
5958
const CERTIFICATE_EXPIRY_BUFFER: Duration =
60-
Duration::from_minutes_unchecked(WEBHOOK_CERTIFICATE_LIFETIME_HOURS * 60 / 6);
59+
Duration::from_minutes_unchecked(CERTIFICATE_EXPIRY_BUFFER_HOURS);
60+
61+
/// How often to check whether the certificate needs rotation. This is intentionally independent of
62+
/// the certificate lifetime - it controls how quickly we detect wall-clock drift (from hibernation,
63+
/// VM migration, etc.), not how long the certificate lives.
64+
const CERTIFICATE_ROTATION_CHECK_INTERVAL: Duration = Duration::from_minutes_unchecked(5);
6165

6266
pub type Result<T, E = TlsServerError> = std::result::Result<T, E>;
6367

@@ -170,12 +174,9 @@ impl TlsServer {
170174
router,
171175
} = self;
172176

173-
// Periodically check whether the certificate needs rotation based on
174-
// wall-clock time. This avoids the monotonic vs wall-clock drift problem
175-
// that can occur during hibernation, VM migration, or cgroup freezing.
176-
let check_start = tokio::time::Instant::now() + *CERTIFICATE_ROTATION_CHECK_INTERVAL;
177-
let mut rotation_check =
178-
tokio::time::interval_at(check_start, *CERTIFICATE_ROTATION_CHECK_INTERVAL);
177+
let start = tokio::time::Instant::now() + *CERTIFICATE_ROTATION_CHECK_INTERVAL;
178+
let mut rotation_check_interval =
179+
tokio::time::interval_at(start, *CERTIFICATE_ROTATION_CHECK_INTERVAL);
179180

180181
let tls_acceptor = TlsAcceptor::from(Arc::new(config));
181182
let tcp_listener = TcpListener::bind(socket_addr)
@@ -220,7 +221,7 @@ impl TlsServer {
220221

221222
// Check wall-clock time to decide if the certificate needs rotation.
222223
// This is cancellation-safe: if cancelled, the tick is NOT consumed.
223-
_ = rotation_check.tick() => {
224+
_ = rotation_check_interval.tick() => {
224225
if cert_resolver.needs_rotation(*CERTIFICATE_EXPIRY_BUFFER) {
225226
tracing::info!("certificate approaching expiry, rotating");
226227
cert_resolver

0 commit comments

Comments
 (0)