Dns Cache Poisoning in Axum with Cockroachdb
Dns Cache Poisoning in Axum with Cockroachdb — how this specific combination creates or exposes the vulnerability
DNS cache poisoning is a network-layer attack where a resolver is tricked into accepting malicious DNS responses, causing it to cache incorrect IP mappings. When an Axum service running in Rust depends on a CockroachDB backend, poisoned DNS entries can redirect database connections to an attacker-controlled host. Because Axum applications often resolve database hostnames at startup or lazily via system resolvers, a cached poison can persist across requests and affect all subsequent database interactions.
In this combination, Axum does not inherently validate DNS responses; it relies on the operating system’s resolver and TCP/HTTP client behavior. CockroachDB typically listens on specific ports (default 26257) and uses TLS for encrypted connections. If an attacker injects a spoofed response that maps, for example, cockroach.internal.example.com to a malicious IP, Axum may open TLS connections to the attacker instead of the intended CockroachDB cluster. The attacker can terminate TLS with a self-signed certificate or proxy traffic to the real database while eavesdropping or manipulating queries. Because CockroachDB often uses client certificate authentication and strong identity checks, an attacker without proper certificates may fail to authenticate; however, in configurations with relaxed certificate validation or in dev/test environments, the risk increases.
Another vector involves SRV records or custom DNS-based routing used by CockroachDB clusters. If Axum’s client resolves SRV records that are poisoned, the resulting connection list may point to attacker nodes. Additionally, long-lived HTTP clients or connection pools in Axum can retain poisoned entries for extended periods, amplifying impact. The vulnerability is not in Axum or CockroachDB per se, but in the shared reliance on external DNS infrastructure without additional verification. Mitigation requires controlling the resolution path, validating server identity, and reducing cache lifetime for critical endpoints.
Cockroachdb-Specific Remediation in Axum — concrete code fixes
Remediation focuses on ensuring that Axum-based services validate DNS and server identity when connecting to CockroachDB. Use explicit IPs or tightly controlled hostnames, enforce TLS with pinned certificates, and avoid reliance on system resolver caching for critical endpoints. The following examples assume you are using rustls and the cockroachdb-rs or a generic PostgreSQL driver that supports TLS via rustls.
1. Use IPs or controlled hostnames in configuration
Prefer static IPs or configuration-managed hostnames instead of dynamic DNS lookups. If DNS is necessary, resolve once at startup and inject the resolved IP into your Axum state.
use axum::Router;
use cockroachdb_rs::Client;
use std::net::SocketAddr;
#[tokio::main]
async fn main() {
// Resolve once at startup
let resolved_ip = "10.0.0.5:26257"; // injected via env or config
let client = Client::new(resolved_ip, Some("/path/to/certs")).expect("client init");
let app = Router::new().with_state(client);
// ... routes
}
2. Enforce TLS with certificate pinning
Pin the server certificate or public key to prevent man-in-the-middle via poisoned DNS. Load the expected certificate fingerprint and verify it during TLS handshake.
use axum::Router;
use cockroachdb_rs::Client;
use rustls::{ClientConfig, Certificate, ServerName};
use std::sync::Arc;
fn make_tls_config() -> Arc {
let mut root_store = rustls::RootCertStore::empty();
// Load pinned cert
let cert = std::fs::read("certs/cockroach-ca.crt").expect("cert read");
root_store.add(&Certificate(cert)).expect("add cert");
let config = ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_store)
.with_no_client_auth();
Arc::new(config)
}
#[tokio::main]
async fn main() {
let tls = make_tls_config();
let client = Client::new_with_tls("cockroach.example.com:26257", tls)
.expect("client with tls");
let app = Router::new().with_state(client);
}
3. Validate server identity programmatically
After establishing a connection, verify server-supplied certificates against a known fingerprint or SAN list. This prevents an attacker who poisoned DNS from presenting a valid but unauthorized certificate from another service.
use cockroachdb_rs::Client;
use tokio_rustls::TlsConnector;
use std::net::ToSocketAddrs;
async fn verify_connect() {
let host = "cockroach.example.com";
let addrs: Vec<_> = host.to_socket_addrs().unwrap().collect();
let server_ip = addrs[0].ip(); // Use first resolved addr
let connector = TlsConnector::from(Arc::new(your_rustls_config()));
let stream = connector.connect(ServerName::try_from(host).unwrap(), server_ip).await.unwrap();
// Perform cert verification here, e.g., check fingerprint
}
4. Reduce resolver caching and set timeouts
Configure Tokio’s resolver to short cache durations and set strict timeouts for DNS queries to limit the window of poisoned cache usage.
use tokio::net::TcpStream;
use tokio_util::compat::TokioAsyncReadCompatExt;
let resolver = tokio::net::lookup_host(("cockroach.example.com", 26257)).await.unwrap();
for addr in resolver {
let _stream = TcpStream::connect_timeout(&addr, std::time::Duration::from_secs(2)).await.unwrap();
}