Container Escape in Rocket with Mutual Tls
Container Escape in Rocket with Mutual Tls — how this specific combination creates or exposes the vulnerability
A container escape in a Rocket service that uses mutual TLS (mTLS) typically arises when the API endpoint is reachable from the container network and the mTLS configuration does not sufficiently restrict the set of trusted certificates or the identity validation performed by Rocket. Even though mTLS provides transport‑level authentication, a misconfiguration can allow an authenticated but malicious client inside the same container network to probe or abuse the endpoint in ways that lead to unauthorized access to host resources, metadata, or other containers.
Consider a Rocket API that enforces client certificate verification but does not validate the certificate’s identity (e.g., Common Name or SAN) against an allowlist. An attacker who can place a valid client certificate within the same container or compromise a trusted certificate authority may authenticate successfully and then leverage overly permissive routes to interact with the host filesystem, invoke unsafe endpoints, or enumerate services that should remain internal. The scanner’s checks include Unauthenticated LLM endpoint detection and unsafe consumption patterns; when mTLS is present but improperly scoped, these checks can surface findings such as BOLA/IDOR or privilege‑escalation risks because the API trusts any presented certificate without enforcing strict identity constraints.
From a compliance and mapping perspective, such a finding may align with OWASP API Top 10:2023 — Broken Object Level Authorization (BOLA) and Security Misconfiguration. Real attack patterns include using a valid mTLS session to call administrative routes or to access internal metadata services (e.g., http://169.254.169.254) that are inadvertently exposed within the container network. Because Rocket routes are defined at compile time, an attacker does not need to exploit a runtime vulnerability in Rocket itself; instead, they exploit insufficient authorization tied to the authenticated identity. The scanner’s parallel checks, such as Property Authorization and Input Validation, are designed to detect whether endpoints enforce per‑identity checks and whether inputs are constrained, which helps surface cases where mTLS authentication exists but authorization is missing or incomplete.
In environments where the API definition is provided via OpenAPI/Swagger (2.0, 3.0, 3.1) with full $ref resolution, the scanner cross‑references spec definitions with runtime behavior. This means a route documented as requiring mTLS but lacking explicit scope or role constraints can be flagged: the scanner detects that authentication is present but authorization is weak, producing a finding with severity and remediation guidance. MiddleBrick reports do not fix the configuration; they provide actionable steps so you can tighten certificate validation, narrow allowed identities, and ensure routes check the authenticated principal before performing sensitive operations.
Mutual Tls-Specific Remediation in Rocket — concrete code fixes
To remediate container‑escape risks when using mutual TLS in Rocket, focus on strict certificate validation, identity-based authorization, and minimizing what authenticated clients can reach. Below are concrete, realistic Rocket snippets that demonstrate proper mTLS setup and per‑route identity checks.
1. Configure Rocket to require and verify client certificates
Use Rocket’s TLS configuration to require client certificates and rely on the Rust TLS backend’s verification. The following example shows a basic Rocket configuration with mTLS using rustls:
// Cargo.toml dependencies
// rocket = { version = "0.5", features = ["tls"] }
// tokio = { version = "1", features = ["rt", "net", "macros"] }
use rocket::Build;
use rocket::fairing::AdHoc;
use rocket::http::Status;
use rocket::outcome::Outcome;
use rocket::request::{self, FromRequest, Request};
use rocket::State;
use std::sync::Arc;
use rustls::{ClientConfig, RootCertStore, ServerCertVerifier};
use std::io::Cursor;
use std::sync::Mutex;
// A simple request guard that checks a client certificate fingerprint against an allowlist.
struct AuthenticatedClient {
subject: String,
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for AuthenticatedClient {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> request::Outcome {
// Extract client certs from the request extensions (populated by rustls)
if let Some(certs) = request.extensions().get::>() {
if let Some(first) = certs.first() {
let fingerprint = sha2::Sha256::digest(&first.0);
// In practice, compare against a stored set of allowed fingerprints or SANs.
let allowed = vec!["aa:bb:cc:..." /* expected fingerprint */];
if allowed.contains(&format!("{:x}", fingerprint).as_str()) {
return Outcome::Success(AuthenticatedClient { subject: format!("{:x}", fingerprint) });
}
}
}
Outcome::Failure((Status::Unauthorized, ()))
}
}
#[rocket::main]
async fn main() {
let mut root_store = RootCertStore::empty();
// Load your trusted CA bundle (PEM)
let pem = std::fs::read("ca.pem").expect("unable to read CA");
root_store.add_parsable_certificates(&mut rustls_pemfile::certs(&mut Cursor::new(pem)).unwrap());
let mut client_config = ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_store)
.with_no_client_auth(); // We’ll enforce client auth per route or via fairing
// If you want server requires client certs globally, you can set client_auth_mandatory
// For this example we enforce via a guard and route-level requires.
let tls = rocket::tls::TlsConfig {
certs: "server.crt".into(),
key: "server.key".into(),
client_ca: Some("ca.pem".into()),
client_auth_mandatory: false, // handled by guard
..Default::default()
};
rocket::build()
.manage(Arc::new(client_config))
.attach(AdHoc::on_ignite("Configure TLS", |rocket| async {
rocket::custom(rocket.config().clone()).configure()
}))
.mount("/", routes![public, admin])
.configure(rocket::Config { tls: Some(tls), ..Default::default() })
.launch()
.await
.unwrap();
}
#[rocket::get("/public")]
fn public() -> &'static str {
"public endpoint"
}
#[rocket::get("/admin")]
#[rocket::require(AuthenticatedClient)]
fn admin(client: AuthenticatedClient) -> String {
format!("admin access for client fingerprint: {}", client.subject)
}
2. Enforce identity checks and least privilege
After mTLS authentication, ensure each route validates the authenticated identity against an allowlist and only grants the minimum required access. The above guard checks a fingerprint; in production you would map fingerprints or SANs to roles/scopes and verify before performing sensitive actions. Combine this with input validation and rate limiting to reduce the impact of a compromised certificate.
If you prefer a centralized policy, use a request guard that reads a role claim from a JWT embedded in the certificate (if using OIDC-style issuance) or query an authorization service. MiddleBrick’s scans (via CLI or Dashboard) can then verify that authentication is present and that routes include per‑identity checks, surfacing any gaps between mTLS presence and authorization logic.