Table of Contents

Mutual TLS

Changelog

  • 2024-09-26: Init for nginx v1.24.0, OpenSSL v3.0.13

Comparison with VPN

mTLS requires clients to present a client certificate to authenticate usage. The mTLS flow goes roughly like:

  1. Client sends request to server
  2. Server responds with server cert (as usual) and set of accepted client cert
  3. Client validates server cert (as usual) and sends client cert
  4. Server validates client cert and performs authentication flow

Authentication is facilitated by PKI. The subset of cases where this may be undesirable includes:

Where this is desirable includes:

Setup

Assuming the basic web configuration has already been setup, together with certificates. Then the main changes are:

server {
    ssl_client_certificate <PATH_TO_CA_CERT>;
    ssl_verify_client optional;
    if ($ssl_client_verify != SUCCESS) {
        ...  # reject clients
    }
    ...
}

If no special behaviour based on the validity of the client certificate is required (processed based on the return code of $ssl_client_verify), then ssl_verify_client can be set to "on", and the response will be a simple 400 Bad Request.

Note the ssl_trusted_certificate directive is mainly for OSCP stapling, not for hiding the list of accepted client certificates from the client. Using this in tandem with ssl_verify_client without ssl_client_certificate raises a configuration error.

Interplay with other topology

Consider the following additional considerations:

# Create CA
user:~$ easyrsa init-pki  # configure vars, e.g. for ECC
user:~$ easyrsa build-ca
 
# Create client certificate
user:~$ easyrsa gen-req <MY_CLIENT>
user:~$ easyrsa --san=DNS:<MY_DOMAIN> sign-req client <MY_CLIENT>
 
# Verify client certificate, e.g. extended key usage of Client Authentication and SAN
user:~$ cat pki/issued/<MY_CLIENT>.crt
 
# Package into p12 bundle (set export password if desired)
# Note there is no real need to generate the full chain
user:~$ openssl pkcs12 -export \
            -in pki/issued/<MY_CLIENT>.crt \
            -inkey pki/private/<MY_CLIENT>.key \
            -out pki/private/<MY_CLIENT>.p12

Client-side installation

Generally per usual for Linux/Windows.

For iOS devices, the .p12 file needs to be created using the -legacy flag in OpenSSL 3, and then either:

  1. Distributed via HTTPS endpoint, and open in Safari
  2. Distributed via email, and open in the iOS Mail app

So many hurdles to leap through... *internal screaming* Even then, the client certificate is added only to Apple's keychain, so these certificates cannot be used in other browsers unless those apps support client cert importing mechanisms, see answer. *more internal screaming*