Inspect and tighten a TLS service

applied · 40 min · Objective 3.5

Task

Stand up a TLS service, inspect it from the outside the way an auditor would, find the three failures that pass in a browser and fail everywhere else, and tighten the protocol and cipher configuration. Then confirm an old client is correctly refused.

Steps

  1. Generate a self-signed certificate and configure nginx to use it. Confirm with openssl s_client -connect localhost:443 that the connection is encrypted, and state what the self-signed certificate does NOT provide.
  2. Create the incomplete-chain failure deliberately: install a leaf certificate without its intermediate. Show that a browser you have visited the CA from works, while curl reports "unable to get local issuer certificate".
  3. Create the SAN failure: issue a certificate with the hostname only in the Common Name, not the SAN. Confirm modern clients reject it while openssl x509 -text still shows the expected name.
  4. Create the key-mismatch failure: install a certificate alongside the wrong private key. Confirm nginx refuses to start, and diagnose it by comparing the two modulus hashes.
  5. Tighten the configuration to TLS 1.2 and 1.3 only, with a modern cipher list. Confirm with openssl s_client -tls1_1 that an old protocol is now refused.
  6. Enumerate what the server actually accepts with nmap --script ssl-enum-ciphers -p 443 localhost and confirm no weak suites remain.
  7. On RHEL, set this system-wide with update-crypto-policies --set FUTURE and observe that it affects every TLS service at once.

Verify

echo | openssl s_client -connect localhost:443 2>/dev/null | openssl x509 -noout -dates
openssl s_client -connect localhost:443 -tls1_1 </dev/null 2>&1 | grep -qi 'no protocols available\|handshake failure' && echo "TLS 1.1 refused"
openssl x509 -noout -modulus -in /etc/pki/tls/certs/lab.crt | openssl md5
openssl rsa -noout -modulus -in /etc/pki/tls/private/lab.key | openssl md5
nmap --script ssl-enum-ciphers -p 443 localhost 2>/dev/null | grep -ci 'sslv3\|tlsv1.0\|tlsv1.1'   # 0

The two modulus hashes must match, and the old-protocol connection must be refused. The nmap count of weak protocols must be zero. Each of the three browser-passes-curl-fails cases in steps 2 to 4 is one an auditor meets weekly.

Notes

The incomplete chain is the nastiest of the three because the obvious test -- open it in a browser -- passes, since browsers cache intermediates from previous sites. curl and every server-to-server call do not. Always test with something other than a browser.