fix(server): install rustls default CryptoProvider at startup - #34
Merged
Conversation
aisix panics on first TLS handshake (etcd connect, reqwest register
call, etc.) with:
thread 'main' panicked at rustls/.../crypto/mod.rs:249:14:
Could not automatically determine the process-level CryptoProvider
from Rustls crate features.
rustls 0.23 dropped implicit provider selection — when both
`aws-lc-rs` and `ring` are reachable through transitive deps (we have
reqwest `rustls-tls`, etcd-client, tokio-rustls… all enabling one or
the other), the runtime can't pick. The panic fires the first time
any TLS operation touches the crypto layer.
Call `aws_lc_rs::default_provider().install_default()` at the very
top of main, before anything else loads. `let _ =` because install
is idempotent and we don't care if another crate beat us to it.
There was a problem hiding this comment.
Pull request overview
Ensures aisix doesn’t panic on first TLS usage under rustls 0.23 by explicitly installing a process-wide rustls CryptoProvider at startup.
Changes:
- Install the aws-lc-rs rustls
CryptoProviderat the start ofmain()to avoid runtime provider auto-detection panics when multiple crypto backends are present via transitive dependencies.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+56
to
+57
| // already depends on transitively. Falls back to ring only if | ||
| // the process somehow has a provider installed already (idempotent). |
There was a problem hiding this comment.
The comment says this "falls back to ring" if a provider is already installed, but the code doesn't install ring or perform any fallback logic; it simply keeps whatever default provider was installed first. Consider rewording to avoid implying ring is involved (e.g., "if another provider is already installed, keep it").
Suggested change
| // already depends on transitively. Falls back to ring only if | |
| // the process somehow has a provider installed already (idempotent). | |
| // already depends on transitively. If another provider is already | |
| // installed for the process, `install_default()` keeps it unchanged. |
3 tasks
moonming
added a commit
that referenced
this pull request
Apr 26, 2026
…t tests CI on main has been red since #34 because aisix-core's Config crate loader merges every AISIX_-prefixed env var into the root Config struct (config-rs Environment::with_prefix("AISIX")), and Config has #[serde(deny_unknown_fields)]. The redis integration test sets AISIX_REDIS_URL on the rust-unit job, which leaks into every Config::load_from_path call as `redis_url` and panics: Config("deserialize: unknown field `redis_url`, expected one of `etcd`, `proxy`, `admin`, `observability`, `cache`, `managed`") 8 of 9 aisix-core::config::tests fail (the one that doesn't is rejects_unknown_fields, which intentionally swallows the error). Rename the env var so it doesn't sit under the AISIX_ prefix at all. crates/aisix-cache/tests/redis_integration.rs reads CACHE_TEST_REDIS_URL; CI sets the same. docs/testing.md + crates/aisix-cache/src/redis.rs comment updated to match. Verified: with the rename, all 9 config tests pass even with CACHE_TEST_REDIS_URL set; reproducing with the old AISIX_REDIS_URL still fails as expected (so the loader behaviour is unchanged for real AISIX_-prefixed env overrides).
moonming
added a commit
that referenced
this pull request
Apr 26, 2026
…40) * fix(aisix-etcd): make supervisor cache-write tests deterministic Two supervisor tests waited on the spawned cache write via `tokio::time::sleep(50ms)`. Under heavy CI load the spawn lost the race against the disk read that followed, surfacing as: resync_writes_to_disk_cache_then_restore_replays_it FAILED put_and_delete_keep_cache_in_sync FAILED Track the JoinHandle for each spawned write in a `pending_writes` Mutex<Vec<_>> on the Supervisor, and expose a test-only async `await_pending_cache_writes` that drains and awaits them. Both tests now wait on real completion instead of a wall clock. The new field is `#[cfg(test)]`-friendly via the awaiter — production code never reads it. If a handle is dropped during shutdown the underlying write either completed or was cancelled; the on-disk cache is best-effort, and the next live cycle re-publishes from etcd anyway. * fix(ci): rename AISIX_REDIS_URL → CACHE_TEST_REDIS_URL to unblock unit tests CI on main has been red since #34 because aisix-core's Config crate loader merges every AISIX_-prefixed env var into the root Config struct (config-rs Environment::with_prefix("AISIX")), and Config has #[serde(deny_unknown_fields)]. The redis integration test sets AISIX_REDIS_URL on the rust-unit job, which leaks into every Config::load_from_path call as `redis_url` and panics: Config("deserialize: unknown field `redis_url`, expected one of `etcd`, `proxy`, `admin`, `observability`, `cache`, `managed`") 8 of 9 aisix-core::config::tests fail (the one that doesn't is rejects_unknown_fields, which intentionally swallows the error). Rename the env var so it doesn't sit under the AISIX_ prefix at all. crates/aisix-cache/tests/redis_integration.rs reads CACHE_TEST_REDIS_URL; CI sets the same. docs/testing.md + crates/aisix-cache/src/redis.rs comment updated to match. Verified: with the rename, all 9 config tests pass even with CACHE_TEST_REDIS_URL set; reproducing with the old AISIX_REDIS_URL still fails as expected (so the loader behaviour is unchanged for real AISIX_-prefixed env overrides). * ci: tolerate artifact upload quota errors temporarily Both `rust unit + coverage` and `build ui` jobs are currently failing on the upload-artifact step with: Failed to CreateArtifact: Artifact storage quota has been hit. Unable to upload any new artifacts. Usage is recalculated every 6-12 hours. Tests + clippy pass; only the artifact upload is blocked. Add `continue-on-error: true` to those two upload-artifact steps so the test-passing signal isn't masked by the quota issue. Downstream jobs that need ui-dist (build-bin → e2e) will fail at download-artifact when the upload was skipped; e2e is already `continue-on-error: true` at the job level, and coverage-gate is advisory. Revert this once the org-level storage usage refreshes (within 6-12h) or the quota is raised. * ci: soft-fail build-aisix while artifact storage quota persists build-aisix downloads ui-dist from build-ui. With build-ui's upload-artifact set to continue-on-error during the storage quota outage, the download fails and build-aisix errors. Since build-aisix only feeds the advisory e2e job, mark it continue-on-error too so the PR doesn't go red on a transitive dependency. Revert with the other two when storage usage refreshes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
aisix panics on first TLS handshake (etcd connect, reqwest /dp/register, etc.) with:
```
thread 'main' panicked at rustls/.../crypto/mod.rs:249:14:
Could not automatically determine the process-level CryptoProvider from Rustls crate features.
```
rustls 0.23 dropped implicit provider selection — when both `aws-lc-rs` and `ring` are reachable through transitive deps (reqwest `rustls-tls`, etcd-client, tokio-rustls…), the runtime can't pick. Fix: call `aws_lc_rs::default_provider().install_default()` at the very top of `main`, before anything else.
`let _ =` because install is idempotent and the return value only signals "another crate got there first" — which doesn't affect correctness.
Why aws-lc-rs and not ring
It's the upstream rustls default as of 0.23, FIPS-capable out of the box, and already in the transitive dep graph through reqwest + etcd-client. Picking it avoids adding another RSA/EC crypto implementation to the binary.
How we caught it
AISIX-Cloud e2e stack exposed this: the DP container is `docker run` via the test harness, hits this panic within 100ms of startup, exits, and `docker run --rm` removes the container before the harness can query `docker port`. Container logs now dumped on failure.
Test plan