Add client SSL authentication using key-file for Postgres, MySQL and MariaDB (#1850)

* use native-tls API

* Add client cert and key to MySQL connector

* Add client ssl tests for PostgreSQL

* Add client ssl tests for MariaDB and MySQL

* Adapt GA tests

* Fix RUSTFLAGS to run all tests

* Remove containers to free the DB port before running SSL auth tests

* Fix CI bad naming

* Use docker-compose down to remove also the network

* Fix main rebase

* Stop trying to stop service using docker-compose, simply use docker cmd

* Fix RUSTFLAGS for Postgres

* Name the Docker images for MariaDB and MySQL so we can stop them using their name

* Add the exception for mysql 5.7 not supporting compatible TLS version with RusTLS

* Rebase fixes

* Set correctly tls struct (fix merge)

* Handle Elliptic Curve variant for private key

* Fix tests suite

* Fix features in CI

* Add tests for Postgres 15 + rebase

* Python tests: fix exception for MySQL 5.7 + remove unneeded for loops

* CI: run SSL tests only when building with TLS support

---------

Co-authored-by: Barry Simons <linuxuser586@gmail.com>
This commit is contained in:
Thibs 2023-02-18 01:03:24 +01:00 committed by Austin Bonander
parent 1fd05716af
commit c4130d45e3
23 changed files with 641 additions and 43 deletions

View file

@ -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 }}

View file

@ -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<S, Ws>(

View file

@ -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<S: Socket> {
@ -53,6 +53,14 @@ pub async fn handshake<S: Socket>(
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)) {

View file

@ -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<S: Socket> {
@ -48,7 +48,7 @@ impl<S: Socket> Socket for RustlsSocket<S> {
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<u8>) -> Result<Vec<rustls::Certificate>, 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<u8>) -> Result<rustls::PrivateKey, Error> {
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 {

View file

@ -64,6 +64,8 @@ pub(super) async fn maybe_upgrade<S: Socket>(
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

View file

@ -61,6 +61,8 @@ pub struct MySqlConnectOptions {
pub(crate) database: Option<String>,
pub(crate) ssl_mode: MySqlSslMode,
pub(crate) ssl_ca: Option<CertificateInput>,
pub(crate) ssl_client_cert: Option<CertificateInput>,
pub(crate) ssl_client_key: Option<CertificateInput>,
pub(crate) statement_cache_capacity: usize,
pub(crate) charset: String,
pub(crate) collation: Option<String>,
@ -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<Path>) -> 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<Path>) -> 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

View file

@ -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)?);

View file

@ -58,6 +58,8 @@ async fn maybe_upgrade<S: Socket>(
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

View file

@ -87,6 +87,8 @@ pub struct PgConnectOptions {
pub(crate) database: Option<String>,
pub(crate) ssl_mode: PgSslMode,
pub(crate) ssl_root_cert: Option<CertificateInput>,
pub(crate) ssl_client_cert: Option<CertificateInput>,
pub(crate) ssl_client_key: Option<CertificateInput>,
pub(crate) statement_cache_capacity: usize,
pub(crate) application_name: Option<String>,
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<Path>) -> 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<Path>) -> Self {
self.ssl_client_key = Some(CertificateInput::File(key.as_ref().to_path_buf()));
self
}
/// Sets PEM encoded trusted SSL Certificate Authorities (CA).
///
/// # Example

View file

@ -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)?);

View file

@ -1,5 +1,7 @@
*
!certs/*
!keys/*
!mysql/my.cnf
!mssql/*.sh
!postgres/pg_hba.conf
!*/*.sql

1
tests/certs/ca.srl Normal file
View file

@ -0,0 +1 @@
B6C71F4B11C0A189

57
tests/certs/client.crt Normal file
View file

@ -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-----

60
tests/certs/client.csr Normal file
View file

@ -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-----

View file

@ -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

View file

@ -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

28
tests/keys/client.key Normal file
View file

@ -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-----

15
tests/mysql/Dockerfile Normal file
View file

@ -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

4
tests/mysql/my.cnf Normal file
View file

@ -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

View file

@ -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

View file

@ -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

Binary file not shown.

View file

@ -142,7 +142,7 @@ for runtime in ["async-std", "tokio"]:
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 @@ for runtime in ["async-std", "tokio"]:
#
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 @@ for runtime in ["async-std", "tokio"]:
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 @@ for runtime in ["async-std", "tokio"]:
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