From 1fb1315aedc8816aa038520b2275b1434a14418f Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Wed, 4 Jun 2025 21:07:47 +0100 Subject: [PATCH] compute-ctl: add spec for enable_tls, separate from compute-ctl config (#12109) ## Problem Inbetween adding the TLS config for compute-ctl, and adding the TLS config in controlplane, we switched from using a provision flag to a bind flag. This happened to work in all of my testing in preview regions as they have no VM pool, so each bind was also a provision. However, in staging I found that the TLS config is still only processed during provision, even though it's only sent on bind. ## Summary of changes * Add a new feature flag value, `tls_experimental`, which tells postgres/pgbouncer/local_proxy to use the TLS certificates on bind. * compute_ctl on provision will be told where the certificates are, instead of being told on bind. --- compute_tools/src/compute.rs | 41 +++++++++++++++++----- compute_tools/src/http/routes/configure.rs | 2 +- libs/compute_api/src/spec.rs | 3 ++ 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/compute_tools/src/compute.rs b/compute_tools/src/compute.rs index 1ed71180d0..bd6ed910be 100644 --- a/compute_tools/src/compute.rs +++ b/compute_tools/src/compute.rs @@ -3,7 +3,7 @@ use chrono::{DateTime, Utc}; use compute_api::privilege::Privilege; use compute_api::responses::{ ComputeConfig, ComputeCtlConfig, ComputeMetrics, ComputeStatus, LfcOffloadState, - LfcPrewarmState, + LfcPrewarmState, TlsConfig, }; use compute_api::spec::{ ComputeAudit, ComputeFeature, ComputeMode, ComputeSpec, ExtVersion, PgIdent, @@ -603,6 +603,8 @@ impl ComputeNode { }); } + let tls_config = self.tls_config(&pspec.spec); + // If there are any remote extensions in shared_preload_libraries, start downloading them if pspec.spec.remote_extensions.is_some() { let (this, spec) = (self.clone(), pspec.spec.clone()); @@ -659,7 +661,7 @@ impl ComputeNode { info!("tuning pgbouncer"); let pgbouncer_settings = pgbouncer_settings.clone(); - let tls_config = self.compute_ctl_config.tls.clone(); + let tls_config = tls_config.clone(); // Spawn a background task to do the tuning, // so that we don't block the main thread that starts Postgres. @@ -678,7 +680,10 @@ impl ComputeNode { // Spawn a background task to do the configuration, // so that we don't block the main thread that starts Postgres. - let local_proxy = local_proxy.clone(); + + let mut local_proxy = local_proxy.clone(); + local_proxy.tls = tls_config.clone(); + let _handle = tokio::spawn(async move { if let Err(err) = local_proxy::configure(&local_proxy) { error!("error while configuring local_proxy: {err:?}"); @@ -1205,13 +1210,15 @@ impl ComputeNode { let spec = &pspec.spec; let pgdata_path = Path::new(&self.params.pgdata); + let tls_config = self.tls_config(&pspec.spec); + // Remove/create an empty pgdata directory and put configuration there. self.create_pgdata()?; config::write_postgres_conf( pgdata_path, &pspec.spec, self.params.internal_http_port, - &self.compute_ctl_config.tls, + tls_config, )?; // Syncing safekeepers is only safe with primary nodes: if a primary @@ -1536,14 +1543,22 @@ impl ComputeNode { .clone(), ); + let mut tls_config = None::; + if spec.features.contains(&ComputeFeature::TlsExperimental) { + tls_config = self.compute_ctl_config.tls.clone(); + } + let max_concurrent_connections = self.max_service_connections(compute_state, &spec); // Merge-apply spec & changes to PostgreSQL state. self.apply_spec_sql(spec.clone(), conf.clone(), max_concurrent_connections)?; if let Some(local_proxy) = &spec.clone().local_proxy_config { + let mut local_proxy = local_proxy.clone(); + local_proxy.tls = tls_config.clone(); + info!("configuring local_proxy"); - local_proxy::configure(local_proxy).context("apply_config local_proxy")?; + local_proxy::configure(&local_proxy).context("apply_config local_proxy")?; } // Run migrations separately to not hold up cold starts @@ -1595,11 +1610,13 @@ impl ComputeNode { pub fn reconfigure(&self) -> Result<()> { let spec = self.state.lock().unwrap().pspec.clone().unwrap().spec; + let tls_config = self.tls_config(&spec); + if let Some(ref pgbouncer_settings) = spec.pgbouncer_settings { info!("tuning pgbouncer"); let pgbouncer_settings = pgbouncer_settings.clone(); - let tls_config = self.compute_ctl_config.tls.clone(); + let tls_config = tls_config.clone(); // Spawn a background task to do the tuning, // so that we don't block the main thread that starts Postgres. @@ -1617,7 +1634,7 @@ impl ComputeNode { // Spawn a background task to do the configuration, // so that we don't block the main thread that starts Postgres. let mut local_proxy = local_proxy.clone(); - local_proxy.tls = self.compute_ctl_config.tls.clone(); + local_proxy.tls = tls_config.clone(); tokio::spawn(async move { if let Err(err) = local_proxy::configure(&local_proxy) { error!("error while configuring local_proxy: {err:?}"); @@ -1635,7 +1652,7 @@ impl ComputeNode { pgdata_path, &spec, self.params.internal_http_port, - &self.compute_ctl_config.tls, + tls_config, )?; if !spec.skip_pg_catalog_updates { @@ -1755,6 +1772,14 @@ impl ComputeNode { } } + pub fn tls_config(&self, spec: &ComputeSpec) -> &Option { + if spec.features.contains(&ComputeFeature::TlsExperimental) { + &self.compute_ctl_config.tls + } else { + &None:: + } + } + /// Update the `last_active` in the shared state, but ensure that it's a more recent one. pub fn update_last_active(&self, last_active: Option>) { let mut state = self.state.lock().unwrap(); diff --git a/compute_tools/src/http/routes/configure.rs b/compute_tools/src/http/routes/configure.rs index f7a19da611..c29e3a97da 100644 --- a/compute_tools/src/http/routes/configure.rs +++ b/compute_tools/src/http/routes/configure.rs @@ -22,7 +22,7 @@ pub(in crate::http) async fn configure( State(compute): State>, request: Json, ) -> Response { - let pspec = match ParsedSpec::try_from(request.spec.clone()) { + let pspec = match ParsedSpec::try_from(request.0.spec) { Ok(p) => p, Err(e) => return JsonResponse::error(StatusCode::BAD_REQUEST, e), }; diff --git a/libs/compute_api/src/spec.rs b/libs/compute_api/src/spec.rs index 343923d446..0e23b70265 100644 --- a/libs/compute_api/src/spec.rs +++ b/libs/compute_api/src/spec.rs @@ -192,6 +192,9 @@ pub enum ComputeFeature { /// track short-lived connections as user activity. ActivityMonitorExperimental, + /// Enable TLS functionality. + TlsExperimental, + /// This is a special feature flag that is used to represent unknown feature flags. /// Basically all unknown to enum flags are represented as this one. See unit test /// `parse_unknown_features()` for more details.