judu/ex-egopublic Fork
⑂ df9986a
Commits
⬇ Clone ▾
git clone https://git.rickub.com/judu/ex-ego.git
git clone ssh://git@rickub.com/judu/ex-ego.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

fix: TLS imposé sur le pool Postgres quelle que soit l'URLUnverified

PgConnectOptions + ssl_mode(PgSslMode::Require) en dur, au lieu
d'ajouter sslmode=require à l'URL seulement quand il manquait (une URL
sslmode=disable/prefer passait en l'état — et le serveur CC accepte le
clair, vérifié via pg_stat_ssl). Test unitaire sur les 4 variantes
d'URL ; connexion TLSv1.3 constatée même avec sslmode=disable.
Julien Durillon committed 2026-08-06T23:09:17+02:00 Browse files
df9986a parent: e8f7a6a
modified src/cli.rs +1 -1
@@ -37,7 +37,7 @@ pub async fn user_add(username: &str, password_arg: Option<String>) -> Result<()
3737
3838 let pool = PgPoolOptions::new()
3939 .max_connections(1)
40- .connect(&config::database_url()?)
40+ .connect_with(config::pg_options()?)
4141 .await
4242 .context("connexion à la base")?;
4343 sqlx::migrate!().run(&pool).await?;
@@ -37,7 +37,7 @@ pub async fn user_add(username: &str, password_arg: Option<String>) -> Result<()
37 37
38 let pool = PgPoolOptions::new()38 let pool = PgPoolOptions::new()
39 .max_connections(1)39 .max_connections(1)
40- .connect(&config::database_url()?)40+ .connect_with(config::pg_options()?)
41 .await41 .await
42 .context("connexion à la base")?;42 .context("connexion à la base")?;
43 sqlx::migrate!().run(&pool).await?;43 sqlx::migrate!().run(&pool).await?;
modified src/config.rs +24 -25
@@ -1,22 +1,21 @@
1+use std::str::FromStr;
2+
13 use anyhow::{Context, Result};
4+use sqlx::postgres::{PgConnectOptions, PgSslMode};
25
3-/// URL de la base : POSTGRESQL_ADDON_URI (Clever Cloud) sinon DATABASE_URL,
4-/// avec sslmode=require imposé si absent.
5-pub fn database_url() -> Result<String> {
6+/// Options de connexion : POSTGRESQL_ADDON_URI (Clever Cloud) sinon
7+/// DATABASE_URL, avec TLS **imposé** (`PgSslMode::Require`) quoi que
8+/// dise l'URL — y compris `sslmode=disable`.
9+pub fn pg_options() -> Result<PgConnectOptions> {
610 let url = std::env::var("POSTGRESQL_ADDON_URI")
711 .or_else(|_| std::env::var("DATABASE_URL"))
812 .context("POSTGRESQL_ADDON_URI ou DATABASE_URL doit être définie")?;
9- Ok(ensure_sslmode_require(&url))
13+ Ok(force_tls(&url)?)
1014 }
1115
12-fn ensure_sslmode_require(url: &str) -> String {
13- if url.contains("sslmode=") {
14- url.to_string()
15- } else if url.contains('?') {
16- format!("{url}&sslmode=require")
17- } else {
18- format!("{url}?sslmode=require")
19- }
16+fn force_tls(url: &str) -> Result<PgConnectOptions> {
17+ let opts = PgConnectOptions::from_str(url).context("URL de base invalide")?;
18+ Ok(opts.ssl_mode(PgSslMode::Require))
2019 }
2120
2221 pub fn port() -> u16 {
@@ -36,18 +35,18 @@ mod tests {
3635 use super::*;
3736
3837 #[test]
39- fn sslmode_appended() {
40- assert_eq!(
41- ensure_sslmode_require("postgres://u:p@h:5432/db"),
42- "postgres://u:p@h:5432/db?sslmode=require"
43- );
44- assert_eq!(
45- ensure_sslmode_require("postgres://u:p@h:5432/db?foo=1"),
46- "postgres://u:p@h:5432/db?foo=1&sslmode=require"
47- );
48- assert_eq!(
49- ensure_sslmode_require("postgres://u:p@h:5432/db?sslmode=verify-full"),
50- "postgres://u:p@h:5432/db?sslmode=verify-full"
51- );
38+ fn tls_forced_whatever_the_url_says() {
39+ for url in [
40+ "postgres://u:p@h:5432/db",
41+ "postgres://u:p@h:5432/db?sslmode=disable",
42+ "postgres://u:p@h:5432/db?sslmode=prefer",
43+ "postgres://u:p@h:5432/db?sslmode=verify-full",
44+ ] {
45+ let opts = force_tls(url).unwrap();
46+ assert!(
47+ format!("{opts:?}").contains("Require"),
48+ "TLS non imposé pour {url}"
49+ );
50+ }
5251 }
5352 }
@@ -1,22 +1,21 @@
1+use std::str::FromStr;
2+
1 use anyhow::{Context, Result};3 use anyhow::{Context, Result};
4+use sqlx::postgres::{PgConnectOptions, PgSslMode};
2 5
3-/// URL de la base : POSTGRESQL_ADDON_URI (Clever Cloud) sinon DATABASE_URL,6+/// Options de connexion : POSTGRESQL_ADDON_URI (Clever Cloud) sinon
4-/// avec sslmode=require imposé si absent.7+/// DATABASE_URL, avec TLS **imposé** (`PgSslMode::Require`) quoi que
5-pub fn database_url() -> Result<String> {8+/// dise l'URL — y compris `sslmode=disable`.
9+pub fn pg_options() -> Result<PgConnectOptions> {
6 let url = std::env::var("POSTGRESQL_ADDON_URI")10 let url = std::env::var("POSTGRESQL_ADDON_URI")
7 .or_else(|_| std::env::var("DATABASE_URL"))11 .or_else(|_| std::env::var("DATABASE_URL"))
8 .context("POSTGRESQL_ADDON_URI ou DATABASE_URL doit être définie")?;12 .context("POSTGRESQL_ADDON_URI ou DATABASE_URL doit être définie")?;
9- Ok(ensure_sslmode_require(&url))13+ Ok(force_tls(&url)?)
10 }14 }
11 15
12-fn ensure_sslmode_require(url: &str) -> String {16+fn force_tls(url: &str) -> Result<PgConnectOptions> {
13- if url.contains("sslmode=") {17+ let opts = PgConnectOptions::from_str(url).context("URL de base invalide")?;
14- url.to_string()18+ Ok(opts.ssl_mode(PgSslMode::Require))
15- } else if url.contains('?') {
16- format!("{url}&sslmode=require")
17- } else {
18- format!("{url}?sslmode=require")
19- }
20 }19 }
21 20
22 pub fn port() -> u16 {21 pub fn port() -> u16 {
@@ -36,18 +35,18 @@ mod tests {
36 use super::*;35 use super::*;
37 36
38 #[test]37 #[test]
39- fn sslmode_appended() {38+ fn tls_forced_whatever_the_url_says() {
40- assert_eq!(39+ for url in [
41- ensure_sslmode_require("postgres://u:p@h:5432/db"),40+ "postgres://u:p@h:5432/db",
42- "postgres://u:p@h:5432/db?sslmode=require"41+ "postgres://u:p@h:5432/db?sslmode=disable",
43- );42+ "postgres://u:p@h:5432/db?sslmode=prefer",
44- assert_eq!(43+ "postgres://u:p@h:5432/db?sslmode=verify-full",
45- ensure_sslmode_require("postgres://u:p@h:5432/db?foo=1"),44+ ] {
46- "postgres://u:p@h:5432/db?foo=1&sslmode=require"45+ let opts = force_tls(url).unwrap();
47- );46+ assert!(
48- assert_eq!(47+ format!("{opts:?}").contains("Require"),
49- ensure_sslmode_require("postgres://u:p@h:5432/db?sslmode=verify-full"),48+ "TLS non imposé pour {url}"
50- "postgres://u:p@h:5432/db?sslmode=verify-full"49+ );
51- );50+ }
52 }51 }
53 }52 }
modified src/main.rs +1 -1
@@ -70,7 +70,7 @@ async fn serve() -> Result<()> {
7070
7171 let pool = PgPoolOptions::new()
7272 .max_connections(5)
73- .connect(&config::database_url()?)
73+ .connect_with(config::pg_options()?)
7474 .await
7575 .context("connexion à la base")?;
7676 sqlx::migrate!().run(&pool).await.context("migrations")?;
@@ -70,7 +70,7 @@ async fn serve() -> Result<()> {
70 70
71 let pool = PgPoolOptions::new()71 let pool = PgPoolOptions::new()
72 .max_connections(5)72 .max_connections(5)
73- .connect(&config::database_url()?)73+ .connect_with(config::pg_options()?)
74 .await74 .await
75 .context("connexion à la base")?;75 .context("connexion à la base")?;
76 sqlx::migrate!().run(&pool).await.context("migrations")?;76 sqlx::migrate!().run(&pool).await.context("migrations")?;