Insufficient Logging in Axum with Mutual Tls
Insufficient Logging in Axum with Mutual Tls — how this specific combination creates or exposes the vulnerability
Insufficient Logging in an Axum service that uses Mutual TLS (mTLS) can leave security events undocumented and hinder incident response. mTLS adds a strong authentication layer by requiring clients to present valid certificates, but it does not automatically guarantee that important events are recorded. When logging is insufficient, successful and failed mTLS handshakes, certificate validation outcomes, and request processing details may not be captured, allowing attackers to probe endpoints without leaving an auditable trace.
In Axum, which is built on top of Tower and Hyper, the lack of structured logging around mTLS verification steps means operators lose visibility into critical security signals. For example, if a client certificate is rejected due to expiry or an untrusted CA, an Axum application without explicit logging may produce no record of the rejection, making it difficult to detect reconnaissance or credential misuse. This gap is especially risky in environments where mTLS is used to enforce strict access control, because the absence of logs prevents correlation between rejected certificates and potential intrusion attempts.
Furthermore, insufficient logging can mask application-layer issues that occur after mTLS authentication, such as malformed requests or unexpected state transitions. Without logging request identifiers, certificate subject information, or outcome statuses, security teams cannot reliably trace an incident across network and application boundaries. In regulated contexts, this absence of detail conflicts with expectations for auditability, since frameworks like OWASP API Top 10 and standards such as PCI-DSS require sufficient logging to support forensic analysis.
middleBrick scans can surface Insufficient Logging findings by correlating runtime behavior with expected security controls, including mTLS interactions. The tool identifies missing log signals around authentication, authorization, and data exposure, and provides prioritized findings with remediation guidance. This helps teams ensure that mTLS deployments in Axum are not only enforced but also observable.
Mutual Tls-Specific Remediation in Axum — concrete code fixes
To address Insufficient Logging in Axum with Mutual TLS, implement structured logging at key stages: certificate validation, request handling, and error paths. Use the tracing and tracing_subscriber crates to emit structured events that include certificate metadata and request context. This ensures that mTLS-related decisions are recorded in a machine-readable format that can be consumed by monitoring systems.
Below are concrete, working examples of Axum services with mTLS and appropriate logging. The first example shows a basic mTLS setup using hyper::server::conn::AddrStream and Rustls to extract peer certificates and log them. The second example demonstrates how to propagate certificate information through request extensions and log it within handlers.
Example 1: mTLS with certificate logging on connection
use axum::Router;
use std::net::SocketAddr;
use tokio_rustls::rustls::{ServerConfig, NoClientAuth};
use tokio_rustls::TlsAcceptor;
use std::sync::Arc;
use tracing::{info, error};
use tracing_subscriber;
async fn run_mtls_server() {
tracing_subscriber::fmt::init();
let mut config = ServerConfig::builder()
.with_safe_defaults()
.with_client_auth_cert(vec![include_bytes("../ca.crt") as &[u8]].into())
.expect("valid client auth");
config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
let tls_acceptor = TlsAcceptor::from(Arc::new(config));
let app = Router::new()
.route("/health", axum::routing::get(|| async { "ok" }));
let listener = tokio::net::TcpListener::bind("0.0.0.0:8443").await.unwrap();
info!(message = "mTLS server listening", port = 8443);
loop {
let (stream, addr) = listener.accept().await.unwrap();
let tls_acceptor = tls_acceptor.clone();
tokio::spawn(async move {
let tls_stream = match tls_acceptor.accept(&mut stream).await {
Ok(t) => t,
Err(e) => {
error!(peer = %addr, error = %e, "mTLS handshake failed");
return;
}
};
if let Some(certs) = tls_stream.get_ref().1.peer_certificates() {
for cert in certs {
info!(peer = %addr, certificate = ?cert, "mTLS client certificate");
}
} else {
warn!(peer = %addr, "no client certificate presented");
}
// Forward to Axum service (not shown) — in practice you’d integrate with hyper service handling
});
}
}
Example 2: Logging within Axum handlers using request extensions
use axum::{routing::get, Router, Extension, http::Request};
use std::sync::Arc;
use tracing::info;
struct CertInfo {
subject: String,
}
async fn handler(Extension(cert_info): Extension>) -> String {
info!(subject = %cert_info.subject, "handling request with mTLS client");
format!("Hello, {}", cert_info.subject)
}
fn build_app() -> Router {
Router::new()
.route("/api", get(handler))
.layer(axum::middleware::from_fn(|req, next| {
let cert_info = req.extensions().get::>().cloned();
async move {
match cert_info {
Some(ci) => {
info!(subject = %ci.subject, stage = "middleware", "mTLS info");
next.run(req).await
}
None => {
warn!(stage = "middleware", "missing certificate extension");
let response = axum::response::Response::builder()
.status(400)
.body("missing cert".into())
.unwrap();
Ok(response)
}
}
}
}))
}
These examples show how to log certificate details and request context in Axum services using mTLS. By emitting structured logs at the connection and handler layers, teams can ensure that mTLS enforcement is observable and that security-relevant events are retained for analysis.