SSL/TLS#
TLS is the encryption-and-authentication layer that wraps almost
every modern protocol (HTTPS, IMAPS, LDAPS, gRPC, MQTT, mail
submission, the lot. SSL is the obsolete name (SSLv3 and earlier are
broken; SSLv2 has been forbidden in browsers since 2011) but the
term survives in tool names (openssl), config keywords
(ssl_certificate), and conversation.
The operator cares about TLS for three reasons: it terminates
somewhere they own (front-end, load balancer, redirector), it tells
them whether traffic is trustworthy (cert chain, hostname, expiry,
issuer), and it occasionally becomes the whole incident (expired
cert at 03:00). Operator-facing TLS: handshake basics, the trust
store, openssl s_client workflows, certificate issuance (Let’s
Encrypt), and the common failure modes.
Handshake#
TLS 1.3 (RFC 8446, the current standard) compresses the handshake into one round trip; TLS 1.2 needs two. Both negotiate cipher suite, authenticate the server, and derive session keys.
sequenceDiagram
participant C as Client
participant S as Server
C->>S: ClientHello<br/>(versions, ciphers, SNI, key share)
S->>C: ServerHello + cert + key share + Finished
C->>S: Finished
Note over C,S: TLS 1.3: 1-RTT handshake, app data flows
C->>S: Application data (encrypted)
S->>C: Application data (encrypted)
What gets verified:
Property |
How |
|---|---|
Server identity |
Certificate signed by a trusted CA, |
Session integrity |
AEAD cipher (AES-GCM, ChaCha20-Poly1305) over every record |
Forward secrecy |
ECDHE / DHE key exchange (mandatory in TLS 1.3) |
Replay resistance |
Sequence numbers in record layer |
The trust store#
A client only trusts a cert if its issuing CA chain ends in a CA already in the local trust store.
Path |
Distro / use |
|---|---|
|
Debian / Ubuntu bundle |
|
RHEL / Fedora bundle |
|
Alpine |
|
Debian: drop |
|
RHEL: drop here, run |
|
Override at runtime (curl, openssl, many libs) |
Inspect a server#
The operator’s Swiss-army knife is openssl s_client:
$ openssl s_client -connect example.com:443 -servername example.com
$ openssl s_client -connect example.com:443 -showcerts # full chain
$ openssl s_client -connect smtp.example.com:25 -starttls smtp
$ openssl s_client -connect imap.example.com:143 -starttls imap
Quick certificate facts:
# subject, issuer, dates
$ openssl s_client -connect host:443 </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates -ext subjectAltName
# fingerprint
$ openssl x509 -in cert.pem -noout -fingerprint -sha256
# decode a PKCS#12 / PFX
$ openssl pkcs12 -in bundle.p12 -nokeys -nodes
Issuing certificates#
Source |
Use |
|---|---|
Public CA |
Trusted by browsers; via ACME (Let’s Encrypt, ZeroSSL, Buypass) or paid CAs (DigiCert, Sectigo). Always domain-validated; OV / EV exist but rarely matter operationally. |
Private CA |
Internal CA for VPN clients, service mesh, mTLS.
|
Self-signed |
Test only; nothing trusts it without manual install. |
ACME (Let’s Encrypt) on a real host:
$ sudo apt install certbot python3-certbot-nginx
$ sudo certbot --nginx -d example.com -d www.example.com
$ sudo certbot renew --dry-run # test the renewal hook
$ sudo systemctl status certbot.timer # auto-renew
Common failure modes#
Symptom |
Likely cause |
|---|---|
|
Renewal didn’t run; check |
hostname mismatch |
Cert |
unknown CA / not trusted |
Intermediate cert missing from chain (use
|
handshake_failure |
Client and server share no common cipher /
TLS version. Check with |
|
Client trust store doesn’t include the issuing
CA root. Update with
|
See also#
HTTP, the protocol HTTPS wraps.
man openssl-s_client,man openssl-x509.testssl.sh and sslyze, comprehensive TLS auditors.
RFC 8446 (TLS 1.3), RFC 5246 (TLS 1.2), RFC 8555 (ACME).