Dangling Dns in Actix with Mutual Tls
Dangling Dns in Actix with Mutual Tls — how this specific combination creates or exposes the vulnerability
A dangling DNS record occurs when a hostname resolves to an infrastructure that is no longer in use or no longer intended to serve the application. In an Actix web service configured for mutual TLS (mTLS), a dangling DNS entry can expose private endpoints or internal services if a client resolves the name to an unexpected IP. Because mTLS requires both the server and the client to present valid certificates, the mismatch between the expected DNS name and the actual host running the service can lead to failed handshakes or, in misconfigured deployments, to acceptance of connections that should not be trusted.
Consider a scenario where a staging environment is decommissioned but its DNS record is not removed or updated. If an Actix server is still reachable at that hostname and still requests client certificates, an attacker who can route traffic to that address might present a valid certificate issued for a different service. The Actix application, validating the client certificate but not strictly verifying that the hostname matches the certificate’s subject or SAN (subject alternative names), may accept the connection. This is especially risky when combined with internal IPs or when the mTLS configuration prioritizes certificate validity over hostname alignment.
The interaction between dangling DNS and mTLS in Actix becomes critical when certificate verification does not include strict hostname checks. For example, if the server-side TLS configuration uses a permissive domain pattern or does not enforce server name indication (SNI) validation, a client with a valid certificate for another service can connect to the dangling endpoint. The server may interpret the request as legitimate, potentially exposing routes that rely on mTLS for access control. This can lead to unauthorized access across microservices that share a mesh but do not expect cross-environment communication.
To detect this class of issue, scans should resolve the hostname independently and compare the resolved IPs to the expected deployment inventory. If the IP does not match known infrastructure, the DNS record is considered dangling. In the context of mTLS, scanners also validate that the server requests and verifies client certificates and that the hostname is checked against the certificate. middleBrick performs unauthenticated scans that include DNS resolution and TLS handshake analysis, checking for missing hostname verification in mTLS setups and identifying endpoints that may be exposed due to stale DNS entries.
An example of unsafe server-side configuration in Actix where hostname verification is omitted:
use actix_web::{web, App, HttpServer, Responder};
use actix_web::middleware::Logger;
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
fn create_ssl() -> SslAcceptor {
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
builder.set_private_key_file("key.pem", SslFiletype::PEM).unwrap();
builder.set_certificate_chain_file("cert.pem").unwrap();
// Missing: set_verify_peer, set_verify_callback to enforce client cert hostname checks
builder
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.service(web::resource("/api/data").to(|| async { "ok" }))
})
.bind_openssl("0.0.0.0:8443", create_ssl())?
.run()
.await
}
Mutual Tls-Specific Remediation in Actix — concrete code fixes
To mitigate dangling DNS risks in an Actix service with mTLS, enforce strict hostname verification on the server side and ensure client certificates are issued only for intended DNS names. The server must validate that the client certificate’s subject or SAN matches the expected hostname. In OpenSSL terms, this means enabling peer verification and setting a proper verification callback that checks the hostname against the certificate.
Below is a secure Actix configuration that enables mTLS with hostname verification. The example loads server and CA certificates, requests and verifies client certificates, and checks the hostname using the certificate’s SAN or common name.
use actix_web::{web, App, HttpServer, Responder};
use actix_web::middleware::Logger;
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode};
use openssl::x509::X509;
use std::net::IpAddr;
fn create_ssl() -> SslAcceptor {
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
builder.set_private_key_file("key.pem", SslFiletype::PEM).unwrap();
builder.set_certificate_chain_file("cert.pem").unwrap();
// Require client certificate
builder.set_verify(
SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT,
verify_callback,
);
builder
}
fn verify_callback(
certs: &[X509],
server_verifier: &mut openssl::ssl::ServerVerifyArgs,
) -> bool {
// Expect at least one certificate
let client_cert = match certs.first() {
Some(cert) => cert,
None => return false,
};
// Perform hostname verification: compare certificate SANs and CN with expected hostname
let hostname = server_verifier.hostname().unwrap_or_default();
if !verify_hostname(client_cert, hostname) {
return false;
}
// Optionally, enforce additional constraints (e.g., extended key usage)
true
}
fn verify_hostname(cert: &X509, hostname: &str) -> bool {
// Check SANs first
for i in 0..cert.subject_name().entry_count() {
if let Some(entry) = cert.subject_name().get_entry_by_nid(entry.nid()) {
let data = entry.data();
if let Ok(s) = data.as_utf8_string() {
// naive match; in production, use a proper hostname matching library
if s == hostname {
return true;
}
}
}
}
false
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.service(web::resource("/api/data").to(|| async { "ok" }))
})
.bind_openssl("0.0.0.0:8443", create_ssl())?
.run()
.await
}
Key points in the remediation:
- Set
SslVerifyMode::PEERandFAIL_IF_NO_PEER_CERTto require client certificates. - Implement a verification callback that checks the client certificate against the hostname the client intended to reach (using SNI). This prevents a valid certificate issued for another service from being accepted against a dangling DNS name.
- Ensure the hostname verification logic covers Subject Alternative Names and, if needed, the Common Name field, using a robust matching approach rather than simple string equality.
Additionally, remove or update stale DNS records so that the hostname does not resolve to unintended infrastructure. In CI/CD, the GitHub Action can be configured to fail builds if scans detect missing hostname verification in mTLS configurations, preventing deployments with such issues.