From 622fe9639611c623bfaf91106881c75a3ad0b00b Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 29 Sep 2023 22:14:27 -0400 Subject: [PATCH] wip --- .github/workflows/ci.yml | 18 ++- Cargo.lock | 240 +++++++++++++++++++++++-------------- Cargo.toml | 6 +- Dockerfile | 4 +- arm64.Dockerfile | 3 +- docs/src/development.md | 4 +- docs/src/installation.md | 2 - homebrew-formula/martin.rb | 2 - martin/Cargo.toml | 11 +- martin/src/args/pg.rs | 13 -- martin/src/pg/config.rs | 3 - martin/src/pg/errors.rs | 30 +++-- martin/src/pg/pool.rs | 24 +++- martin/src/pg/tls.rs | 124 ++++++++++++------- tests/test.sh | 4 +- 15 files changed, 295 insertions(+), 193 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7a8c6d94..aaee8a87a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,6 +52,7 @@ jobs: uses: actions/checkout@v4 - name: Rust Versions run: rustc --version && cargo --version + - uses: Swatinem/rust-cache@v2 - name: Lint (Linux) if: matrix.target == 'x86_64-unknown-linux-gnu' run: | @@ -61,18 +62,11 @@ jobs: cargo clippy --package martin-mbtiles --no-default-features -- -D warnings cargo clippy --package martin-mbtiles -- -D warnings cargo clippy --package martin -- -D warnings - cargo clippy --package martin --features vendored-openssl -- -D warnings cargo clippy --package martin --features bless-tests -- -D warnings - - name: Install OpenSSL (Windows) - if: runner.os == 'Windows' - shell: powershell - run: | - echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append - vcpkg install openssl:x64-windows-static-md - name: Build (native) if: matrix.cross != 'true' run: | - cargo build --release --target ${{ matrix.target }} --package martin --features=ssl --package martin + cargo build --release --target ${{ matrix.target }} --package martin cargo build --release --target ${{ matrix.target }} --package martin-mbtiles - name: Build (cross - aarch64-apple-darwin) if: matrix.target == 'aarch64-apple-darwin' @@ -80,7 +74,7 @@ jobs: rustup target add "${{ matrix.target }}" # compile without debug symbols because stripping them with `strip` does not work cross-platform export RUSTFLAGS='-C link-arg=-s' - cargo build --release --target ${{ matrix.target }} --package martin --features=vendored-openssl + cargo build --release --target ${{ matrix.target }} --package martin cargo build --release --target ${{ matrix.target }} --package martin-mbtiles - name: Build (cross - aarch64-unknown-linux-gnu) if: matrix.target == 'aarch64-unknown-linux-gnu' @@ -89,7 +83,7 @@ jobs: rustup target add "${{ matrix.target }}" # compile without debug symbols because stripping them with `strip` does not work cross-platform export RUSTFLAGS='-C link-arg=-s -C linker=aarch64-linux-gnu-gcc' - cargo build --release --target ${{ matrix.target }} --package martin --features=vendored-openssl + cargo build --release --target ${{ matrix.target }} --package martin cargo build --release --target ${{ matrix.target }} --package martin-mbtiles - name: Build (debian package) if: matrix.target == 'debian-x86_64' @@ -130,6 +124,7 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + - uses: Swatinem/rust-cache@v2 - name: Start postgres uses: nyurik/action-setup-postgis@v1 id: pg @@ -154,7 +149,6 @@ jobs: cargo test --package martin-mbtiles --no-default-features cargo test --package martin-mbtiles cargo test --package martin - cargo test --package martin --features vendored-openssl cargo test --doc RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --workspace cargo clean @@ -255,6 +249,7 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + - uses: Swatinem/rust-cache@v2 - name: Setup database run: tests/fixtures/initdb.sh env: @@ -345,6 +340,7 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + - uses: Swatinem/rust-cache@v2 - name: Setup database run: tests/fixtures/initdb.sh env: diff --git a/Cargo.lock b/Cargo.lock index a2373b4b9..77103cd50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -687,6 +687,22 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + [[package]] name = "cpufeatures" version = "0.2.9" @@ -1147,21 +1163,6 @@ dependencies = [ "ttf-parser 0.19.2", ] -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.0" @@ -1758,13 +1759,14 @@ dependencies = [ "martin-mbtiles", "martin-tile-utils", "num_cpus", - "openssl", "pmtiles", "postgis", "postgres", - "postgres-openssl", "postgres-protocol", "regex", + "rustls", + "rustls-native-certs", + "rustls-pemfile", "semver", "serde", "serde_json", @@ -1774,6 +1776,7 @@ dependencies = [ "thiserror", "tilejson", "tokio", + "tokio-postgres-rustls", ] [[package]] @@ -1988,52 +1991,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] -name = "openssl" -version = "0.10.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" -dependencies = [ - "bitflags 2.4.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.37", -] - -[[package]] -name = "openssl-src" -version = "300.1.5+3.1.3" +name = "openssl-probe" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "559068e4c12950d7dcaa1857a61725c0d38d4fc03ff8e070ab31a75d6e316491" -dependencies = [ - "cc", -] - -[[package]] -name = "openssl-sys" -version = "0.9.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" -dependencies = [ - "cc", - "libc", - "openssl-src", - "pkg-config", - "vcpkg", -] +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "oxipng" @@ -2275,19 +2236,6 @@ dependencies = [ "tokio-postgres", ] -[[package]] -name = "postgres-openssl" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de0ea6504e07ca78355a6fb88ad0f36cafe9e696cbc6717f16a207f3a60be72" -dependencies = [ - "futures", - "openssl", - "tokio", - "tokio-openssl", - "tokio-postgres", -] - [[package]] name = "postgres-protocol" version = "0.6.6" @@ -2499,6 +2447,21 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "roxmltree" version = "0.18.0" @@ -2578,6 +2541,49 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rustls" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustybuzz" version = "0.7.0" @@ -2609,12 +2615,54 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.19" @@ -3340,18 +3388,6 @@ dependencies = [ "syn 2.0.37", ] -[[package]] -name = "tokio-openssl" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08f9ffb7809f1b20c1b398d92acf4cc719874b3b2b2d9ea2f09b4a80350878a" -dependencies = [ - "futures-util", - "openssl", - "openssl-sys", - "tokio", -] - [[package]] name = "tokio-postgres" version = "0.7.10" @@ -3378,6 +3414,30 @@ dependencies = [ "whoami", ] +[[package]] +name = "tokio-postgres-rustls" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5831152cb0d3f79ef5523b357319ba154795d64c7078b2daa95a803b54057f" +dependencies = [ + "futures", + "ring", + "rustls", + "tokio", + "tokio-postgres", + "tokio-rustls", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.14" @@ -3544,6 +3604,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" version = "2.4.1" diff --git a/Cargo.toml b/Cargo.toml index 9cc4e70e8..9893e655d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,13 +34,14 @@ log = "0.4" martin-mbtiles = { path = "./martin-mbtiles", version = "0.5.0", default-features = false } martin-tile-utils = { path = "./martin-tile-utils", version = "0.1.0" } num_cpus = "1" -openssl = "0.10" pmtiles = { version = "0.3", features = ["mmap-async-tokio", "tilejson"] } postgis = "0.9" postgres = { version = "0.19", features = ["with-time-0_3", "with-uuid-1", "with-serde_json-1"] } -postgres-openssl = "0.5" postgres-protocol = "0.6" regex = "1" +rustls = { version = "0.21", features = ["dangerous_configuration"] } +rustls-native-certs = "0.6" +rustls-pemfile = "1" semver = "1" serde = { version = "1", features = ["derive"] } serde_json = "1" @@ -52,6 +53,7 @@ subst = { version = "0.3", features = ["yaml"] } thiserror = "1" tilejson = "0.3" tokio = { version = "1.32.0", features = ["macros"] } +tokio-postgres-rustls = "0.10" [profile.dev.package.sqlx-macros] # See https://github.com/launchbadge/sqlx#compile-time-verification diff --git a/Dockerfile b/Dockerfile index 71ffdf5e8..035f84ab6 100755 --- a/Dockerfile +++ b/Dockerfile @@ -3,10 +3,10 @@ FROM rust:alpine as builder WORKDIR /usr/src/martin RUN apk update \ - && apk add --no-cache openssl-dev musl-dev perl build-base + && apk add --no-cache musl-dev perl build-base COPY . . -RUN CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse cargo build --release --features=vendored-openssl +RUN CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse cargo build --release FROM alpine:latest diff --git a/arm64.Dockerfile b/arm64.Dockerfile index 4912078d2..e15c42712 100644 --- a/arm64.Dockerfile +++ b/arm64.Dockerfile @@ -4,12 +4,11 @@ WORKDIR /usr/src/martin RUN apt-get update \ && apt-get install -y --no-install-recommends \ - libssl-dev \ perl \ && rm -rf /var/lib/apt/lists/* COPY . . -RUN CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse cargo build --release --features=vendored-openssl +RUN CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse cargo build --release FROM debian:bullseye-slim diff --git a/docs/src/development.md b/docs/src/development.md index adf969d09..86891fbed 100644 --- a/docs/src/development.md +++ b/docs/src/development.md @@ -20,11 +20,11 @@ Install [docker](https://docs.docker.com/get-docker/) and [docker-compose](https sudo apt install -y docker.io docker-compose ``` -Install a few libs and tools like [openssl](https://www.openssl.org/): +Install a few required libs and tools: ```shell, ignore # For Ubuntu-based distros -sudo apt install -y libssl-dev build-essential pkg-config jq file +sudo apt install -y build-essential pkg-config jq file ``` Install [Just](https://github.com/casey/just#readme) (improved makefile processor). Note that some Linux and Homebrew distros have outdated versions of Just, so you should install it from source: diff --git a/docs/src/installation.md b/docs/src/installation.md index f1bf68969..908d98925 100644 --- a/docs/src/installation.md +++ b/docs/src/installation.md @@ -25,8 +25,6 @@ cargo install martin martin --help ``` -If your PostgreSQL connection requires SSL, you would need to install OpenSSL and run `cargo install martin --features ssl`, or even install with `--features vendored-openssl` to [statically link OpenSSL](https://docs.rs/openssl/latest/openssl/#vendored) into the binary. - ## Homebrew If you are using macOS and [Homebrew](https://brew.sh/) you can install martin using Homebrew tap. diff --git a/homebrew-formula/martin.rb b/homebrew-formula/martin.rb index e89f08b46..031200cfd 100644 --- a/homebrew-formula/martin.rb +++ b/homebrew-formula/martin.rb @@ -10,8 +10,6 @@ class Martin < Formula sha256 "92f660b1bef3a54dc84e4794a5ba02a8817c25f21ce7000783749bbae9e50de1" version "#{current_version}" - depends_on "openssl@3" - def install bin.install "martin" end diff --git a/martin/Cargo.toml b/martin/Cargo.toml index 6eb70d11e..c3ea8cb73 100644 --- a/martin/Cargo.toml +++ b/martin/Cargo.toml @@ -24,7 +24,6 @@ revision = "" maintainer = "Yuri Astrakhan , Stepan Kuzmin , MapLibre contributors" maintainer-scripts = "../debian" depends = "$auto" -features = ["ssl"] assets = [ ["target/release/martin", "/usr/bin/martin", "755"], ["target/release/mbtiles", "/usr/bin/mbtiles", "755"], @@ -50,8 +49,6 @@ path = "src/bin/main.rs" [features] default = [] -vendored-openssl = ["ssl", "openssl?/vendored"] -ssl = ["dep:openssl", "dep:postgres-openssl"] bless-tests = [] [dependencies] @@ -78,6 +75,9 @@ postgis.workspace = true postgres-protocol.workspace = true postgres.workspace = true regex.workspace = true +rustls-native-certs.workspace = true +rustls-pemfile.workspace = true +rustls.workspace = true semver.workspace = true serde.workspace = true serde_json = { workspace = true, features = ["preserve_order"] } @@ -87,10 +87,7 @@ subst.workspace = true thiserror.workspace = true tilejson.workspace = true tokio = { workspace = true, features = ["io-std"] } - -# Optional dependencies for ssl support -openssl = { workspace = true, optional = true } -postgres-openssl = { workspace = true, optional = true } +tokio-postgres-rustls.workspace = true [dev-dependencies] cargo-husky.workspace = true diff --git a/martin/src/args/pg.rs b/martin/src/args/pg.rs index 88c9a1f86..f77c06716 100644 --- a/martin/src/args/pg.rs +++ b/martin/src/args/pg.rs @@ -13,7 +13,6 @@ pub struct PgArgs { #[arg(short = 'b', long)] pub disable_bounds: bool, /// Loads trusted root certificates from a file. The file should contain a sequence of PEM-formatted CA certificates. - #[cfg(feature = "ssl")] #[arg(long)] pub ca_root_file: Option, /// If a spatial PG table has SRID 0, then this default SRID will be used as a fallback. @@ -82,7 +81,6 @@ impl PgArgs { }); } - #[cfg(feature = "ssl")] if self.ca_root_file.is_some() { info!("Overriding root certificate file to {} on all Postgres connections because of a CLI parameter", self.ca_root_file.as_ref().unwrap().display()); @@ -145,13 +143,6 @@ impl PgArgs { }) } - #[cfg(not(feature = "ssl"))] - #[allow(clippy::unused_self)] - fn get_certs<'a>(&self, _env: &impl Env<'a>) -> PgSslCerts { - PgSslCerts {} - } - - #[cfg(feature = "ssl")] fn get_certs<'a>(&self, env: &impl Env<'a>) -> PgSslCerts { let mut result = PgSslCerts { ssl_cert: Self::parse_env_var(env, "PGSSLCERT", "ssl certificate"), @@ -172,7 +163,6 @@ impl PgArgs { result } - #[cfg(feature = "ssl")] fn parse_env_var<'a>( env: &impl Env<'a>, env_var: &str, @@ -194,7 +184,6 @@ fn is_postgresql_string(s: &str) -> bool { #[cfg(test)] mod tests { - #[cfg(feature = "ssl")] use std::path::PathBuf; use super::*; @@ -262,7 +251,6 @@ mod tests { Some(OneOrMany::One(PgConfig { connection_string: some("postgres://localhost:5432"), default_srid: Some(10), - #[cfg(feature = "ssl")] ssl_certificates: PgSslCerts { ssl_root_cert: Some(PathBuf::from("file")), ..Default::default() @@ -297,7 +285,6 @@ mod tests { Some(OneOrMany::One(PgConfig { connection_string: some("postgres://localhost:5432"), default_srid: Some(20), - #[cfg(feature = "ssl")] ssl_certificates: PgSslCerts { ssl_cert: Some(PathBuf::from("cert")), ssl_key: Some(PathBuf::from("key")), diff --git a/martin/src/pg/config.rs b/martin/src/pg/config.rs index 8d4207538..befc9fd6d 100644 --- a/martin/src/pg/config.rs +++ b/martin/src/pg/config.rs @@ -19,17 +19,14 @@ pub trait PgInfo { pub struct PgSslCerts { /// Same as PGSSLCERT /// https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-SSLCERT - #[cfg(feature = "ssl")] #[serde(skip_serializing_if = "Option::is_none")] pub ssl_cert: Option, /// Same as PGSSLKEY /// https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-SSLKEY - #[cfg(feature = "ssl")] #[serde(skip_serializing_if = "Option::is_none")] pub ssl_key: Option, /// Same as PGSSLROOTCERT /// https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-SSLROOTCERT - #[cfg(feature = "ssl")] #[serde(skip_serializing_if = "Option::is_none")] pub ssl_root_cert: Option, } diff --git a/martin/src/pg/errors.rs b/martin/src/pg/errors.rs index 62ddec726..c377e1a25 100644 --- a/martin/src/pg/errors.rs +++ b/martin/src/pg/errors.rs @@ -1,3 +1,6 @@ +use std::io; +use std::path::PathBuf; + use deadpool_postgres::tokio_postgres::Error as TokioPgError; use deadpool_postgres::{BuildError, PoolError}; use semver::Version; @@ -10,23 +13,24 @@ pub type Result = std::result::Result; #[derive(thiserror::Error, Debug)] pub enum PgError { - #[cfg(feature = "ssl")] - #[error("Can't build TLS connection: {0}")] - BuildSslConnectorError(#[from] openssl::error::ErrorStack), + #[error("Cannot load platform root certificates: {0}")] + CantLoadRoots(#[source] io::Error), + + #[error("Cannot open certificate file {}: {0}", .1.display())] + CantOpenCert(#[source] io::Error, PathBuf), + + #[error("Cannot parse certificate file {}: {0}", .1.display())] + CantParseCert(#[source] io::Error, PathBuf), - #[cfg(feature = "ssl")] - #[error("Can't set trusted root certificate {}: {0}", .1.display())] - BadTrustedRootCertError(#[source] openssl::error::ErrorStack, std::path::PathBuf), + #[error("Unable to parse PEM RSA key file {}", .0.display())] + InvalidPrivateKey(PathBuf), - #[cfg(feature = "ssl")] - #[error("Can't set client certificate {}: {0}", .1.display())] - BadClientCertError(#[source] openssl::error::ErrorStack, std::path::PathBuf), + #[error("Unable to use client certificate pair {} / {}: {0}", .1.display(), .2.display())] + CannotUseClientKey(#[source] rustls::Error, PathBuf, PathBuf), - #[cfg(feature = "ssl")] - #[error("Can't set client certificate key {}: {0}", .1.display())] - BadClientKeyError(#[source] openssl::error::ErrorStack, std::path::PathBuf), + #[error("Rustls Error: {0:?}")] + RustlsError(#[from] rustls::Error), - #[cfg(feature = "ssl")] #[error("Unknown SSL mode: {0:?}")] UnknownSslMode(deadpool_postgres::tokio_postgres::config::SslMode), diff --git a/martin/src/pg/pool.rs b/martin/src/pg/pool.rs index 15b26b9d9..14ba6fe65 100755 --- a/martin/src/pg/pool.rs +++ b/martin/src/pg/pool.rs @@ -1,5 +1,6 @@ use deadpool_postgres::{Manager, ManagerConfig, Object, Pool, RecyclingMethod}; use log::{info, warn}; +use postgres::config::SslMode; use semver::Version; use crate::pg::config::PgConfig; @@ -40,12 +41,31 @@ impl PgPool { ToString::to_string, ); - let connector = make_connector(&config.ssl_certificates, ssl_mode)?; + // let connector = make_connector(&config.ssl_certificates, ssl_mode)?; + // + // let mgr_config = ManagerConfig { + // recycling_method: RecyclingMethod::Fast, + // }; + // let mgr = Manager::from_config(pg_cfg, connector, mgr_config); let mgr_config = ManagerConfig { recycling_method: RecyclingMethod::Fast, }; - let mgr = Manager::from_config(pg_cfg, connector, mgr_config); + + let enable_ssl = pg_cfg.get_ssl_mode() != SslMode::Disable; + info!( + "Connecting {} SSL support to {conn_str:?}", + if enable_ssl { "with" } else { "without" } + ); + + let mgr = if enable_ssl { + let connector = make_connector(&config.ssl_certificates, ssl_mode)?; + Manager::from_config(pg_cfg, connector, mgr_config) + } else { + let connector = deadpool_postgres::tokio_postgres::NoTls {}; + Manager::from_config(pg_cfg, connector, mgr_config) + }; + let pool = Pool::builder(mgr) .max_size(config.pool_size.unwrap_or(POOL_SIZE_DEFAULT)) .build() diff --git a/martin/src/pg/tls.rs b/martin/src/pg/tls.rs index 06186fadd..9bbdd9d8b 100644 --- a/martin/src/pg/tls.rs +++ b/martin/src/pg/tls.rs @@ -1,20 +1,20 @@ +use std::fs::File; +use std::io::BufReader; +use std::path::PathBuf; use std::str::FromStr; use deadpool_postgres::tokio_postgres::config::SslMode; use deadpool_postgres::tokio_postgres::Config; -#[cfg(feature = "ssl")] use log::{info, warn}; -#[cfg(feature = "ssl")] -use openssl::ssl::SslFiletype; -#[cfg(feature = "ssl")] -use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; use regex::Regex; +use rustls::{Certificate, PrivateKey}; +use rustls_native_certs::load_native_certs; +use rustls_pemfile::Item::RSAKey; +use tokio_postgres_rustls::MakeRustlsConnect; -use crate::pg::PgError::BadConnectionString; -#[cfg(feature = "ssl")] use crate::pg::PgError::{ - BadClientCertError, BadClientKeyError, BadTrustedRootCertError, BuildSslConnectorError, - UnknownSslMode, + BadConnectionString, CannotUseClientKey, CantLoadRoots, CantOpenCert, CantParseCert, + InvalidPrivateKey, UnknownSslMode, }; use crate::pg::{PgSslCerts, Result}; @@ -51,21 +51,41 @@ pub fn parse_conn_str(conn_str: &str) -> Result<(Config, SslModeOverride)> { Ok((pg_cfg, mode)) } -#[cfg(not(feature = "ssl"))] -#[allow(clippy::unnecessary_wraps)] -pub fn make_connector( - _pg_certs: &PgSslCerts, - _ssl_mode: SslModeOverride, -) -> Result { - Ok(deadpool_postgres::tokio_postgres::NoTls) +struct NoCertificateVerification {} + +impl rustls::client::ServerCertVerifier for NoCertificateVerification { + fn verify_server_cert( + &self, + _end_entity: &Certificate, + _intermediates: &[Certificate], + _server_name: &rustls::ServerName, + _scts: &mut dyn Iterator, + _ocsp: &[u8], + _now: std::time::SystemTime, + ) -> std::result::Result { + Ok(rustls::client::ServerCertVerified::assertion()) + } +} + +fn read_certs(file: &PathBuf) -> Result> { + Ok(rustls_pemfile::certs(&mut cert_reader(file)?) + .map_err(|e| CantParseCert(e, file.clone()))? + .into_iter() + .map(Certificate) + .collect()) +} + +fn cert_reader(file: &PathBuf) -> Result> { + Ok(BufReader::new( + File::open(file).map_err(|e| CantOpenCert(e, file.clone()))?, + )) } -#[cfg(feature = "ssl")] pub fn make_connector( pg_certs: &PgSslCerts, ssl_mode: SslModeOverride, -) -> Result { - let (verify_ca, verify_hostname) = match ssl_mode { +) -> Result { + let (verify_ca, _verify_hostname) = match ssl_mode { SslModeOverride::Unmodified(mode) => match mode { SslMode::Disable | SslMode::Prefer => (false, false), SslMode::Require => match pg_certs.ssl_root_cert { @@ -84,40 +104,58 @@ pub fn make_connector( SslModeOverride::VerifyFull => (true, true), }; - let tls = SslMethod::tls_client(); - let mut builder = SslConnector::builder(tls).map_err(BuildSslConnectorError)?; - - if let (Some(cert), Some(key)) = (&pg_certs.ssl_cert, &pg_certs.ssl_key) { - builder - .set_certificate_file(cert, SslFiletype::PEM) - .map_err(|e| BadClientCertError(e, cert.clone()))?; - builder - .set_private_key_file(key, SslFiletype::PEM) - .map_err(|e| BadClientKeyError(e, key.clone()))?; - } else if pg_certs.ssl_key.is_some() || pg_certs.ssl_key.is_some() { - warn!("SSL client certificate and key files must be set to use client certificate with Postgres. Only one of them was set."); - } + let mut roots = rustls::RootCertStore::empty(); if let Some(file) = &pg_certs.ssl_root_cert { - builder - .set_ca_file(file) - .map_err(|e| BadTrustedRootCertError(e, file.clone()))?; + for cert in read_certs(file)? { + roots.add(&cert)?; + } info!("Using {} as a root certificate", file.display()); } - if !verify_ca { - builder.set_verify(SslVerifyMode::NONE); + if verify_ca || pg_certs.ssl_root_cert.is_some() || pg_certs.ssl_cert.is_some() { + let certs = load_native_certs().map_err(CantLoadRoots)?; + for cert in certs { + roots.add(&Certificate(cert.0))?; + } } - let mut connector = postgres_openssl::MakeTlsConnector::new(builder.build()); + let builder = rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(roots); + + let mut builder = if let (Some(cert), Some(key)) = (&pg_certs.ssl_cert, &pg_certs.ssl_key) { + match rustls_pemfile::read_one(&mut cert_reader(key)?) + .map_err(|e| CantParseCert(e, key.clone()))? + { + Some(RSAKey(rsa_key)) => builder + .with_client_auth_cert(read_certs(cert)?, PrivateKey(rsa_key)) + .map_err(|e| CannotUseClientKey(e, cert.clone(), key.clone()))?, + _ => Err(InvalidPrivateKey(key.clone()))?, + } + } else { + if pg_certs.ssl_key.is_some() || pg_certs.ssl_key.is_some() { + warn!("SSL client certificate and key files must be set to use client certificate with Postgres. Only one of them was set."); + } + builder.with_no_client_auth() + }; - if !verify_hostname { - connector.set_callback(|cfg, _domain| { - cfg.set_verify_hostname(false); - Ok(()) - }); + if !verify_ca { + builder + .dangerous() + .set_certificate_verifier(std::sync::Arc::new(NoCertificateVerification {})); } + let connector = MakeRustlsConnect::new(builder); + + // TODO: ??? + // if !verify_hostname { + // connector.set_callback(|cfg, _domain| { + // cfg.set_verify_hostname(false); + // Ok(()) + // }); + // } + Ok(connector) } diff --git a/tests/test.sh b/tests/test.sh index ab47e712a..ca08a7b86 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -4,11 +4,11 @@ set -euo pipefail # TODO: use --fail-with-body to get the response body on failure CURL=${CURL:-curl --silent --show-error --fail --compressed} DATABASE_URL="${DATABASE_URL:-postgres://postgres@localhost/db}" -MARTIN_BUILD="${MARTIN_BUILD:-cargo build --features ssl}" +MARTIN_BUILD="${MARTIN_BUILD:-cargo build}" MARTIN_PORT="${MARTIN_PORT:-3111}" MARTIN_URL="http://localhost:${MARTIN_PORT}" MARTIN_ARGS="${MARTIN_ARGS:---listen-addresses localhost:${MARTIN_PORT}}" -MARTIN_BIN="${MARTIN_BIN:-cargo run --features ssl --} ${MARTIN_ARGS}" +MARTIN_BIN="${MARTIN_BIN:-cargo run --} ${MARTIN_ARGS}" MBTILES_BUILD="${MBTILES_BUILD:-cargo build -p martin-mbtiles}" MBTILES_BIN="${MBTILES_BIN:-target/debug/mbtiles}"