This repository is a fork of Cloudflare Pingora maintained by the Praxis project. The fork is intended to be temporary.
Upstream Pingora does not expose all of the TLS customization hooks and proxy body handling that Praxis requires. Upstream review cycles are slow, making it impractical to wait for changes to be accepted before shipping Praxis releases.
Syncing onto upstream 0.9.0 also pulls in native peer-certificate handshake callbacks for the rustls backend (cloudflare/pingora#908), which let Praxis read a peer's SPIFFE identity during mutual-TLS handshakes. Earlier releases handed the rustls handshake-complete callback an empty certificate reference, unlike the boringssl and openssl backends.
The 0.11.0 release moves the base to upstream main
at 4487f7b, five commits past 0.9.0. Those commits
make the response body and trailer filters on
ProxyHttp async (every implementation has to
follow), parameterize downstream sessions, add an
owned HTTP test origin, and abort TLS offload tasks
when they are dropped.
The 0.11.1 patch release fixes L4 stream construction with Tokio 1.54 and preserves unbuffered reads when configuring connection buffers.
The 0.11.2 patch release updates header compression
dependencies to zstd 0.14 and zstd-safe 8 and
raises the fork's minimum supported Rust version to
1.88. It also aligns CI with that minimum and fixes
cache tests that assumed unique timestamps or a
deterministic TinyLFU admission order.
The fork is based on upstream main at 4487f7b
(0.9.0 plus the five commits above) with seven
functional changes:
Adds TlsSettings::with_server_config() for
injecting a fully built rustls ServerConfig (0-RTT,
session resumption, custom certificate resolvers).
Adapted from the approach proposed in
cloudflare/pingora#726.
Upstream 0.9.0 added a related but distinct
Acceptor::from_server_config(). The fork keeps
TlsSettings::with_server_config() so existing Praxis
call sites need no changes.
Files: pingora-core/src/listeners/tls/rustls/mod.rs
Adds WrappedX509::parse() for per-cluster CA
verification, enabling Praxis to load and verify
certificates for individual upstream clusters.
Files: pingora-core/src/utils/tls/rustls.rs,
pingora-core/src/utils/tls/s2n.rs
Triggers initial body send when the downstream connection has already consumed data, fixing a proxy body forwarding edge case.
Files: pingora-proxy/src/proxy_custom.rs,
pingora-proxy/src/proxy_h1.rs,
pingora-proxy/src/proxy_h2.rs
pingora-rustls no longer depends on ring and
installs no rustls CryptoProvider; rustls'
custom-provider feature makes a missing install a
loud failure instead of a silent default. The
application chooses the provider and installs it
before any service is constructed. This is what lets
Praxis run all cryptography in the RHEL OpenSSL FIPS
provider. Upstream still hardcodes ring.
pingora-s2n is untouched.
Files: pingora-rustls/,
pingora-core/src/connectors/tls/rustls/mod.rs,
pingora-core/src/listeners/tls/rustls/mod.rs
Every upstream ClientConfig the rustls connector
builds sets require_ems (RFC 7627), which NIST
SP 800-52r2 requires and without which rustls reports
a config as not FIPS.
Files: pingora-core/src/connectors/tls/rustls/mod.rs
pingora-rustls-openssl/ is
tofay/rustls-openssl (MIT) at 0.4.1
plus its pull request #44, which routes the last
legacy OpenSSL calls through the EVP provider APIs so
they reach the FIPS module and adds the
SigningKey::public_key override Praxis needs to
load certificates. It is published as
quixotic-plecostomus-rustls-openssl with the
rustls_openssl library name unchanged. The copy is
a snapshot; commit-level history stays upstream.
When upstream releases those changes Praxis switches
back and the copy is removed.
A client that connects and hangs up before the TLS
handshake finishes (a Kubernetes TCP probe, a load
balancer health check, a port scanner) is logged at
debug instead of error. Real failures, such as
protocol errors, received alerts, missing client
certificates and the handshake timeout, still log at
error. To tell them apart, the rustls server
handshake keeps the underlying io::Error as the
error's cause instead of flattening it into the
context string. Only the rustls backend does this;
boringssl, openssl and s2n are unchanged.
A client that rejects the server certificate and
disconnects without sending an alert looks the same
as a probe, so it also logs at debug. Upstream
still logs every failed downstream handshake at
error.
Files: pingora-core/src/services/listening.rs,
pingora-core/src/protocols/tls/rustls/server.rs,
pingora-core/src/protocols/tls/rustls/stream.rs
The fork also carries dependency-hygiene changes:
dropping the unmaintained derivative crate,
replacing the archived serde_yaml with yaml_serde,
and modernizing dashmap, rand, x509-parser, and
indexmap/blake2. All remaining commits are CI and
fork infrastructure scaffolding.
The fork is published to crates.io as
quixotic-plecostomus-* (22 pingora crates plus the
vendored quixotic-plecostomus-rustls-openssl; the
upstream pingora-test-utils crate stays unpublished
and path-only). The name was
chosen to avoid appearing in search results for
"Pingora" or "Praxis", since the fork is temporary
and not intended for external use.
Each crate preserves [lib] name = "pingora_*" so
that consumer source code requires zero changes -
only Cargo.toml package aliasing is needed:
pingora-core = {
version = "0.11.2",
package = "quixotic-plecostomus-core",
}The plan is to contribute the remaining changes
upstream and eliminate this fork. The ServerConfig
change already has a related upstream PR
(cloudflare/pingora#726). If upstream does
not accept the changes, we will document the
divergence formally and maintain this fork as a
first-class dependency with clear provenance.
| Upstream | https://github.com/cloudflare/pingora |
| Org fork | https://github.com/praxis-proxy/pingora |
| Base | upstream main at 4487f7b (0.9.0 + 5 commits) |
| License | Apache 2.0 (unchanged from upstream) |
| crates.io | quixotic-plecostomus-* v0.11.2 (pingora crates); quixotic-plecostomus-rustls-openssl v0.4.1 |