Distributed Denial Of Service in Axum with Mutual Tls
Distributed Denial Of Service in Axum with Mutual Tls — how this specific combination creates or exposes the vulnerability
When Axum is deployed with Mutual TLS (mTLS), the handshake and per-request cryptographic verification introduce resource costs that can be abused to create a Distributed Denial of Service (DDoS) condition. Unlike plaintext HTTP, mTLS requires each client to present a valid certificate and complete asymmetric cryptography operations during the TLS handshake, which consumes CPU and memory. In an Axum service behind a load balancer or reverse proxy that terminates TLS, enabling mTLS at the application layer (e.g., via rustls middleware) means every connection incurs additional verification costs before business logic executes.
Specific risks emerge in three areas. First, certificate validation and key exchange increase CPU utilization, making the service more susceptible to CPU exhaustion under high concurrency. Second, mTLS connections remain open longer during the handshake, tying up worker threads or async tasks and reducing the pool available for legitimate requests. Third, if rate limiting or connection throttling is not applied before the application layer, an attacker can open many mTLS connections with valid but low-reputation certificates, exhausting thread pools or memory and causing legitimate clients to time out. These vectors do not require authentication bypass or business logic flaws; they exploit the inherent overhead of mTLS within Axum’s request processing model.
Because middleBrick scans the unauthenticated attack surface, it can detect whether an Axum endpoint exposes resource-intensive mTLS handshakes without adequate rate limiting or connection caps. Findings may highlight missing transport-layer protections that, while not changing certificate validity, help reduce the DDoS surface when combined with infrastructure-level mitigations.
Mutual Tls-Specific Remediation in Axum — concrete code fixes
To reduce DDoS risk when using Mutual TLS in Axum, apply layered controls: terminate TLS at the edge, limit handshake costs, and enforce strict connection and rate limits before requests reach the Rust runtime. Below are concrete Axum examples using rustls and tower middleware.
1. Axum with rustls mTLS server configuration
This example configures an Axum server with client certificate verification using rustls. Note that heavy per-connection validation is offloaded to the acceptor, and middleware enforces request limits.
use axum::Server;
use rustls::{Certificate, PrivateKey, ServerConfig};
use rustls_pemfile::{certs, pkcs8_private_keys};
use std::fs::File;
use std::io::BufReader;
use std::net::SocketAddr;
use axum::routing::get;
use axum::Router;
use tower_http::limit::RateLimitLayer;
use tower_http::timeout::TimeoutLayer;
use std::time::Duration;
#[tokio::main]
async fn main() {
// Load server certificate and key
let cert_file = &mut BufReader::new(File::open("server.crt").unwrap());
let key_file = &mut BufReader::new(File::open("server.key").unwrap());
let cert_chain: Vec = certs(cert_file).unwrap().into_iter().map(Certificate).collect();
let mut keys: Vec = pkcs8_private_keys(key_file).unwrap().into_iter().map(PrivateKey).collect();
// Configure TLS with client authentication
let mut server_config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth() // Start without mTLS
.with_single_cert(cert_chain, keys.remove(0))
.unwrap();
server_config.client_auth_root_subjects = vec![/* omitted: trusted CAs as rustls::DistinguishedNames */];
server_config.client_auth = Some(rustls::server::ClientAuthMode::RequestCert);
// Build Axum app with rate and timeout layers
let app = Router::new()
.route("/health", get(|| async { "ok" }))
.layer(RateLimitLayer::new(100, Duration::from_secs(1))) // 100 requests per second
.layer(TimeoutLayer::new(Duration::from_secs(5)));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
Server::bind(&addr)
.https(rustls::TlsAcceptor::from(Arc::new(server_config))
.serve(app.into_make_service()))
.await
.unwrap();
}
2. Enforce connection and request caps
Use tower layers to cap concurrent connections and reject excessive mTLS handshakes before they consume thread pool resources.
use axum::Router;
use tower_http::classify::ServerErrorsAsFailures;
use tower_http::services::ServeDir;
use tower_limit::concurrency::ConcurrencyLimitLayer;
use tower_limit::rate::RateLimitLayer;
use std::sync::Arc;
use tokio::sync::Semaphore;
// Limit concurrent connections to protect against resource exhaustion
let semaphore = Arc::new(Semaphore::new(200));
let app = Router::new()
.route("/api/data", get(|| async { "data" }))
.layer(ConcurrencyLimitLayer::new(semaphore.clone()))
.layer(RateLimitLayer::new(50, std::time::Duration::from_secs(1)));
3. Edge termination and proxy controls
In production, terminate mTLS at a load balancer or ingress controller and forward only necessary headers to Axum. Configure the proxy to enforce connection caps and timeouts, reducing the likelihood that resource-heavy handshakes reach the application layer.