Skip to content

[Fix Native TLS Reqwest] Activate native-tls connector for Entra HTTPS#8

Merged
affandar merged 7 commits into
mainfrom
fix/native-tls-reqwest-feature
May 13, 2026
Merged

[Fix Native TLS Reqwest] Activate native-tls connector for Entra HTTPS#8
affandar merged 7 commits into
mainfrom
fix/native-tls-reqwest-feature

Conversation

@ChrisKrawczyk

Copy link
Copy Markdown
Contributor

[Fix Native TLS Reqwest] Activate native-tls connector for Entra HTTPS

Summary

The Microsoft Entra credential chain in 0.1.310.1.32 failed at hyper's connector layer with "invalid URL, scheme is not http" for every HTTPS request — making WorkloadIdentityCredential (AKS / federated identity), ManagedIdentityCredential (HTTPS-bound), and the ClientAssertionCredential token endpoint unreachable. As a result PostgresProvider::new_with_entra / new_with_schema_and_entra could not acquire access tokens in any production topology that did not have the Azure CLI installed locally — passwordless Entra was effectively broken on AKS and similar environments.

Root cause: azure_core 0.35's reqwest feature activates typespec_client_core/reqwest, which activates the optional reqwest dep with default-features = false. With no TLS feature enabled on the resolved reqwest build, hyper rejects every HTTPS URL at the connector layer before any DNS or TCP I/O occurs. The Cargo.toml comment block above the Azure deps claimed the configuration "routes through reqwest's default-tls feature, keeping the build on native-tls" — both halves of that claim are wrong:

  1. azure_core/reqwest = ["typespec_client_core/reqwest"] and typespec_client_core/reqwest = ["dep:reqwest"] activate only the optional dep; neither flag enables any reqwest features. typespec_client_core's reqwest dep is default-features = false, suppressing reqwest's own defaults entirely.
  2. In reqwest 0.13.3, default-tls = ["rustls"] (the feature was renamed in 0.12→0.13). Even if defaults were active, they would NOT preserve the openssl posture.

tests/entra_live_test.rs only exercises DeveloperToolsCredential (shells out to az login), so the missing TLS in the reqwest transport was never traversed by CI.

Fix

A single line added to [dependencies] in Cargo.toml:

reqwest = { version = "0.13", default-features = false, features = ["native-tls"] }

Cargo's feature unification merges this declaration with typespec_client_core's reqwest dep (same resolved 0.13.3), giving the resolved reqwest crate the ["stream", "native-tls"] feature union. The native-tls feature activates hyper-tls 0.6 and tokio-native-tls 0.3 (openssl-backed on Linux). No source-code changes are required.

The accompanying comment block in Cargo.toml:46-72 is rewritten to describe the actual feature graph and rationale for the explicit dep.

Changes

  • Cargo.toml
    • Add explicit reqwest = { version = "0.13", default-features = false, features = ["native-tls"] } to [dependencies].
    • Rewrite the comment block above the Azure deps to reflect the real dep graph (drop the incorrect default-tls claim).
    • Add url = "2" to [dev-dependencies] for the new regression test.
    • Bump package.version from 0.1.32 to 0.1.33.
  • tests/native_tls_regression.rs (new)
    • Constructs the HTTP client via azure_core::http::new_http_client(None) — the same constructor azure_identity uses internally — and issues a GET to https://127.0.0.1:1/ (closed local port, fail-fast at TCP-connect, no network egress, no DNS).
    • Asserts the error chain does NOT contain "scheme is not http". That substring is hyper's signature for "no TLS connector compiled in"; its presence indicates the regression has returned.
  • CHANGELOG.md
    • Add ## [0.1.33] entry under ### Fixed describing the symptom, root cause, and fix.

Testing

  • cargo build --release — clean.
  • cargo test --release --test native_tls_regression1/1 passes.
  • cargo test --release --lib — 35 lib tests pass. 3 provider::entra_pipeline_tests::* fail with "pool timed out while waiting for an open connection" — these require a live Postgres at localhost:5432 and are pre-existing on main, unrelated to this change.
  • cargo clippy --all-targets — 0 errors. Only pre-existing warnings (map_identity at src/provider.rs:2913, expect_fun_call in two test files).
  • cargo tree --target x86_64-unknown-linux-gnu --edges normal -i rustls / ring / aws-lc-rs / hyper-rustls / tokio-rustls — each returns "did not match any packages". FIPS-compliance posture (no rustls / ring / aws-lc-rs) preserved.
  • cargo tree -i hyper-tlshyper-tls v0.6.0 activated via reqwest v0.13.3duroxide-pg v0.1.33. ✅

Breaking Changes

None. Public API is unchanged; this is a pure dependency-graph fix.

Forward-compatibility note

This fix relies on Cargo's per-crate-version feature unification merging this crate's explicit reqwest 0.13 declaration with typespec_client_core's declaration. If typespec_client_core is ever bumped to declare reqwest at a different major (e.g. 0.14), the two declarations will resolve to different reqwest crates and the fix will silently regress — tests/native_tls_regression.rs is the canary that catches that scenario.

Notes for reviewers

  • The fix is one line of Cargo.toml + a comment-block rewrite. No source code is touched.
  • The regression test runs entirely offline (no network egress, no Azure credentials) and finishes in ~2 seconds.
  • A sibling crate (duroxide-pg-opt) has the same bug and will receive an identical fix in a separate PR.

Artifacts

PAW workflow artifacts (CodeResearch.md, ImplementationPlan.md, Docs.md, WorkflowContext.md) were tracked during development and removed via stop-tracking before this PR. They remain available on the branch at commit 73d369d.


🐾 Generated with PAW

chkraw and others added 7 commits May 13, 2026 09:10
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Entra credential chain failed with 'invalid URL, scheme is not http'
at hyper's connector layer because azure_core/reqwest activated the
optional reqwest dep via typespec_client_core with default-features =
false, leaving the resolved reqwest with no TLS backend.

Adds an explicit reqwest dep with features = ["native-tls"] so Cargo
feature unification activates the openssl-backed connector. Verified
that the resolved dep graph contains no rustls / ring / aws-lc-rs.

Bumps version to 0.1.33 and adds a regression test in
tests/native_tls_regression.rs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ChrisKrawczyk ChrisKrawczyk requested a review from affandar May 13, 2026 16:52
@affandar affandar merged commit 61ae974 into main May 13, 2026
4 checks passed
ChrisKrawczyk added a commit that referenced this pull request May 13, 2026
Follow-up to PR #8 — step 3 of prompts/publish-crate.md (README
'Latest Release' update) was omitted from the version-bump PR.
Demote 0.1.32 to 'Previous Release' and add 0.1.33 summary.

Co-authored-by: Christopher Krawczyk (SQL) (from Dev Box) <chkraw@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ChrisKrawczyk added a commit to microsoft/duroxide-node that referenced this pull request May 13, 2026
Bumps duroxide-pg 0.1.32 → 0.1.33 to pick up the upstream native-tls fix
(microsoft/duroxide-pg#8). Released duroxide-pg 0.1.32 was missing the
native-tls reqwest feature, so HTTPS calls — including the AAD token
acquisition that powers connectWithEntra and connectWithSchemaAndEntra —
failed at runtime with 'error sending request' / 'invalid URL, scheme is
not http'. No public API changes.

Co-authored-by: Christopher Krawczyk (SQL) (from Dev Box) <chkraw@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ChrisKrawczyk pushed a commit to affandar/PilotSwarm that referenced this pull request May 13, 2026
duroxide 0.1.26 picks up duroxide-pg 0.1.33 / duroxide-pg-opt 0.1.29 with the reqwest default-features=false + native-tls fix (upstream PRs microsoft/duroxide-pg#8, microsoft/duroxide-pg-opt#9). Without this, AAD token acquisition inside the duroxide orchestration store fails with a TLS handshake error in containers, making useManagedIdentity:true unusable end-to-end.

Validated on chkrawps7 AKS with all 3 worker replicas running clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ChrisKrawczyk added a commit to affandar/PilotSwarm that referenced this pull request May 13, 2026
#26)

* feat(sdk): route duroxide orchestration store through Entra in MI mode

When useManagedIdentity is true, the worker, client, and management
client now all route the duroxide Postgres store through
PostgresProvider.connectWithSchemaAndEntra (added in duroxide-node
0.1.25) instead of connectWithSchema. CMS, facts, and the orchestration
store now all authenticate via Microsoft Entra ID — closing the last
gap that forced deployments to keep a password URL for duroxide.

The legacy password-in-URL path (useManagedIdentity unset or false) is
unchanged, so the existing scripts/deploy-aks.sh flow continues to work
without changes.

URL parsing and AAD user resolution are extracted from
pg-pool-factory.buildPgPoolConfig into parsePostgresUrl /
resolveAadPostgresUser helpers so the orchestration path and the
CMS/facts path stay aligned.

Adds duroxide-provider-factory.ts (createDuroxidePostgresProvider) with
unit coverage for legacy vs MI routing, URL parsing defaults, and the
missing-user error path. Updates stale 'no token-callback hook
upstream' comments and docs (README, deploy scripts README, bicep
postgres comment, worker/portal overlay .env files, template.env,
compose-env.mjs, types.ts JSDoc).

Bumps pilotswarm-sdk to 0.1.29.

Unblocks downstream pure-Entra cutover (microsoft/waldemort PR #7
Phase 8): consumers can now drop the password store argument, flip
passwordAuth: 'Disabled' on postgres.bicep, and remove
postgres-admin-password from Key Vault and SecretProviderClass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* build(docker): bump worker + portal base to node:24-trixie-slim for duroxide 0.1.25 glibc 2.39+

duroxide-linux-x64-gnu prebuilds shipped in duroxide-node 0.1.25 link against

GLIBC_2.39. node:24-slim (bookworm-slim) ships glibc 2.36 and crashes at

dlopen with 'version GLIBC_2.39 not found'. Trixie (Debian 13) provides

glibc 2.41, satisfying the requirement.

Caught during the chkrawps7 E2E deploy validating the orchestration-store

Entra migration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore(sdk): bump duroxide to ^0.1.26 for native-tls TLS fix

duroxide 0.1.26 picks up duroxide-pg 0.1.33 / duroxide-pg-opt 0.1.29 with the reqwest default-features=false + native-tls fix (upstream PRs microsoft/duroxide-pg#8, microsoft/duroxide-pg-opt#9). Without this, AAD token acquisition inside the duroxide orchestration store fails with a TLS handshake error in containers, making useManagedIdentity:true unusable end-to-end.

Validated on chkrawps7 AKS with all 3 worker replicas running clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Christopher Krawczyk (SQL) (from Dev Box) <chkraw@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants