diff --git a/.github/workflows/sqlx.yml b/.github/workflows/sqlx.yml index 2ca687ca2b..3e71536a96 100644 --- a/.github/workflows/sqlx.yml +++ b/.github/workflows/sqlx.yml @@ -232,6 +232,26 @@ jobs: # but `PgLTree` should just fall back to text format RUSTFLAGS: --cfg postgres_${{ matrix.postgres }} + # client SSL authentication + + - run: | + docker stop postgres_${{ matrix.postgres }} + docker-compose -f tests/docker-compose.yml run -d -p 5432:5432 --name postgres_${{ matrix.postgres }}_client_ssl postgres_${{ matrix.postgres }}_client_ssl + docker exec postgres_${{ matrix.postgres }}_client_ssl bash -c "until pg_isready; do sleep 1; done" + + - uses: actions-rs/cargo@v1 + if: matrix.tls != 'none' + with: + command: test + args: > + --no-default-features + --features any,postgres,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} + env: + DATABASE_URL: postgres://postgres@localhost:5432/sqlx?sslmode=verify-ca&sslrootcert=.%2Ftests%2Fcerts%2Fca.crt&sslkey=.%2Ftests%2Fkeys%2Fclient.key&sslcert=.%2Ftests%2Fcerts%2Fclient.crt + # FIXME: needed to disable `ltree` tests in Postgres 9.6 + # but `PgLTree` should just fall back to text format + RUSTFLAGS: --cfg postgres_${{ matrix.postgres }}_client_ssl + mysql: name: MySQL runs-on: ubuntu-20.04 @@ -260,7 +280,7 @@ jobs: args: > --features mysql,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} - - run: docker-compose -f tests/docker-compose.yml run -d -p 3306:3306 mysql_${{ matrix.mysql }} + - run: docker-compose -f tests/docker-compose.yml run -d -p 3306:3306 --name mysql_${{ matrix.mysql }} mysql_${{ matrix.mysql }} - run: sleep 60 - uses: actions-rs/cargo@v1 @@ -285,6 +305,25 @@ jobs: DATABASE_URL: mysql://root:password@localhost:3306/sqlx RUSTFLAGS: --cfg mysql_${{ matrix.mysql }} + # client SSL authentication + + - run: | + docker stop mysql_${{ matrix.mysql }} + docker-compose -f tests/docker-compose.yml run -d -p 3306:3306 --name mysql_${{ matrix.mysql }}_client_ssl mysql_${{ matrix.mysql }}_client_ssl + sleep 60 + + # MySQL 5.7 supports TLS but not TLSv1.3 as required by RusTLS. + - uses: actions-rs/cargo@v1 + if: ${{ !(matrix.mysql == '5_7' && matrix.tls == 'rustls') && matrix.tls != 'none' }} + with: + command: test + args: > + --no-default-features + --features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} + env: + DATABASE_URL: mysql://root@localhost:3306/sqlx?sslmode=verify_ca&ssl-ca=.%2Ftests%2Fcerts%2Fca.crt&ssl-key=.%2Ftests%2Fkeys%2Fclient.key&ssl-cert=.%2Ftests%2Fcerts%2Fclient.crt + RUSTFLAGS: --cfg mysql_${{ matrix.mysql }} + mariadb: name: MariaDB runs-on: ubuntu-20.04 @@ -313,7 +352,7 @@ jobs: args: > --features mysql,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} - - run: docker-compose -f tests/docker-compose.yml run -d -p 3306:3306 mariadb_${{ matrix.mariadb }} + - run: docker-compose -f tests/docker-compose.yml run -d -p 3306:3306 --name mariadb_${{ matrix.mariadb }} mariadb_${{ matrix.mariadb }} - run: sleep 30 - uses: actions-rs/cargo@v1 @@ -325,3 +364,21 @@ jobs: env: DATABASE_URL: mysql://root:password@localhost:3306/sqlx RUSTFLAGS: --cfg mariadb_${{ matrix.mariadb }} + + # client SSL authentication + + - run: | + docker stop mariadb_${{ matrix.mariadb }} + docker-compose -f tests/docker-compose.yml run -d -p 3306:3306 --name mariadb_${{ matrix.mariadb }}_client_ssl mariadb_${{ matrix.mariadb }}_client_ssl + sleep 60 + + - uses: actions-rs/cargo@v1 + if: matrix.tls != 'none' + with: + command: test + args: > + --no-default-features + --features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} + env: + DATABASE_URL: mysql://root@localhost:3306/sqlx?sslmode=verify_ca&ssl-ca=.%2Ftests%2Fcerts%2Fca.crt&ssl-key=.%2Ftests%2Fkeys%2Fclient.key&ssl-cert=.%2Ftests%2Fcerts%2Fclient.crt + RUSTFLAGS: --cfg mariadb_${{ matrix.mariadb }} diff --git a/sqlx-core/src/net/tls/mod.rs b/sqlx-core/src/net/tls/mod.rs index 3fae0ecaed..0b1df2b341 100644 --- a/sqlx-core/src/net/tls/mod.rs +++ b/sqlx-core/src/net/tls/mod.rs @@ -61,6 +61,8 @@ pub struct TlsConfig<'a> { pub accept_invalid_hostnames: bool, pub hostname: &'a str, pub root_cert_path: Option<&'a CertificateInput>, + pub client_cert_path: Option<&'a CertificateInput>, + pub client_key_path: Option<&'a CertificateInput>, } pub async fn handshake( diff --git a/sqlx-core/src/net/tls/tls_native_tls.rs b/sqlx-core/src/net/tls/tls_native_tls.rs index 4b0e3085d4..1c40b4b01f 100644 --- a/sqlx-core/src/net/tls/tls_native_tls.rs +++ b/sqlx-core/src/net/tls/tls_native_tls.rs @@ -6,7 +6,7 @@ use crate::net::tls::TlsConfig; use crate::net::Socket; use crate::Error; -use native_tls::HandshakeError; +use native_tls::{HandshakeError, Identity}; use std::task::{Context, Poll}; pub struct NativeTlsSocket { @@ -53,6 +53,14 @@ pub async fn handshake( builder.add_root_certificate(native_tls::Certificate::from_pem(&data).map_err(Error::tls)?); } + // authentication using user's key-file and its associated certificate + if let (Some(cert_path), Some(key_path)) = (config.client_cert_path, config.client_key_path) { + let cert_path = cert_path.data().await?; + let key_path = key_path.data().await?; + let identity = Identity::from_pkcs8(&cert_path, &key_path).map_err(Error::tls)?; + builder.identity(identity); + } + let connector = builder.build().map_err(Error::tls)?; let mut mid_handshake = match connector.connect(config.hostname, StdSocket::new(socket)) { diff --git a/sqlx-core/src/net/tls/tls_rustls.rs b/sqlx-core/src/net/tls/tls_rustls.rs index 230e03527f..ab0f721f79 100644 --- a/sqlx-core/src/net/tls/tls_rustls.rs +++ b/sqlx-core/src/net/tls/tls_rustls.rs @@ -1,6 +1,6 @@ use futures_util::future; -use std::io; -use std::io::{Cursor, Read, Write}; +use rustls::{Certificate, PrivateKey}; +use std::io::{self, BufReader, Cursor, Read, Write}; use std::sync::Arc; use std::task::{Context, Poll}; use std::time::SystemTime; @@ -13,7 +13,7 @@ use rustls::{ use crate::error::Error; use crate::io::ReadBuf; use crate::net::tls::util::StdSocket; -use crate::net::tls::TlsConfig; +use crate::net::tls::{CertificateInput, TlsConfig}; use crate::net::Socket; pub struct RustlsSocket { @@ -48,7 +48,7 @@ impl Socket for RustlsSocket { match self.state.writer().write(buf) { // Returns a zero-length write when the buffer is full. Ok(0) => Err(io::ErrorKind::WouldBlock.into()), - other => return other, + other => other, } } @@ -81,10 +81,32 @@ where { let config = ClientConfig::builder().with_safe_defaults(); + // authentication using user's key and its associated certificate + let user_auth = match (tls_config.client_cert_path, tls_config.client_key_path) { + (Some(cert_path), Some(key_path)) => { + let cert_chain = certs_from_pem(cert_path.data().await?)?; + let key_der = private_key_from_pem(key_path.data().await?)?; + Some((cert_chain, key_der)) + } + (None, None) => None, + (_, _) => { + return Err(Error::Configuration( + "user auth key and certs must be given together".into(), + )) + } + }; + let config = if tls_config.accept_invalid_certs { - config - .with_custom_certificate_verifier(Arc::new(DummyTlsVerifier)) - .with_no_client_auth() + if let Some(user_auth) = user_auth { + config + .with_custom_certificate_verifier(Arc::new(DummyTlsVerifier)) + .with_single_cert(user_auth.0, user_auth.1) + .map_err(Error::tls)? + } else { + config + .with_custom_certificate_verifier(Arc::new(DummyTlsVerifier)) + .with_no_client_auth() + } } else { let mut cert_store = RootCertStore::empty(); cert_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| { @@ -100,7 +122,7 @@ where let mut cursor = Cursor::new(data); for cert in rustls_pemfile::certs(&mut cursor) - .map_err(|_| Error::Tls(format!("Invalid certificate {}", ca).into()))? + .map_err(|_| Error::Tls(format!("Invalid certificate {ca}").into()))? { cert_store .add(&rustls::Certificate(cert)) @@ -111,9 +133,21 @@ where if tls_config.accept_invalid_hostnames { let verifier = WebPkiVerifier::new(cert_store, None); + if let Some(user_auth) = user_auth { + config + .with_custom_certificate_verifier(Arc::new(NoHostnameTlsVerifier { verifier })) + .with_single_cert(user_auth.0, user_auth.1) + .map_err(Error::tls)? + } else { + config + .with_custom_certificate_verifier(Arc::new(NoHostnameTlsVerifier { verifier })) + .with_no_client_auth() + } + } else if let Some(user_auth) = user_auth { config - .with_custom_certificate_verifier(Arc::new(NoHostnameTlsVerifier { verifier })) - .with_no_client_auth() + .with_root_certificates(cert_store) + .with_single_cert(user_auth.0, user_auth.1) + .map_err(Error::tls)? } else { config .with_root_certificates(cert_store) @@ -135,6 +169,34 @@ where Ok(socket) } +fn certs_from_pem(pem: Vec) -> Result, Error> { + let cur = Cursor::new(pem); + let mut reader = BufReader::new(cur); + rustls_pemfile::certs(&mut reader)? + .into_iter() + .map(|v| Ok(rustls::Certificate(v))) + .collect() +} + +fn private_key_from_pem(pem: Vec) -> Result { + let cur = Cursor::new(pem); + let mut reader = BufReader::new(cur); + + loop { + match rustls_pemfile::read_one(&mut reader)? { + Some( + rustls_pemfile::Item::RSAKey(key) + | rustls_pemfile::Item::PKCS8Key(key) + | rustls_pemfile::Item::ECKey(key), + ) => return Ok(rustls::PrivateKey(key)), + None => break, + _ => {} + } + } + + Err(Error::Configuration("no keys found pem file".into())) +} + struct DummyTlsVerifier; impl ServerCertVerifier for DummyTlsVerifier { diff --git a/sqlx-mysql/src/connection/tls.rs b/sqlx-mysql/src/connection/tls.rs index b8cb05c9d3..f98c5c532f 100644 --- a/sqlx-mysql/src/connection/tls.rs +++ b/sqlx-mysql/src/connection/tls.rs @@ -64,6 +64,8 @@ pub(super) async fn maybe_upgrade( accept_invalid_hostnames: !matches!(options.ssl_mode, MySqlSslMode::VerifyIdentity), hostname: &options.host, root_cert_path: options.ssl_ca.as_ref(), + client_cert_path: options.ssl_client_cert.as_ref(), + client_key_path: options.ssl_client_key.as_ref(), }; // Request TLS upgrade diff --git a/sqlx-mysql/src/options/mod.rs b/sqlx-mysql/src/options/mod.rs index d0959579d1..dc06380b07 100644 --- a/sqlx-mysql/src/options/mod.rs +++ b/sqlx-mysql/src/options/mod.rs @@ -61,6 +61,8 @@ pub struct MySqlConnectOptions { pub(crate) database: Option, pub(crate) ssl_mode: MySqlSslMode, pub(crate) ssl_ca: Option, + pub(crate) ssl_client_cert: Option, + pub(crate) ssl_client_key: Option, pub(crate) statement_cache_capacity: usize, pub(crate) charset: String, pub(crate) collation: Option, @@ -88,6 +90,8 @@ impl MySqlConnectOptions { collation: None, ssl_mode: MySqlSslMode::Preferred, ssl_ca: None, + ssl_client_cert: None, + ssl_client_key: None, statement_cache_capacity: 100, log_settings: Default::default(), pipes_as_concat: true, @@ -186,6 +190,36 @@ impl MySqlConnectOptions { self } + /// Sets the name of a file containing SSL client certificate. + /// + /// # Example + /// + /// ```rust + /// # use sqlx_core::mysql::{MySqlSslMode, MySqlConnectOptions}; + /// let options = MySqlConnectOptions::new() + /// .ssl_mode(MySqlSslMode::VerifyCa) + /// .ssl_client_cert("path/to/client.crt"); + /// ``` + pub fn ssl_client_cert(mut self, cert: impl AsRef) -> Self { + self.ssl_client_cert = Some(CertificateInput::File(cert.as_ref().to_path_buf())); + self + } + + /// Sets the name of a file containing SSL client key. + /// + /// # Example + /// + /// ```rust + /// # use sqlx_core::mysql::{MySqlSslMode, MySqlConnectOptions}; + /// let options = MySqlConnectOptions::new() + /// .ssl_mode(MySqlSslMode::VerifyCa) + /// .ssl_client_key("path/to/client.key"); + /// ``` + pub fn ssl_client_key(mut self, key: impl AsRef) -> Self { + self.ssl_client_key = Some(CertificateInput::File(key.as_ref().to_path_buf())); + self + } + /// Sets the capacity of the connection's statement cache in a number of stored /// distinct statements. Caching is handled using LRU, meaning when the /// amount of queries hits the defined limit, the oldest statement will get diff --git a/sqlx-mysql/src/options/parse.rs b/sqlx-mysql/src/options/parse.rs index 854a95c427..5ba5c3207a 100644 --- a/sqlx-mysql/src/options/parse.rs +++ b/sqlx-mysql/src/options/parse.rs @@ -43,11 +43,11 @@ impl MySqlConnectOptions { for (key, value) in url.query_pairs().into_iter() { match &*key { - "ssl-mode" => { + "sslmode" | "ssl-mode" => { options = options.ssl_mode(value.parse().map_err(Error::config)?); } - "ssl-ca" => { + "sslca" | "ssl-ca" => { options = options.ssl_ca(&*value); } @@ -59,6 +59,10 @@ impl MySqlConnectOptions { options = options.collation(&*value); } + "sslcert" | "ssl-cert" => options = options.ssl_client_cert(&*value), + + "sslkey" | "ssl-key" => options = options.ssl_client_key(&*value), + "statement-cache-capacity" => { options = options.statement_cache_capacity(value.parse().map_err(Error::config)?); diff --git a/sqlx-postgres/src/connection/tls.rs b/sqlx-postgres/src/connection/tls.rs index b9e40d90dc..04bab793a7 100644 --- a/sqlx-postgres/src/connection/tls.rs +++ b/sqlx-postgres/src/connection/tls.rs @@ -58,6 +58,8 @@ async fn maybe_upgrade( accept_invalid_hostnames, hostname: &options.host, root_cert_path: options.ssl_root_cert.as_ref(), + client_cert_path: options.ssl_client_cert.as_ref(), + client_key_path: options.ssl_client_key.as_ref(), }; tls::handshake(socket, config, SocketIntoBox).await diff --git a/sqlx-postgres/src/options/mod.rs b/sqlx-postgres/src/options/mod.rs index 9bca6629df..0f1972f92d 100644 --- a/sqlx-postgres/src/options/mod.rs +++ b/sqlx-postgres/src/options/mod.rs @@ -87,6 +87,8 @@ pub struct PgConnectOptions { pub(crate) database: Option, pub(crate) ssl_mode: PgSslMode, pub(crate) ssl_root_cert: Option, + pub(crate) ssl_client_cert: Option, + pub(crate) ssl_client_key: Option, pub(crate) statement_cache_capacity: usize, pub(crate) application_name: Option, pub(crate) log_settings: LogSettings, @@ -112,6 +114,8 @@ impl PgConnectOptions { /// * `PGPASSWORD` /// * `PGDATABASE` /// * `PGSSLROOTCERT` + /// * `PGSSLCERT` + /// * `PGSSLKEY` /// * `PGSSLMODE` /// * `PGAPPNAME` /// @@ -145,6 +149,8 @@ impl PgConnectOptions { password: var("PGPASSWORD").ok(), database, ssl_root_cert: var("PGSSLROOTCERT").ok().map(CertificateInput::from), + ssl_client_cert: var("PGSSLCERT").ok().map(CertificateInput::from), + ssl_client_key: var("PGSSLKEY").ok().map(CertificateInput::from), ssl_mode: var("PGSSLMODE") .ok() .and_then(|v| v.parse().ok()) @@ -314,6 +320,38 @@ impl PgConnectOptions { self } + /// Sets the name of a file containing SSL client certificate. + /// + /// # Example + /// + /// ```rust + /// # use sqlx_core::postgres::{PgSslMode, PgConnectOptions}; + /// let options = PgConnectOptions::new() + /// // Providing a CA certificate with less than VerifyCa is pointless + /// .ssl_mode(PgSslMode::VerifyCa) + /// .ssl_client_cert("./client.crt"); + /// ``` + pub fn ssl_client_cert(mut self, cert: impl AsRef) -> Self { + self.ssl_client_cert = Some(CertificateInput::File(cert.as_ref().to_path_buf())); + self + } + + /// Sets the name of a file containing SSL client key. + /// + /// # Example + /// + /// ```rust + /// # use sqlx_core::postgres::{PgSslMode, PgConnectOptions}; + /// let options = PgConnectOptions::new() + /// // Providing a CA certificate with less than VerifyCa is pointless + /// .ssl_mode(PgSslMode::VerifyCa) + /// .ssl_client_key("./client.key"); + /// ``` + pub fn ssl_client_key(mut self, key: impl AsRef) -> Self { + self.ssl_client_key = Some(CertificateInput::File(key.as_ref().to_path_buf())); + self + } + /// Sets PEM encoded trusted SSL Certificate Authorities (CA). /// /// # Example diff --git a/sqlx-postgres/src/options/parse.rs b/sqlx-postgres/src/options/parse.rs index e837a95019..4c5cf41c3e 100644 --- a/sqlx-postgres/src/options/parse.rs +++ b/sqlx-postgres/src/options/parse.rs @@ -53,6 +53,10 @@ impl PgConnectOptions { options = options.ssl_root_cert(&*value); } + "sslcert" | "ssl-cert" => options = options.ssl_client_cert(&*value), + + "sslkey" | "ssl-key" => options = options.ssl_client_key(&*value), + "statement-cache-capacity" => { options = options.statement_cache_capacity(value.parse().map_err(Error::config)?); diff --git a/tests/.dockerignore b/tests/.dockerignore index 6c513a8a33..ca8cb8cf84 100644 --- a/tests/.dockerignore +++ b/tests/.dockerignore @@ -1,5 +1,7 @@ * !certs/* !keys/* +!mysql/my.cnf !mssql/*.sh +!postgres/pg_hba.conf !*/*.sql diff --git a/tests/certs/ca.srl b/tests/certs/ca.srl new file mode 100644 index 0000000000..ef2f121531 --- /dev/null +++ b/tests/certs/ca.srl @@ -0,0 +1 @@ +B6C71F4B11C0A189 diff --git a/tests/certs/client.crt b/tests/certs/client.crt new file mode 100644 index 0000000000..0edde05434 --- /dev/null +++ b/tests/certs/client.crt @@ -0,0 +1,57 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: + e0:be:1f:7a:49:1e:49:ec + Signature Algorithm: NULL + Issuer: CN = postgres + Validity + Not Before: Apr 10 20:59:23 2021 GMT + Not After : Apr 8 20:59:23 2031 GMT + Subject: CN = postgres + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:bf:4f:18:ca:d8:ff:a3:93:aa:9a:3b:90:35:c7: + ff:82:65:d1:d0:e8:65:9d:9c:6c:cb:70:4e:31:7e: + 7e:52:ce:2d:85:7a:83:ee:b8:eb:f1:ba:37:0e:34: + 66:3d:b6:db:cb:45:6f:64:0f:5c:4d:ba:53:25:c9: + ff:e0:a1:39:9b:82:c9:c0:08:e8:17:6b:01:6a:99: + 47:05:d8:c5:2f:83:f3:33:f7:ad:bb:f3:dd:5f:6a: + 95:4f:d9:8e:1d:bc:ff:84:78:77:eb:98:40:36:2d: + 9a:a3:29:a6:ba:58:90:c1:92:88:5f:07:c3:a8:a6: + 06:f0:ca:f8:81:40:13:65:1d:08:6c:97:9f:d4:b4: + 8d:f7:77:32:f6:2c:d4:9b:07:b3:86:3a:62:7f:da: + 3d:3c:e9:96:71:cc:62:2e:ac:6d:00:ca:ac:6c:a1: + b4:68:28:67:18:be:4b:31:e7:f1:c3:1d:a4:ad:05: + 50:59:44:30:09:b1:91:e1:86:5d:ec:75:06:a9:70: + 43:6b:81:5c:ff:98:fd:22:5c:3a:0e:08:2e:e3:b3: + c4:e0:65:dd:cd:e7:f2:69:08:0a:1b:90:c4:06:c1: + 06:ee:75:ee:d3:3c:ab:a2:9c:51:00:1c:56:fe:24: + 92:36:ee:e1:f3:6f:0c:14:79:32:07:f9:12:2b:26: + 79:c1 + Exponent: 65537 (0x10001) + Signature Algorithm: NULL +-----BEGIN CERTIFICATE----- +MIIDjjCCAfYCCQC2xx9LEcChiTANBgkqhkiG9w0BAQsFADB/MR4wHAYDVQQKExVt +a2NlcnQgZGV2ZWxvcG1lbnQgQ0ExKjAoBgNVBAsMIW1laGNvZGVAR29sZW0ubG9j +YWwgKFJ5YW4gTGVja2V5KTExMC8GA1UEAwwobWtjZXJ0IG1laGNvZGVAR29sZW0u +bG9jYWwgKFJ5YW4gTGVja2V5KTAeFw0yMTA0MTAyMDU5MjNaFw0zMTA0MDgyMDU5 +MjNaMBMxETAPBgNVBAMMCHBvc3RncmVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAv08Yytj/o5OqmjuQNcf/gmXR0OhlnZxsy3BOMX5+Us4thXqD7rjr +8bo3DjRmPbbby0VvZA9cTbpTJcn/4KE5m4LJwAjoF2sBaplHBdjFL4PzM/etu/Pd +X2qVT9mOHbz/hHh365hANi2aoymmuliQwZKIXwfDqKYG8Mr4gUATZR0IbJef1LSN +93cy9izUmwezhjpif9o9POmWccxiLqxtAMqsbKG0aChnGL5LMefxwx2krQVQWUQw +CbGR4YZd7HUGqXBDa4Fc/5j9Ilw6Dggu47PE4GXdzefyaQgKG5DEBsEG7nXu0zyr +opxRABxW/iSSNu7h828MFHkyB/kSKyZ5wQIDAQABMA0GCSqGSIb3DQEBCwUAA4IB +gQBxzRXtmp1gXzNTnwQ+acdZ2mRkjoEkr00e5wQTXCcOhfsXG/udQaEU1SUhaCyV +HppmxDB4i3aHhiGKztk6JU/SE9o4B//BbdLfmv741lwrE/5Lgx2YSBnATqDWC7rI +W2Tj33Sf06y7MKgkG5TszkM2cGdYhowhsyhhpww50gKfoRBNTp935jLo3nytShiM +NeQpf7/Wjcd1yIRYbWefTDJDSwGnzBoPCNHIEhAT15RUV2jGe9ctSMU2zQWInDll +U8dkWRZp9cZpQCvx2HkMy7oqsigoHxSSnsMzc8gtJHdhovjoLAVu9y5mAtEjHnTd +2ud1woYVo5dDoQEaFMp1Ll4qotLhMRVDl3SBPJoKOrEQfS/4JwITzuS8C7RSlmxE +UR2gPw7R39ocTE/rigUnE4WHf4q18kWrkRRZoMsvitv9FSyMkN1yaL0IintkRXzg +ZkSZbzxVriE1dZ5u+Ie1zNaa5rB+yb/nzRC9HMbBtZbVgHe1ngr+pEyAMWFd4U8N +HRQ= +-----END CERTIFICATE----- diff --git a/tests/certs/client.csr b/tests/certs/client.csr new file mode 100644 index 0000000000..619f572efa --- /dev/null +++ b/tests/certs/client.csr @@ -0,0 +1,60 @@ +Certificate Request: + Data: + Version: 1 (0x0) + Subject: CN = postgres + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:bf:4f:18:ca:d8:ff:a3:93:aa:9a:3b:90:35:c7: + ff:82:65:d1:d0:e8:65:9d:9c:6c:cb:70:4e:31:7e: + 7e:52:ce:2d:85:7a:83:ee:b8:eb:f1:ba:37:0e:34: + 66:3d:b6:db:cb:45:6f:64:0f:5c:4d:ba:53:25:c9: + ff:e0:a1:39:9b:82:c9:c0:08:e8:17:6b:01:6a:99: + 47:05:d8:c5:2f:83:f3:33:f7:ad:bb:f3:dd:5f:6a: + 95:4f:d9:8e:1d:bc:ff:84:78:77:eb:98:40:36:2d: + 9a:a3:29:a6:ba:58:90:c1:92:88:5f:07:c3:a8:a6: + 06:f0:ca:f8:81:40:13:65:1d:08:6c:97:9f:d4:b4: + 8d:f7:77:32:f6:2c:d4:9b:07:b3:86:3a:62:7f:da: + 3d:3c:e9:96:71:cc:62:2e:ac:6d:00:ca:ac:6c:a1: + b4:68:28:67:18:be:4b:31:e7:f1:c3:1d:a4:ad:05: + 50:59:44:30:09:b1:91:e1:86:5d:ec:75:06:a9:70: + 43:6b:81:5c:ff:98:fd:22:5c:3a:0e:08:2e:e3:b3: + c4:e0:65:dd:cd:e7:f2:69:08:0a:1b:90:c4:06:c1: + 06:ee:75:ee:d3:3c:ab:a2:9c:51:00:1c:56:fe:24: + 92:36:ee:e1:f3:6f:0c:14:79:32:07:f9:12:2b:26: + 79:c1 + Exponent: 65537 (0x10001) + Attributes: + a0:00 + Signature Algorithm: sha256WithRSAEncryption + b1:1f:11:89:d3:6a:a3:b3:fb:9e:9d:de:b4:cb:5c:44:0f:86: + 69:c7:c5:81:f8:cc:42:24:6d:92:1c:e8:85:bc:22:ba:49:6f: + d4:f0:89:21:6c:39:9d:29:31:a5:2a:21:81:76:58:1b:0a:1b: + fb:46:9a:59:fd:e3:c8:7b:54:25:ad:ca:86:0f:2b:e7:aa:79: + 92:d4:f5:c7:91:5d:f2:f8:ff:fe:d1:5f:0c:30:8a:1a:89:0d: + 3a:d1:1b:f2:a4:77:bd:fb:3b:5a:c9:6c:15:e5:54:f9:10:ba: + 58:6a:a2:ee:7e:32:dc:fa:ef:51:f8:52:63:67:6e:e8:fa:fc: + 21:79:46:fb:f2:6d:16:34:6c:79:96:ae:1c:8b:2c:1b:c5:ab: + b7:ac:ad:14:25:55:de:41:76:a1:47:34:0e:b4:c7:48:b1:73: + e6:74:ed:17:5f:d9:f2:d0:ec:6a:6a:97:bd:7c:81:b9:22:09: + 14:d0:e0:5e:b8:14:70:f3:3d:b1:aa:2e:43:c8:10:7d:00:85: + 90:9c:80:9f:3d:03:c3:6c:df:f3:da:50:19:e7:5e:a0:0e:17: + f9:5c:ed:83:35:38:c2:9a:5b:ea:ea:ec:8b:27:1d:51:38:8b: + 94:eb:d0:69:4a:87:dd:52:49:dc:75:86:ce:5e:ee:ec:33:ff: + 8d:0c:30:40 +-----BEGIN CERTIFICATE REQUEST----- +MIICWDCCAUACAQAwEzERMA8GA1UEAwwIcG9zdGdyZXMwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQC/TxjK2P+jk6qaO5A1x/+CZdHQ6GWdnGzLcE4xfn5S +zi2FeoPuuOvxujcONGY9ttvLRW9kD1xNulMlyf/goTmbgsnACOgXawFqmUcF2MUv +g/Mz9627891fapVP2Y4dvP+EeHfrmEA2LZqjKaa6WJDBkohfB8OopgbwyviBQBNl +HQhsl5/UtI33dzL2LNSbB7OGOmJ/2j086ZZxzGIurG0AyqxsobRoKGcYvksx5/HD +HaStBVBZRDAJsZHhhl3sdQapcENrgVz/mP0iXDoOCC7js8TgZd3N5/JpCAobkMQG +wQbude7TPKuinFEAHFb+JJI27uHzbwwUeTIH+RIrJnnBAgMBAAGgADANBgkqhkiG +9w0BAQsFAAOCAQEAsR8RidNqo7P7np3etMtcRA+GacfFgfjMQiRtkhzohbwiuklv +1PCJIWw5nSkxpSohgXZYGwob+0aaWf3jyHtUJa3Khg8r56p5ktT1x5Fd8vj//tFf +DDCKGokNOtEb8qR3vfs7WslsFeVU+RC6WGqi7n4y3PrvUfhSY2du6Pr8IXlG+/Jt +FjRseZauHIssG8Wrt6ytFCVV3kF2oUc0DrTHSLFz5nTtF1/Z8tDsamqXvXyBuSIJ +FNDgXrgUcPM9saouQ8gQfQCFkJyAnz0Dw2zf89pQGedeoA4X+VztgzU4wppb6urs +iycdUTiLlOvQaUqH3VJJ3HWGzl7u7DP/jQwwQA== +-----END CERTIFICATE REQUEST----- diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml index 030d1bacff..f521efb3a4 100644 --- a/tests/docker-compose.yml +++ b/tests/docker-compose.yml @@ -19,6 +19,21 @@ services: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: sqlx + mysql_8_client_ssl: + build: + context: . + dockerfile: mysql/Dockerfile + args: + IMAGE: mysql:8.0.27 + volumes: + - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql" + ports: + - 3306 + environment: + MYSQL_ROOT_HOST: '%' + MYSQL_DATABASE: sqlx + MYSQL_ALLOW_EMPTY_PASSWORD: 1 + mysql_5_7: image: mysql:5.7 volumes: @@ -29,6 +44,22 @@ services: MYSQL_ROOT_HOST: '%' MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: sqlx + + mysql_5_7_client_ssl: + build: + context: . + dockerfile: mysql/Dockerfile + args: + IMAGE: mysql:5.7 + volumes: + - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql" + ports: + - 3306 + environment: + MYSQL_ROOT_HOST: '%' + MYSQL_DATABASE: sqlx + MYSQL_ALLOW_EMPTY_PASSWORD: 1 + # # MariaDB 10.6, 10.5, 10.4, 10.3 # https://mariadb.org/about/#maintenance-policy @@ -44,6 +75,20 @@ services: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: sqlx + mariadb_10_6_client_ssl: + build: + context: . + dockerfile: mysql/Dockerfile + args: + IMAGE: mariadb:10.6 + volumes: + - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql" + ports: + - 3306 + environment: + MARIADB_DATABASE: sqlx + MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1 + mariadb_10_5: image: mariadb:10.5 volumes: @@ -54,6 +99,20 @@ services: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: sqlx + mariadb_10_5_client_ssl: + build: + context: . + dockerfile: mysql/Dockerfile + args: + IMAGE: mariadb:10.5 + volumes: + - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql" + ports: + - 3306 + environment: + MARIADB_DATABASE: sqlx + MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1 + mariadb_10_4: image: mariadb:10.4 volumes: @@ -64,6 +123,20 @@ services: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: sqlx + mariadb_10_4_client_ssl: + build: + context: . + dockerfile: mysql/Dockerfile + args: + IMAGE: mariadb:10.4 + volumes: + - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql" + ports: + - 3306 + environment: + MARIADB_DATABASE: sqlx + MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1 + mariadb_10_3: image: mariadb:10.3 volumes: @@ -74,6 +147,20 @@ services: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: sqlx + mariadb_10_3_client_ssl: + build: + context: . + dockerfile: mysql/Dockerfile + args: + IMAGE: mariadb:10.3 + volumes: + - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql" + ports: + - 3306 + environment: + MARIADB_DATABASE: sqlx + MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1 + # # PostgreSQL 15.x, 14.x, 13.x, 12.x, 11.x # https://www.postgresql.org/support/versioning/ @@ -84,7 +171,7 @@ services: context: . dockerfile: postgres/Dockerfile args: - VERSION: 15 + VERSION: 15 ports: - 5432 environment: @@ -98,12 +185,29 @@ services: command: > -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key + postgres_15_client_ssl: + build: + context: . + dockerfile: postgres/Dockerfile + args: + VERSION: 15 + ports: + - 5432 + environment: + POSTGRES_DB: sqlx + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_INITDB_ARGS: --auth-host=trust + volumes: + - "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql" + command: > + -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key -c ssl_ca_file=/var/lib/postgresql/ca.crt -c hba_file=/var/lib/postgresql/pg_hba.conf + postgres_14: build: context: . dockerfile: postgres/Dockerfile args: - VERSION: 14 + VERSION: 14 ports: - 5432 environment: @@ -117,12 +221,29 @@ services: command: > -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key + postgres_14_client_ssl: + build: + context: . + dockerfile: postgres/Dockerfile + args: + VERSION: 14 + ports: + - 5432 + environment: + POSTGRES_DB: sqlx + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_INITDB_ARGS: --auth-host=trust + volumes: + - "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql" + command: > + -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key -c ssl_ca_file=/var/lib/postgresql/ca.crt -c hba_file=/var/lib/postgresql/pg_hba.conf + postgres_13: build: context: . dockerfile: postgres/Dockerfile args: - VERSION: 13 + VERSION: 13 ports: - 5432 environment: @@ -136,12 +257,29 @@ services: command: > -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key + postgres_13_client_ssl: + build: + context: . + dockerfile: postgres/Dockerfile + args: + VERSION: 13 + ports: + - 5432 + environment: + POSTGRES_DB: sqlx + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_INITDB_ARGS: --auth-host=trust + volumes: + - "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql" + command: > + -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key -c ssl_ca_file=/var/lib/postgresql/ca.crt -c hba_file=/var/lib/postgresql/pg_hba.conf + postgres_12: build: context: . dockerfile: postgres/Dockerfile args: - VERSION: 12 + VERSION: 12 ports: - 5432 environment: @@ -155,6 +293,23 @@ services: command: > -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key + postgres_12_client_ssl: + build: + context: . + dockerfile: postgres/Dockerfile + args: + VERSION: 12.3 + ports: + - 5432 + environment: + POSTGRES_DB: sqlx + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_INITDB_ARGS: --auth-host=trust + volumes: + - "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql" + command: > + -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key -c ssl_ca_file=/var/lib/postgresql/ca.crt -c hba_file=/var/lib/postgresql/pg_hba.conf + postgres_11: build: context: . @@ -162,7 +317,7 @@ services: args: VERSION: 11 ports: - - 5432 + - 5432 environment: POSTGRES_DB: sqlx POSTGRES_USER: postgres @@ -173,3 +328,20 @@ services: - "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql" command: > -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key + + postgres_11_client_ssl: + build: + context: . + dockerfile: postgres/Dockerfile + args: + VERSION: 11 + ports: + - 5432 + environment: + POSTGRES_DB: sqlx + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_INITDB_ARGS: --auth-host=trust + volumes: + - "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql" + command: > + -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key -c ssl_ca_file=/var/lib/postgresql/ca.crt -c hba_file=/var/lib/postgresql/pg_hba.conf diff --git a/tests/docker.py b/tests/docker.py index e664c38c6e..427ed98a44 100644 --- a/tests/docker.py +++ b/tests/docker.py @@ -36,9 +36,6 @@ def start_database(driver, database, cwd): elif driver.startswith("postgres"): port = 5432 - elif driver.startswith("mssql"): - port = 1433 - else: raise NotImplementedError @@ -56,15 +53,29 @@ def start_database(driver, database, cwd): port = int(res.stdout[1:-2].decode()) + # need additional permissions to connect to MySQL when using SSL + res = subprocess.run( + ["docker", "exec", f"sqlx_{driver}_1", "mysql", "-u", "root", "-e", "GRANT ALL PRIVILEGES ON *.* TO 'root' WITH GRANT OPTION;"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=dir_tests, + ) + + if res.returncode != 0: + print(res.stderr, file=sys.stderr) + + # do not set password in URL if authenticating using SSL key file + if driver.endswith("client_ssl"): + password = "" + else: + password = ":password" + # construct appropriate database URL if driver.startswith("mysql") or driver.startswith("mariadb"): - return f"mysql://root:password@127.0.0.1:{port}/{database}" + return f"mysql://root{password}@localhost:{port}/{database}" elif driver.startswith("postgres"): - return f"postgres://postgres:password@localhost:{port}/{database}" - - elif driver.startswith("mssql"): - return f"mssql://sa:Password123!@127.0.0.1:{port}/{database}" + return f"postgres://postgres{password}@localhost:{port}/{database}" else: raise NotImplementedError diff --git a/tests/keys/client.key b/tests/keys/client.key new file mode 100644 index 0000000000..c5a237241e --- /dev/null +++ b/tests/keys/client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC/TxjK2P+jk6qa +O5A1x/+CZdHQ6GWdnGzLcE4xfn5Szi2FeoPuuOvxujcONGY9ttvLRW9kD1xNulMl +yf/goTmbgsnACOgXawFqmUcF2MUvg/Mz9627891fapVP2Y4dvP+EeHfrmEA2LZqj +Kaa6WJDBkohfB8OopgbwyviBQBNlHQhsl5/UtI33dzL2LNSbB7OGOmJ/2j086ZZx +zGIurG0AyqxsobRoKGcYvksx5/HDHaStBVBZRDAJsZHhhl3sdQapcENrgVz/mP0i +XDoOCC7js8TgZd3N5/JpCAobkMQGwQbude7TPKuinFEAHFb+JJI27uHzbwwUeTIH ++RIrJnnBAgMBAAECggEAKNCZO328XIu+lBUtGSxIKOvMLcPHGi8rTuPw6sJP9R6j +u5x91UqCnBnccR1gyr3eeqmfsDtOuA6Oertz6dq7zZ/Dp0K/MW/U54c4DdlHiHGg +S3AGEtleW2MD4/tIRLPz17FT9GGRIX3tRe428f6/M20txwiDB9IUHP9QsVKYULPX +pzX+BMMINj70U1CcwcsIkPH9znDhwdMfjphC/eJUgITDle9EynYRhBHz55ajTTeE +hPsttRPYvbXdxd1WdSnt/Xv4+N10RKcEnrPE17WrbUs9RvqOz7hW4e4QifsBf5dR +0Sw1AemmdOK5xTrA0K9D7gRv6qC8QHuTDjIntVd8gQKBgQD+PyQUNJpvUre1zK1A +HBTVbX7uIqYrX6FWXFFE55HtcnrhEWIY5QCBPfOFsVdcvBJrqclkInpELjdnVbeP +25ETIKhhiP3FnJjJlNZiFXD85NmHbRJzABvNxspb+9UOuIJfB2ixSGmnEKEQIeJf +QmUzz/PJ9+2ct8/rXobZ90Is6QKBgQDAoNe/bUGc/ZmKq132hCcBeHFcjdtW6fkE +6d8giLx90b1kQzYJaM+4jhYF4s1job32ZPlykAlGUCtWBGirhonioJVgiGy4fOc0 +SlIcKg2Gh68FDdKGHcBN5duk0nCc+uLT1fNPqo98jy1DI6VCVej0xWBrFkMFXZ3S +qJ5PWtT/GQKBgFLBkqjRBoO91PZkDPCVM2LVJT+2H4h2tDk8C2f2SFWVsdGYqumX +gLaQx7d4pgsVXJmWxmrFni6bLIWCLSGyQmKLesNkp9Wuxzy2KaH7gK+Qfg3KvvqX +ynUMg8m1CwCjpivwaW9rNpienQ53OQvwvKhExAG1pa4hVpgySIqiJPQhAoGAeFUB +8cdisZuKiyG6NQEhDL4csuC7IHRQ50zh4gUJGuAnG7cQzpf3CydXgp3ICHFFpeI2 +IebwpEf4imd+q4gEItqF9iPDJwx/sh6rZISwplWkc9fKp5V2SDNLHo+HYckoYYTJ +1f6KXBllAQgHeIUKXb3fGYZyn6t3p91F5/SqEiECgYBzjAYDWIRi7IpMkckts2ZQ +p7YXZCbUP4MALTLuWulrI5IFv7gOjW20US/CArNMc4wPv5WtY1uE59pd8Td2CW9X +BX1nQXqaVlF6xLOzgqsWPxloRk7y692J9nYMKcB6VxlkFVUQfbRZksFCsn2I4Y5Z +ZtG/bPbIR6NgZ6ntNa+KIg== +-----END PRIVATE KEY----- diff --git a/tests/mysql/Dockerfile b/tests/mysql/Dockerfile new file mode 100644 index 0000000000..fab69139ca --- /dev/null +++ b/tests/mysql/Dockerfile @@ -0,0 +1,15 @@ +ARG IMAGE +FROM ${IMAGE} + +# Copy SSL certificate (and key) +COPY certs/server.crt /etc/mysql/ssl/server.crt +COPY certs/ca.crt /etc/mysql/ssl/ca.crt +COPY keys/server.key /etc/mysql/ssl/server.key +COPY mysql/my.cnf /etc/mysql/my.cnf + +# Fix permissions +RUN chown mysql:mysql /etc/mysql/ssl/server.crt /etc/mysql/ssl/server.key +RUN chmod 0600 /etc/mysql/ssl/server.crt /etc/mysql/ssl/server.key + +# Create dir for secure-file-priv +RUN mkdir -p /var/lib/mysql-files diff --git a/tests/mysql/my.cnf b/tests/mysql/my.cnf new file mode 100644 index 0000000000..0e0f6c4125 --- /dev/null +++ b/tests/mysql/my.cnf @@ -0,0 +1,4 @@ +[mysqld] +ssl-ca=/etc/mysql/ssl/ca.crt +ssl-cert=/etc/mysql/ssl/server.crt +ssl-key=/etc/mysql/ssl/server.key diff --git a/tests/postgres/Dockerfile b/tests/postgres/Dockerfile index 184f062457..026adfac77 100644 --- a/tests/postgres/Dockerfile +++ b/tests/postgres/Dockerfile @@ -3,7 +3,9 @@ FROM postgres:${VERSION}-alpine # Copy SSL certificate (and key) COPY certs/server.crt /var/lib/postgresql/server.crt +COPY certs/ca.crt /var/lib/postgresql/ca.crt COPY keys/server.key /var/lib/postgresql/server.key +COPY postgres/pg_hba.conf /var/lib/postgresql/pg_hba.conf # Fix permissions RUN chown 70:70 /var/lib/postgresql/server.crt /var/lib/postgresql/server.key diff --git a/tests/postgres/pg_hba.conf b/tests/postgres/pg_hba.conf new file mode 100644 index 0000000000..be33c4bb10 --- /dev/null +++ b/tests/postgres/pg_hba.conf @@ -0,0 +1,4 @@ +# only needed for certificate authentication tests +# omit host to prevent fallback to non certificate authentication +local all all trust +hostssl all all all cert diff --git a/tests/sqlite/sqlite.db b/tests/sqlite/sqlite.db index 6832b724b7..e357615025 100644 Binary files a/tests/sqlite/sqlite.db and b/tests/sqlite/sqlite.db differ diff --git a/tests/x.py b/tests/x.py index e5cca2971f..a487d3986a 100755 --- a/tests/x.py +++ b/tests/x.py @@ -142,7 +142,7 @@ def run(command, comment=None, env=None, service=None, tag=None, args=None, data for runtime in ["async-std", "tokio"]: for tls in ["native-tls", "rustls", "none"]: run( - f"cargo test --no-default-features --manifest-path sqlx-core/Cargo.toml --features json,offline,migrate,_rt-{runtime},_tls-{tls}", + f"cargo test --no-default-features --manifest-path sqlx-core/Cargo.toml --features json,offline,migrate,_rt-{runtime},_tls-{tls}", comment="unit test core", tag=f"unit_{runtime}_{tls}" ) @@ -159,7 +159,7 @@ def run(command, comment=None, env=None, service=None, tag=None, args=None, data # run( - f"cargo test --no-default-features --features macros,any,_unstable-all-types,sqlite,runtime-{runtime},tls-{tls}", + f"cargo test --no-default-features --features any,sqlite,macros,_unstable-all-types,runtime-{runtime},tls-{tls}", comment=f"test sqlite", service="sqlite", tag=f"sqlite" if runtime == "async-std" else f"sqlite_{runtime}", @@ -171,21 +171,30 @@ def run(command, comment=None, env=None, service=None, tag=None, args=None, data for version in ["15", "14", "13", "12", "11"]: run( - f"cargo test --no-default-features --features macros,any,unstable-all-types,postgres,runtime-{runtime},tls-{tls}", + f"cargo test --no-default-features --features any,postgres,macros,_unstable-all-types,runtime-{runtime},tls-{tls}", comment=f"test postgres {version}", service=f"postgres_{version}", tag=f"postgres_{version}" if runtime == "async-std" else f"postgres_{version}_{runtime}", ) - ## +ssl - for version in ["15", "14", "13", "12", "11"]: - run( - f"cargo test --no-default-features --features macros,any,_unstable-all-types,postgres,runtime-{runtime},tls-{tls}", - comment=f"test postgres {version} ssl", - database_url_args="sslmode=verify-ca&sslrootcert=.%2Ftests%2Fcerts%2Fca.crt", - service=f"postgres_{version}", - tag=f"postgres_{version}_ssl" if runtime == "async-std" else f"postgres_{version}_ssl_{runtime}", - ) + if tls != "none": + ## +ssl + run( + f"cargo test --no-default-features --features any,postgres,macros,_unstable-all-types,runtime-{runtime},tls-{tls}", + comment=f"test postgres {version} ssl", + database_url_args="sslmode=verify-ca&sslrootcert=.%2Ftests%2Fcerts%2Fca.crt", + service=f"postgres_{version}", + tag=f"postgres_{version}_ssl" if runtime == "async-std" else f"postgres_{version}_ssl_{runtime}", + ) + + ## +client-ssl + run( + f"cargo test --no-default-features --features any,postgres,macros,_unstable-all-types,runtime-{runtime},tls-{tls}", + comment=f"test postgres {version}_client_ssl no-password", + database_url_args="sslmode=verify-ca&sslrootcert=.%2Ftests%2Fcerts%2Fca.crt&sslkey=.%2Ftests%2Fkeys%2Fclient.key&sslcert=.%2Ftests%2Fcerts%2Fclient.crt", + service=f"postgres_{version}_client_ssl", + tag=f"postgres_{version}_client_ssl_no_password" if runtime == "async-std" else f"postgres_{version}_client_ssl_no_password_{runtime}", + ) # # mysql @@ -193,23 +202,43 @@ def run(command, comment=None, env=None, service=None, tag=None, args=None, data for version in ["8", "5_7"]: run( - f"cargo test --no-default-features --features macros,any,_unstable-all-types,mysql,runtime-{runtime},tls-{tls}", + f"cargo test --no-default-features --features any,mysql,macros,_unstable-all-types,runtime-{runtime},tls-{tls}", comment=f"test mysql {version}", service=f"mysql_{version}", tag=f"mysql_{version}" if runtime == "async-std" else f"mysql_{version}_{runtime}", ) + ## +client-ssl + if tls != "none" and not(version == "5_7" and tls == "rustls"): + run( + f"cargo test --no-default-features --features any,mysql,macros,_unstable-all-types,runtime-{runtime},tls-{tls}", + comment=f"test mysql {version}_client_ssl no-password", + database_url_args="sslmode=verify_ca&ssl-ca=.%2Ftests%2Fcerts%2Fca.crt&ssl-key=.%2Ftests%2Fkeys%2Fclient.key&ssl-cert=.%2Ftests%2Fcerts%2Fclient.crt", + service=f"mysql_{version}_client_ssl", + tag=f"mysql_{version}_client_ssl_no_password" if runtime == "async-std" else f"mysql_{version}_client_ssl_no_password_{runtime}", + ) + # # mariadb # - for version in ["10_6", "10_5", "10_4", "10_3", "10_2"]: + for version in ["10_6", "10_5", "10_4", "10_3"]: run( - f"cargo test --no-default-features --features macros,any,_unstable-all-types,mysql,runtime-{runtime},tls-{tls}", + f"cargo test --no-default-features --features any,mysql,macros,_unstable-all-types,runtime-{runtime},tls-{tls}", comment=f"test mariadb {version}", service=f"mariadb_{version}", tag=f"mariadb_{version}" if runtime == "async-std" else f"mariadb_{version}_{runtime}", ) + ## +client-ssl + if tls != "none": + run( + f"cargo test --no-default-features --features any,mysql,macros,_unstable-all-types,runtime-{runtime},tls-{tls}", + comment=f"test mariadb {version}_client_ssl no-password", + database_url_args="sslmode=verify_ca&ssl-ca=.%2Ftests%2Fcerts%2Fca.crt&ssl-key=.%2Ftests%2Fkeys%2Fclient.key&ssl-cert=.%2Ftests%2Fcerts%2Fclient.crt", + service=f"mariadb_{version}_client_ssl", + tag=f"mariadb_{version}_client_ssl_no_password" if runtime == "async-std" else f"mariadb_{version}_client_ssl_no_password_{runtime}", + ) + # TODO: Use [grcov] if available # ~/.cargo/bin/grcov tests/.cache/target/debug -s sqlx-core/ -t html --llvm --branch -o ./target/debug/coverage