Inspect and tighten a TLS service
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
- Generate a self-signed certificate and configure nginx to use it. Confirm with
openssl s_client -connect localhost:443that the connection is encrypted, and state what the self-signed certificate does NOT provide. - 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
curlreports "unable to get local issuer certificate". - 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 -textstill shows the expected name. - 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.
- Tighten the configuration to TLS 1.2 and 1.3 only, with a modern cipher list. Confirm with
openssl s_client -tls1_1that an old protocol is now refused. - Enumerate what the server actually accepts with
nmap --script ssl-enum-ciphers -p 443 localhostand confirm no weak suites remain. - On RHEL, set this system-wide with
update-crypto-policies --set FUTUREand 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.