mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-25 14:53:34 +00:00
All PostgreSQL connection sites hardcoded NoTls, preventing connections to managed providers that require TLS (AWS RDS, Neon, Supabase, etc.). - Add tokio-postgres-rustls with rustls + system root certificates - Add SslMode enum (disable/prefer/require) via DATABASE_SSLMODE env var - Replace NoTls at all 4 production call sites with TLS-aware pool creation - Add SslMode::from_env() helper for lightweight CLI tools - Log native cert loading errors and warn on empty root store Default mode is Prefer (attempts TLS, matching most managed providers). Co-authored-by: Claude Opus 4.6 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
1f2e8c3b72
commit
3362081192
Generated
+84
@@ -1496,9 +1496,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
|
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const-oid",
|
"const-oid",
|
||||||
|
"der_derive",
|
||||||
|
"flagset",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "der_derive"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.5.8"
|
version = "0.5.8"
|
||||||
@@ -1887,6 +1900,12 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flagset"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.1.9"
|
version = "1.1.9"
|
||||||
@@ -2854,6 +2873,8 @@ dependencies = [
|
|||||||
"rig-core",
|
"rig-core",
|
||||||
"rust_decimal",
|
"rust_decimal",
|
||||||
"rust_decimal_macros",
|
"rust_decimal_macros",
|
||||||
|
"rustls",
|
||||||
|
"rustls-native-certs",
|
||||||
"rustyline",
|
"rustyline",
|
||||||
"secrecy",
|
"secrecy",
|
||||||
"secret-service",
|
"secret-service",
|
||||||
@@ -2870,6 +2891,7 @@ dependencies = [
|
|||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-postgres",
|
"tokio-postgres",
|
||||||
|
"tokio-postgres-rustls",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tokio-test",
|
"tokio-test",
|
||||||
"tokio-tungstenite 0.26.2",
|
"tokio-tungstenite 0.26.2",
|
||||||
@@ -5717,6 +5739,27 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tls_codec"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b"
|
||||||
|
dependencies = [
|
||||||
|
"tls_codec_derive",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tls_codec_derive"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.49.0"
|
version = "1.49.0"
|
||||||
@@ -5792,6 +5835,21 @@ dependencies = [
|
|||||||
"whoami",
|
"whoami",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-postgres-rustls"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "27d684bad428a0f2481f42241f821db42c54e2dc81d8c00db8536c506b0a0144"
|
||||||
|
dependencies = [
|
||||||
|
"const-oid",
|
||||||
|
"ring",
|
||||||
|
"rustls",
|
||||||
|
"tokio",
|
||||||
|
"tokio-postgres",
|
||||||
|
"tokio-rustls",
|
||||||
|
"x509-cert",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-rustls"
|
name = "tokio-rustls"
|
||||||
version = "0.26.4"
|
version = "0.26.4"
|
||||||
@@ -7528,6 +7586,18 @@ dependencies = [
|
|||||||
"tap",
|
"tap",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x509-cert"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94"
|
||||||
|
dependencies = [
|
||||||
|
"const-oid",
|
||||||
|
"der",
|
||||||
|
"spki",
|
||||||
|
"tls_codec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xattr"
|
name = "xattr"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
@@ -7707,6 +7777,20 @@ name = "zeroize"
|
|||||||
version = "1.8.2"
|
version = "1.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize_derive"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerotrie"
|
name = "zerotrie"
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ deadpool-postgres = { version = "0.14", optional = true }
|
|||||||
tokio-postgres = { version = "0.7", features = ["with-uuid-1", "with-chrono-0_4", "with-serde_json-1"], optional = true }
|
tokio-postgres = { version = "0.7", features = ["with-uuid-1", "with-chrono-0_4", "with-serde_json-1"], optional = true }
|
||||||
postgres-types = { version = "0.2", features = ["with-serde_json-1"], optional = true }
|
postgres-types = { version = "0.2", features = ["with-serde_json-1"], optional = true }
|
||||||
refinery = { version = "0.8", features = ["tokio-postgres"], optional = true }
|
refinery = { version = "0.8", features = ["tokio-postgres"], optional = true }
|
||||||
|
tokio-postgres-rustls = { version = "0.13", optional = true }
|
||||||
|
rustls = { version = "0.23", optional = true, default-features = false }
|
||||||
|
rustls-native-certs = { version = "0.8", optional = true }
|
||||||
|
|
||||||
# Database - libSQL/Turso (optional embedded database)
|
# Database - libSQL/Turso (optional embedded database)
|
||||||
libsql = { version = "0.6", optional = true, default-features = false, features = ["core", "replication"] }
|
libsql = { version = "0.6", optional = true, default-features = false, features = ["core", "replication"] }
|
||||||
@@ -179,6 +182,9 @@ default = ["postgres", "libsql", "html-to-markdown"]
|
|||||||
postgres = [
|
postgres = [
|
||||||
"dep:deadpool-postgres",
|
"dep:deadpool-postgres",
|
||||||
"dep:tokio-postgres",
|
"dep:tokio-postgres",
|
||||||
|
"dep:tokio-postgres-rustls",
|
||||||
|
"dep:rustls",
|
||||||
|
"dep:rustls-native-certs",
|
||||||
"dep:postgres-types",
|
"dep:postgres-types",
|
||||||
"dep:refinery",
|
"dep:refinery",
|
||||||
"dep:pgvector",
|
"dep:pgvector",
|
||||||
|
|||||||
+1
-5
@@ -171,11 +171,7 @@ async fn try_pg_connect() -> Result<(), String> {
|
|||||||
url: Some(url),
|
url: Some(url),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let pool = config
|
let pool = crate::db::tls::create_pool(&config, crate::config::SslMode::from_env())
|
||||||
.create_pool(
|
|
||||||
Some(deadpool_postgres::Runtime::Tokio1),
|
|
||||||
tokio_postgres::NoTls,
|
|
||||||
)
|
|
||||||
.map_err(|e| format!("pool error: {e}"))?;
|
.map_err(|e| format!("pool error: {e}"))?;
|
||||||
|
|
||||||
let client = tokio::time::timeout(std::time::Duration::from_secs(5), pool.get())
|
let client = tokio::time::timeout(std::time::Duration::from_secs(5), pool.get())
|
||||||
|
|||||||
+1
-5
@@ -169,11 +169,7 @@ async fn check_database() -> anyhow::Result<()> {
|
|||||||
url: Some(url),
|
url: Some(url),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let pool = config
|
let pool = crate::db::tls::create_pool(&config, crate::config::SslMode::from_env())
|
||||||
.create_pool(
|
|
||||||
Some(deadpool_postgres::Runtime::Tokio1),
|
|
||||||
tokio_postgres::NoTls,
|
|
||||||
)
|
|
||||||
.map_err(|e| anyhow::anyhow!("pool error: {}", e))?;
|
.map_err(|e| anyhow::anyhow!("pool error: {}", e))?;
|
||||||
|
|
||||||
let client = tokio::time::timeout(std::time::Duration::from_secs(5), pool.get())
|
let client = tokio::time::timeout(std::time::Duration::from_secs(5), pool.get())
|
||||||
|
|||||||
@@ -40,6 +40,48 @@ impl std::str::FromStr for DatabaseBackend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// PostgreSQL SSL/TLS mode, matching libpq semantics for the common cases.
|
||||||
|
///
|
||||||
|
/// Default is `Prefer`: attempt TLS, fall back to plaintext. This is the
|
||||||
|
/// safest non-breaking default — local Postgres without TLS keeps working
|
||||||
|
/// while managed providers (Neon, Supabase, RDS) automatically get TLS.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
|
pub enum SslMode {
|
||||||
|
/// Never use TLS (equivalent to libpq `sslmode=disable`).
|
||||||
|
Disable,
|
||||||
|
/// Try TLS first; fall back to plaintext on failure (default).
|
||||||
|
#[default]
|
||||||
|
Prefer,
|
||||||
|
/// Require TLS; fail if the server does not support it.
|
||||||
|
Require,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for SslMode {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Disable => write!(f, "disable"),
|
||||||
|
Self::Prefer => write!(f, "prefer"),
|
||||||
|
Self::Require => write!(f, "require"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::str::FromStr for SslMode {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s.to_lowercase().as_str() {
|
||||||
|
"disable" => Ok(Self::Disable),
|
||||||
|
"prefer" => Ok(Self::Prefer),
|
||||||
|
"require" => Ok(Self::Require),
|
||||||
|
_ => Err(format!(
|
||||||
|
"invalid DATABASE_SSLMODE '{}', expected 'disable', 'prefer', or 'require'",
|
||||||
|
s
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Database configuration.
|
/// Database configuration.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DatabaseConfig {
|
pub struct DatabaseConfig {
|
||||||
@@ -49,6 +91,8 @@ pub struct DatabaseConfig {
|
|||||||
// -- PostgreSQL fields --
|
// -- PostgreSQL fields --
|
||||||
pub url: SecretString,
|
pub url: SecretString,
|
||||||
pub pool_size: usize,
|
pub pool_size: usize,
|
||||||
|
/// TLS mode for PostgreSQL connections (default: Prefer).
|
||||||
|
pub ssl_mode: SslMode,
|
||||||
|
|
||||||
// -- libSQL fields --
|
// -- libSQL fields --
|
||||||
/// Path to local libSQL database file (default: ~/.ironclaw/ironclaw.db).
|
/// Path to local libSQL database file (default: ~/.ironclaw/ironclaw.db).
|
||||||
@@ -88,6 +132,15 @@ impl DatabaseConfig {
|
|||||||
|
|
||||||
let pool_size = parse_optional_env("DATABASE_POOL_SIZE", 10)?;
|
let pool_size = parse_optional_env("DATABASE_POOL_SIZE", 10)?;
|
||||||
|
|
||||||
|
let ssl_mode: SslMode = if let Some(s) = optional_env("DATABASE_SSLMODE")? {
|
||||||
|
s.parse().map_err(|e| ConfigError::InvalidValue {
|
||||||
|
key: "DATABASE_SSLMODE".to_string(),
|
||||||
|
message: e,
|
||||||
|
})?
|
||||||
|
} else {
|
||||||
|
SslMode::default()
|
||||||
|
};
|
||||||
|
|
||||||
let libsql_path = optional_env("LIBSQL_PATH")?.map(PathBuf::from).or_else(|| {
|
let libsql_path = optional_env("LIBSQL_PATH")?.map(PathBuf::from).or_else(|| {
|
||||||
if backend == DatabaseBackend::LibSql {
|
if backend == DatabaseBackend::LibSql {
|
||||||
Some(default_libsql_path())
|
Some(default_libsql_path())
|
||||||
@@ -110,6 +163,7 @@ impl DatabaseConfig {
|
|||||||
backend,
|
backend,
|
||||||
url: SecretString::from(url),
|
url: SecretString::from(url),
|
||||||
pool_size,
|
pool_size,
|
||||||
|
ssl_mode,
|
||||||
libsql_path,
|
libsql_path,
|
||||||
libsql_url,
|
libsql_url,
|
||||||
libsql_auth_token,
|
libsql_auth_token,
|
||||||
@@ -122,7 +176,52 @@ impl DatabaseConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SslMode {
|
||||||
|
/// Read from `DATABASE_SSLMODE` env var, defaulting to `Prefer`.
|
||||||
|
///
|
||||||
|
/// Silently falls back to `Prefer` on missing or unparseable values.
|
||||||
|
/// Used by lightweight CLI tools (status, doctor) that don't run the
|
||||||
|
/// full config pipeline.
|
||||||
|
pub fn from_env() -> Self {
|
||||||
|
std::env::var("DATABASE_SSLMODE")
|
||||||
|
.ok()
|
||||||
|
.and_then(|s| s.parse().ok())
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Default libSQL database path (~/.ironclaw/ironclaw.db).
|
/// Default libSQL database path (~/.ironclaw/ironclaw.db).
|
||||||
pub fn default_libsql_path() -> PathBuf {
|
pub fn default_libsql_path() -> PathBuf {
|
||||||
ironclaw_base_dir().join("ironclaw.db")
|
ironclaw_base_dir().join("ironclaw.db")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ssl_mode_default_is_prefer() {
|
||||||
|
assert_eq!(SslMode::default(), SslMode::Prefer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ssl_mode_parse_roundtrip() {
|
||||||
|
for mode in [SslMode::Disable, SslMode::Prefer, SslMode::Require] {
|
||||||
|
let s = mode.to_string();
|
||||||
|
let parsed: SslMode = s.parse().expect("should parse");
|
||||||
|
assert_eq!(parsed, mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ssl_mode_parse_case_insensitive() {
|
||||||
|
assert_eq!("DISABLE".parse::<SslMode>().unwrap(), SslMode::Disable);
|
||||||
|
assert_eq!("Prefer".parse::<SslMode>().unwrap(), SslMode::Prefer);
|
||||||
|
assert_eq!("REQUIRE".parse::<SslMode>().unwrap(), SslMode::Require);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ssl_mode_parse_invalid() {
|
||||||
|
assert!("invalid".parse::<SslMode>().is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -32,7 +32,7 @@ use crate::settings::Settings;
|
|||||||
pub use self::agent::AgentConfig;
|
pub use self::agent::AgentConfig;
|
||||||
pub use self::builder::BuilderModeConfig;
|
pub use self::builder::BuilderModeConfig;
|
||||||
pub use self::channels::{ChannelsConfig, CliConfig, GatewayConfig, HttpConfig, SignalConfig};
|
pub use self::channels::{ChannelsConfig, CliConfig, GatewayConfig, HttpConfig, SignalConfig};
|
||||||
pub use self::database::{DatabaseBackend, DatabaseConfig, default_libsql_path};
|
pub use self::database::{DatabaseBackend, DatabaseConfig, SslMode, default_libsql_path};
|
||||||
pub use self::embeddings::EmbeddingsConfig;
|
pub use self::embeddings::EmbeddingsConfig;
|
||||||
pub use self::heartbeat::HeartbeatConfig;
|
pub use self::heartbeat::HeartbeatConfig;
|
||||||
pub use self::hygiene::HygieneConfig;
|
pub use self::hygiene::HygieneConfig;
|
||||||
|
|||||||
@@ -12,6 +12,9 @@
|
|||||||
#[cfg(feature = "postgres")]
|
#[cfg(feature = "postgres")]
|
||||||
pub mod postgres;
|
pub mod postgres;
|
||||||
|
|
||||||
|
#[cfg(feature = "postgres")]
|
||||||
|
pub mod tls;
|
||||||
|
|
||||||
#[cfg(feature = "libsql")]
|
#[cfg(feature = "libsql")]
|
||||||
pub mod libsql;
|
pub mod libsql;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
//! TLS connector factory for PostgreSQL connections.
|
||||||
|
//!
|
||||||
|
//! Builds a [`deadpool_postgres::Pool`] with the appropriate TLS connector
|
||||||
|
//! based on the configured [`SslMode`]. Uses `rustls` with system root
|
||||||
|
//! certificates — the same TLS stack that `reqwest` already uses for HTTP.
|
||||||
|
|
||||||
|
use deadpool_postgres::{Pool, Runtime};
|
||||||
|
use tokio_postgres::NoTls;
|
||||||
|
use tokio_postgres_rustls::MakeRustlsConnect;
|
||||||
|
|
||||||
|
use crate::config::SslMode;
|
||||||
|
|
||||||
|
/// Build a rustls-based TLS connector using the platform's root certificate store.
|
||||||
|
fn make_rustls_connector() -> MakeRustlsConnect {
|
||||||
|
let mut root_store = rustls::RootCertStore::empty();
|
||||||
|
let native = rustls_native_certs::load_native_certs();
|
||||||
|
for e in &native.errors {
|
||||||
|
tracing::warn!("error loading system root certs: {e}");
|
||||||
|
}
|
||||||
|
for cert in native.certs {
|
||||||
|
if let Err(e) = root_store.add(cert) {
|
||||||
|
tracing::warn!("skipping invalid system root cert: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if root_store.is_empty() {
|
||||||
|
tracing::error!("no system root certificates found -- TLS connections will fail");
|
||||||
|
}
|
||||||
|
let config = rustls::ClientConfig::builder()
|
||||||
|
.with_root_certificates(root_store)
|
||||||
|
.with_no_client_auth();
|
||||||
|
MakeRustlsConnect::new(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a [`deadpool_postgres::Pool`] with the appropriate TLS connector.
|
||||||
|
///
|
||||||
|
/// - `Disable` → plain TCP (no TLS)
|
||||||
|
/// - `Prefer` / `Require` → rustls with system root certificates
|
||||||
|
///
|
||||||
|
/// **Note:** `Prefer` and `Require` currently behave identically — both
|
||||||
|
/// provide a TLS connector and will fail if the server rejects the TLS
|
||||||
|
/// handshake. True `prefer` semantics (retry without TLS on failure)
|
||||||
|
/// would require reconnection logic that tokio-postgres does not provide
|
||||||
|
/// out of the box. The three-variant enum is kept for forward-compatibility
|
||||||
|
/// and familiarity with libpq's `sslmode` parameter.
|
||||||
|
pub fn create_pool(
|
||||||
|
config: &deadpool_postgres::Config,
|
||||||
|
ssl_mode: SslMode,
|
||||||
|
) -> Result<Pool, deadpool_postgres::CreatePoolError> {
|
||||||
|
match ssl_mode {
|
||||||
|
SslMode::Disable => config.create_pool(Some(Runtime::Tokio1), NoTls),
|
||||||
|
SslMode::Prefer | SslMode::Require => {
|
||||||
|
let tls = make_rustls_connector();
|
||||||
|
config.create_pool(Some(Runtime::Tokio1), tls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_pool_disable_mode() {
|
||||||
|
let mut config = deadpool_postgres::Config::new();
|
||||||
|
config.url = Some("postgres://localhost/test".to_string());
|
||||||
|
// Should succeed — pool is created lazily, no actual connection needed.
|
||||||
|
let pool = create_pool(&config, SslMode::Disable);
|
||||||
|
assert!(pool.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_pool_prefer_mode() {
|
||||||
|
let mut config = deadpool_postgres::Config::new();
|
||||||
|
config.url = Some("postgres://localhost/test".to_string());
|
||||||
|
let pool = create_pool(&config, SslMode::Prefer);
|
||||||
|
assert!(pool.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_pool_require_mode() {
|
||||||
|
let mut config = deadpool_postgres::Config::new();
|
||||||
|
config.url = Some("postgres://localhost/test".to_string());
|
||||||
|
let pool = create_pool(&config, SslMode::Require);
|
||||||
|
assert!(pool.is_ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,10 +2,8 @@
|
|||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
#[cfg(feature = "postgres")]
|
#[cfg(feature = "postgres")]
|
||||||
use deadpool_postgres::{Config, Pool, Runtime};
|
use deadpool_postgres::{Config, Pool};
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
#[cfg(feature = "postgres")]
|
|
||||||
use tokio_postgres::NoTls;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[cfg(feature = "postgres")]
|
#[cfg(feature = "postgres")]
|
||||||
@@ -50,8 +48,7 @@ impl Store {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
let pool = cfg
|
let pool = crate::db::tls::create_pool(&cfg, config.ssl_mode)
|
||||||
.create_pool(Some(Runtime::Tokio1), NoTls)
|
|
||||||
.map_err(|e| DatabaseError::Pool(e.to_string()))?;
|
.map_err(|e| DatabaseError::Pool(e.to_string()))?;
|
||||||
|
|
||||||
// Test connection
|
// Test connection
|
||||||
|
|||||||
+2
-5
@@ -15,10 +15,8 @@ use std::collections::{HashMap, HashSet};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[cfg(feature = "postgres")]
|
#[cfg(feature = "postgres")]
|
||||||
use deadpool_postgres::{Config as PoolConfig, Runtime};
|
use deadpool_postgres::Config as PoolConfig;
|
||||||
use secrecy::{ExposeSecret, SecretString};
|
use secrecy::{ExposeSecret, SecretString};
|
||||||
#[cfg(feature = "postgres")]
|
|
||||||
use tokio_postgres::NoTls;
|
|
||||||
|
|
||||||
use crate::bootstrap::ironclaw_base_dir;
|
use crate::bootstrap::ironclaw_base_dir;
|
||||||
use crate::channels::wasm::{
|
use crate::channels::wasm::{
|
||||||
@@ -556,8 +554,7 @@ impl SetupWizard {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
let pool = cfg
|
let pool = crate::db::tls::create_pool(&cfg, crate::config::SslMode::from_env())
|
||||||
.create_pool(Some(Runtime::Tokio1), NoTls)
|
|
||||||
.map_err(|e| SetupError::Database(format!("Failed to create pool: {}", e)))?;
|
.map_err(|e| SetupError::Database(format!("Failed to create pool: {}", e)))?;
|
||||||
|
|
||||||
let client = pool
|
let client = pool
|
||||||
|
|||||||
Reference in New Issue
Block a user