Skip to content

Mutual TLS (mTLS)

Overview

Mutual TLS (mTLS) enforces two-way authentication: the server presents its certificate to the client as usual, and the client must also present a certificate signed by a CA that the server trusts. Connections that do not present a valid client certificate are rejected at the TLS handshake layer, before any HTTP traffic is exchanged.

Enabling mTLS

Set RWS_CONFIG_TLS_CLIENT_CA_FILE to the path of a PEM file containing the CA certificate (or certificate chain) whose signatures you trust:

Terminal window
export RWS_CONFIG_TLS_CLIENT_CA_FILE=/etc/ssl/client-ca.pem
rws.config.toml
tls_client_ca_file = "/etc/ssl/client-ca.pem"

No other configuration is needed. When this variable is set, create_tls_acceptor_from_vhosts() builds a WebPkiClientVerifier and attaches it to the rustls::ServerConfig. The same CA applies to both the HTTPS (TCP/TLS) listener and the QUIC listener used for HTTP/3.

How the verifier is built

// src/tls/mod.rs (simplified)
let verifier = WebPkiClientVerifier::builder(Arc::new(root_store))
.build()
.unwrap();
ServerConfig::builder()
.with_client_cert_verifier(verifier)
.with_cert_resolver(Arc::new(resolver))

The CA certificate is loaded into a RootCertStore. Every TLS ClientHello that follows must include a certificate chain rooted in that store.

Generating test client certificates

Create a self-signed CA and a client certificate signed by it:

Terminal window
# 1. Generate a CA key and self-signed certificate
openssl req -x509 -newkey rsa:4096 -keyout ca-key.pem -out ca.pem \
-days 3650 -nodes -subj "/CN=MyTestCA"
# 2. Generate a client key and certificate signing request
openssl req -newkey rsa:2048 -keyout client-key.pem -out client.csr \
-nodes -subj "/CN=test-client"
# 3. Sign the client certificate with the CA
openssl x509 -req -in client.csr -CA ca.pem -CAkey ca-key.pem \
-CAcreateserial -out client.pem -days 365

Testing with curl

Terminal window
curl --cert client.pem --key client-key.pem \
--cacert cert.pem \
https://localhost:7878/healthz

--cacert cert.pem tells curl to trust the server’s self-signed certificate. --cert / --key provide the client certificate and key that the server validates against client-ca.pem.

Scope

mTLS applies globally to all connections on the TLS port. There is no per-route or per-virtual-host granularity at the TLS layer; use application middleware (e.g., JwtLayer or a custom Middleware) for finer-grained access control within a single TLS session.

Configuration reference

VariableConfig keyDescription
RWS_CONFIG_TLS_CLIENT_CA_FILEtls_client_ca_filePath to the CA PEM file whose signatures are trusted for client certificates