Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.33] - 2026-05-13

### Fixed

- **HTTPS connector missing from Entra credential chain.** Every HTTPS
request issued by the Microsoft Entra credential chain — including
`WorkloadIdentityCredential` (AKS / federated identity),
`ManagedIdentityCredential` (when going through AAD over HTTPS), and
the `ClientAssertionCredential` token endpoint — failed at hyper's
connector layer with `"invalid URL, scheme is not http"` before any
network I/O occurred. As a result, `PostgresProvider::new_with_entra`
and `new_with_schema_and_entra` were unreachable in production
topologies that rely on Workload Identity (the developer-tools
branch via `az login` was unaffected, which is why CI did not catch
the regression). Root cause: `azure_core`'s `reqwest` feature flows
through `typespec_client_core/reqwest`, which declares the optional
reqwest dep with `default-features = false`. With no TLS feature
enabled on the resolved reqwest build, hyper rejected every HTTPS URL
at the connector layer. Fixed by declaring an explicit
`reqwest = { version = "0.13", default-features = false, features = ["native-tls"] }`
dep in `Cargo.toml`. Cargo feature unification activates the
native-tls (openssl-backed) connector on the resolved reqwest build
without modifying any source code, preserving the crate's FIPS-
compliance posture (no rustls / ring / aws-lc-rs in the resolved
graph). Added a regression test
(`tests/native_tls_regression.rs`) that constructs the
`azure_core::http` client used by `azure_identity` and asserts the
TLS connector accepts `https://` URLs.

## [0.1.32] - 2026-05-08

**Release:** <https://github.com/microsoft/duroxide-pg/releases/tag/v0.1.32>
Expand Down
28 changes: 22 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = [".", "pg-stress"]

[package]
name = "duroxide-pg"
version = "0.1.32"
version = "0.1.33"
edition = "2021"
authors = ["Affan Dar <affandar@gmail.com>"]
description = "A PostgreSQL-based provider implementation for Duroxide, a durable task orchestration framework"
Expand Down Expand Up @@ -51,16 +51,32 @@ include_dir = "0.7"
# their `reqwest_rustls` default feature, which would otherwise pull a
# rustls-based TLS stack into the dependency graph. We enable only the
# minimum needed for HTTP transport (`reqwest` + gzip/deflate) and `tokio`
# for the async runtime; this routes through reqwest's own `default-tls`
# feature, keeping the build on native-tls across all platforms.
# for the async runtime.
#
# Verified with `cargo tree --target x86_64-unknown-linux-gnu --all-features
# --all-targets` and a fresh Cargo.lock: no rustls-based TLS crates appear in
# the resolved dependency graph.
# The explicit `reqwest` dep below is required: `azure_core/reqwest` activates
# `typespec_client_core/reqwest`, which activates the optional reqwest dep
# with `default-features = false`. Without this line, the resolved reqwest
# build has NO TLS backend compiled in — every HTTPS request fails at the
# hyper connector with `"invalid URL, scheme is not http"`, which breaks
# `WorkloadIdentityCredential`, `ManagedIdentityCredential` (HTTPS-bound)
# and the `ClientAssertionCredential` token endpoint. Note that in
# reqwest 0.13.3 the `default-tls` feature resolves to rustls, NOT
# native-tls, so we cannot rely on enabling reqwest defaults to preserve
# the openssl posture; we must select `native-tls` explicitly. Cargo
# feature unification merges this dep with `typespec_client_core`'s reqwest
# dep at the same resolved version, activating the openssl-backed
# connector throughout the graph.
#
# Verified with `cargo tree --target x86_64-unknown-linux-gnu --edges normal`
# and a fresh Cargo.lock: no rustls / ring / aws-lc-rs / hyper-rustls
# crates appear in the resolved dependency graph; `hyper-tls` +
# `tokio-native-tls` + `native-tls` are activated.
azure_core = { version = "0.35", default-features = false, features = ["reqwest", "reqwest_deflate", "reqwest_gzip", "tokio"] }
azure_identity = { version = "0.35", default-features = false, features = ["tokio"] }
reqwest = { version = "0.13", default-features = false, features = ["native-tls"] }
# Used for FutureExt::catch_unwind in the Entra refresh task panic guard.
futures-util = { version = "0.3", default-features = false, features = ["std"] }

[dev-dependencies]
duroxide-pg-stress = { path = "pg-stress" }
url = "2"
50 changes: 50 additions & 0 deletions tests/native_tls_regression.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Regression test for the missing-TLS-backend bug in the resolved reqwest build.
//
// Background: `azure_core 0.35`'s `reqwest` feature activates
// `typespec_client_core/reqwest`, which activates the optional reqwest dep
// with `default-features = false`. Without an explicit reqwest feature
// declaration in this crate's Cargo.toml, the resolved reqwest is compiled
// with NO TLS backend, and every HTTPS request fails at hyper's connector
// with `"invalid URL, scheme is not http"` — before any network I/O.
//
// This breaks `WorkloadIdentityCredential`, `ManagedIdentityCredential`
// (HTTPS branch), and `ClientAssertionCredential` token endpoints — the
// entire Entra credential chain assembled in `src/entra.rs`.
//
// This test exercises the exact `azure_core::http::new_http_client()`
// constructor used by `azure_identity` against an `https://` URL. The URL
// targets a closed local port so the request must fail at TCP-connect,
// NOT at connector initialization. The regression contract: the error
// `Display` must NOT contain `"scheme is not http"` — that substring is
// hyper's signature for "no TLS connector compiled in".
//
// See `CHANGELOG.md` (0.1.33) and `Cargo.toml`'s comment block for the
// dep-graph trace and rationale.

use azure_core::http::{new_http_client, Method, Request};
use url::Url;

#[tokio::test(flavor = "current_thread")]
async fn entra_https_connector_is_compiled_in() {
let client = new_http_client(None);

// Closed local port — fail-fast at TCP connect, no DNS, no network egress.
let url = Url::parse("https://127.0.0.1:1/").expect("valid url");
let req = Request::new(url, Method::Get);

let err = client
.execute_request(&req)
.await
.expect_err("https://127.0.0.1:1/ must fail to connect");

// Format the full source chain — the bug's signature is buried under
// anyhow-style wrapping otherwise.
let chain = format!("{err:#}");
assert!(
!chain.contains("scheme is not http"),
"Regression: reqwest has no TLS backend compiled in. \
The dependency graph dropped its TLS connector — likely the explicit \
`reqwest = {{ ..., features = [\"native-tls\"] }}` line was removed from \
Cargo.toml. Full error chain:\n{chain}"
);
}
Loading