Certificates, Trust & Authentication

What a cert is · Chain of trust · How to get one · Where it lives · Lifecycle · First connection · How auth actually works · mTLS

1 · What is a Cert?
2 · Chain of Trust
3 · How to Get One
4 · Where It Lives
5 · Lifecycle
6 · First Connection
7 · How Auth Works
8 · mTLS & Internal

A certificate is a signed statement: "This public key belongs to api.example.com"

Issued and cryptographically signed by a Certificate Authority your OS already trusts. Click each section below to expand.

Certificate anatomy — click to expand
📋 Subject — who this cert is FOR
Common Nameapi.example.com
SANapi.example.com, *.example.com
OrganisationExample Corp Ltd
SAN is what browsers actually check. CN alone is deprecated. Wildcard *.example.com covers all subdomains.
🔑 Public Key — the server's identity key
AlgorithmECDSA P-256
Key size256 bit
Value04:a3:f1:2e:9c:... (65 bytes)
This public key is shared with everyone. The matching private key never leaves the server. The cert binds this key to the domain above.
🏛️ Issuer — who SIGNED this cert
Issuer CNLet's Encrypt R10
Signature algECDSA with SHA-384
Signature3046:02:21:00:f4:... (71 bytes)
Let's Encrypt used its private key to sign a hash of this cert. Anyone can verify using Let's Encrypt's public key.
📅 Validity Period
Not Before2024-01-01 00:00:00 UTC
Not After2024-04-01 00:00:00 UTC
Duration90 days (Let's Encrypt)
Short TTL is intentional — limits damage if private key stolen. Certbot renews automatically at day 60.
🔒 Extensions
Ext Key UsageTLS Web Server Auth
CA:FALSECannot sign other certs
OCSP URLhttp://r10.o.lencr.org
What a cert is and is NOT
❌ NOT a password or secret
The certificate itself is completely public. You can download any website's cert:
openssl s_client -connect google.com:443 < /dev/null

The secret is the private key, which never leaves the server disk.
❌ NOT about encryption directly
The cert proves identity. Actual encryption uses session keys derived during the handshake. Without identity verification you'd be encrypting to an unknown party — a MITM could intercept.
✓ What it actually does
Binds a domain name to a public key, with a CA vouching for it. Client uses the public key to verify the server's identity during the TLS handshake. That's the whole job.
🛂 The Passport Analogy
A cert is like a passport. It says "this server is api.example.com" and is stamped by a trusted government (CA).

You trust the passport because you trust the government that issued it — you didn't need to know the server before.

The private key is like the passport holder's face — only they have it, and it matches the photo (public key).

Your OS ships with ~150 pre-installed Root CA certs. Every website's cert traces back to one of them. You never manually trust each site — you trust the root, which vouches for intermediates, which vouch for leaf certs.

Step 0 / 5
ROOT CA
🏛️ ISRG Root X1
Self-signed — trusts itself. Operated by Internet Security Research Group. Pre-installed in your OS at manufacture time. Never directly issues leaf certs — too valuable to expose online.

Validity: 2015 → 2035
▲ signed by
INTERMEDIATE
🔗 Let's Encrypt R10
Signed by ISRG Root X1. This is the CA that actually issues leaf certs — online, running the ACME API. If compromised, root CA can revoke this intermediate without touching the root.

Validity: 2023 → 2026
▲ signed by
LEAF CERT
📄 api.example.com
Signed by Let's Encrypt R10. What the server sends during TLS handshake. Tied to your domain. Short-lived: 90 days. Generated from your CSR.

Validity: 2024-01-01 → 2024-04-01
Verification log
Press Next to walk through browser verification...
How signing works — the math
cert content→ SHA-256 →hash
hash+ CA private key →signature
Browser verifies:
signature+ CA public key →H'
H'== SHA-256(cert) ?✓ VALID
If hashes match, cert was definitely signed by that CA's private key — and nobody tampered with it since. Tampering changes the hash, breaking the signature.

Three ways to get a cert. All involve proving you control the domain, then getting a CA to sign your public key. Let's Encrypt automates the whole thing.

Step 0 / 8
Let's Encrypt ACME flow — most common
🖩
Your Server
certbot running
No cert yet
🏛️
Let's Encrypt
ACME API
CA online
🌐
DNS / HTTP
Challenge target
Unchallenged
ACME issuance flow
Press Next Step to walk through cert issuance...
3 ways to get a cert
MethodProcessWhen to use
Let's Encrypt
(ACME)
certbot auto-renews every 60d. Domain proven via HTTP or DNS challenge. Free.All public servers. Default choice.
Paid CABuy cert. Submit CSR. CA validates organisation. Manual.EV/OV certs where org validation required.
Self-signed /
Internal CA
You are the CA. Generate root, sign leaf certs. Install root on clients manually.K8s internal mTLS, homelabs. Never public.
What a CSR is and how to generate one
# Step 1: Generate private key (NEVER share this) openssl ecparam -name prime256v1 -genkey -out server.key # Step 2: Generate CSR — contains public key + domain # This is what you SEND to the CA — not the private key openssl req -new -key server.key -out server.csr \ -subj "/CN=api.example.com/O=Example/C=GB" # Step 3: CA signs CSR → returns your cert # Let's Encrypt / certbot automates all 3 steps: certbot certonly --nginx -d api.example.com

Yes — you (or certbot/cert-manager) manually place cert files in specific directories. The server is configured to read them on startup. Here is exactly where everything goes and what each file is.

Linux / nginx
# certbot places files here automatically: /etc/letsencrypt/live/api.example.com/ fullchain.pem ← cert + intermediates privkey.pem ← private key (600 perms) cert.pem ← leaf cert only chain.pem ← intermediates only # nginx reads them: server { listen 443 ssl; ssl_certificate /etc/letsencrypt/.../fullchain.pem; ssl_certificate_key /etc/letsencrypt/.../privkey.pem; } ⚠ Always use fullchain.pem # cert.pem alone = broken chain in browser
Kubernetes (cert-manager)
# cert-manager auto-creates K8s Secret # Or manually from files: kubectl create secret tls api-tls \ --cert=fullchain.pem \ --key=privkey.pem # Secret stored in etcd (encrypted at rest) # Reference in Ingress: apiVersion: networking.k8s.io/v1 kind: Ingress spec: tls: - hosts: [api.example.com] secretName: api-tls # cert-manager ClusterIssuer: # Watches for Certificate resources # Calls Let's Encrypt ACME # Renews automatically
Cloudflare (your setup)
# Edge cert: Cloudflare manages fully Edge cert: auto-managed ✓ Renewal: auto ✓ Install: zero steps ✓ # Origin cert (Cloudflare → your server): # Option A: Let's Encrypt on origin # Option B: Cloudflare Origin Cert Dashboard → SSL/TLS → Origin Server → Create Certificate → Download # Origin cert valid 15 years # Only trusted by Cloudflare edge # Browsers never see origin cert # (they terminate at edge)
K3s homelab approach
cert-manager + Let's Encrypt + Cloudflare DNS-01 challenge. cert-manager handles CSR, renewal, and K8s Secret injection. Set up once — never touch again.
Client trust stores — where pre-installed root CAs live
PlatformWhere roots are storedCan you add custom roots?
macOSKeychain Access → System RootsYes — drag cert into Keychain → set to Always Trust
Linux/etc/ssl/certs/ — /usr/share/ca-certificates/Yes — copy to /usr/local/share/ca-certificates/ → update-ca-certificates
Windowscertmgr.msc → Trusted Root CAsYes — certmgr or Group Policy
iOSSettings → General → About → Certificate Trust SettingsYes — install profile via Settings → General → VPN and Device Management
Kubernetes pods/etc/ssl/certs/ on node (inherited by pods)Yes — inject custom CA via ConfigMap → mount into pod, or cert-manager CA injector

The full life of a certificate — from key generation to revocation. If automation is correct, you never manually touch any of this.

🔑
Key Generation
certbot generates ECDSA private key. Stored locally. Never transmitted.
📝
CSR Created
Public key + domain info packaged into CSR. Sent to Let's Encrypt.
🧪
Domain Validated
CA proves you control the domain via HTTP or DNS-01 challenge.
✍️
CA Signs Cert
CA hashes CSR, encrypts hash with CA private key → signature appended.
📦
Deployed
cert + key placed on server or in K8s Secret. nginx/Ingress configured.
🔄
Auto-Renewal (day 60)
certbot/cert-manager re-runs steps 1–4. Old cert stays active until swap.
⚠️
Expiry (day 90)
If renewal failed — browsers reject cert instantly. Monitor at 30d + 7d.
🗑️
Revocation
Key compromised → CA marks cert revoked in OCSP. Browsers check before trusting.
Renewal commands
# certbot installs a systemd timer automatically systemctl status certbot.timer # Runs twice daily — renews if < 30d to expiry # Test that renewal works (dry run): certbot renew --dry-run # Force-renew now: certbot renew --force-renewal # Check expiry on live server: openssl s_client -connect api.example.com:443 \ 2>/dev/null | openssl x509 -noout -dates # Revoke if key compromised: certbot revoke --cert-path /etc/letsencrypt/live/.../cert.pem
Expiry monitoring runbook
Days leftActionSeverity
90 → 31Nothing. certbot will handle renewal automatically.OK
30Alert fires. Investigate if certbot hasn't renewed yet.WARN
7Page on-call. Something broken — manual intervention likely needed.CRITICAL
0Site down. Emergency: certbot renew --force-renewal + nginx reload.INCIDENT
Key leakedcertbot revoke immediately. Re-issue with fresh key. Rotate all secrets that used this connection.INCIDENT

The key question: how does a client trust a server it has never spoken to before?

Answer: trust is pre-established through the OS trust store. The client trusts its OS manufacturer → who vetted the Root CAs → who verified domain ownership → who signed the server's cert. No prior relationship with the server needed.

Step 0 / 7
🖥️
Client
~150 root CAs
pre-installed in OS
Has OS trust store
🖩
Server
Has cert + key
Never met client
Has cert+key
🏛️
Root CA
ISRG Root X1
Pre-installed
Pre-trusted in OS
First connection trace
Press Next to trace the very first connection...
❓ The Bootstrap Problem
If you've never talked to a server before, how can you trust its cert? You'd need to already trust it to verify it — a chicken-and-egg problem.
✓ The Solution: Pre-installed Roots
Your OS manufacturer (Apple, Google, Microsoft) ships trusted Root CA certs. They went through rigorous audits. You trust your OS maker → who trusts these CAs → so you transitively trust any cert they signed.
⚠ MITM Attempt
An attacker intercepts traffic and presents their own cert for api.example.com. Client checks: is this cert signed by a trusted CA? Answer: no — attacker doesn't have Let's Encrypt's private key. Connection rejected.
🏢 Corporate MITM (intentional)
Your employer can install a corporate Root CA on your work laptop. Your browser trusts it. The company generates fake certs for any domain and decrypts all your HTTPS traffic. This is why work laptops should never be used for personal banking.

Authentication is proving possession of the private key — without ever transmitting it. The server signs a hash of the handshake transcript. Client verifies with the cert's public key. Simple and elegant.

Step 0 / 8
🖥️
Client
Wants to verify
server identity
Verifying...
🖩
Server
Has cert + private key
Must prove identity
Ready to prove
Authentication steps
Press Next to see how identity proof works...
The core cryptographic proof
handshake transcript→ SHA-256 →hash H
hash H+ server private key →signature S
S+ cert public key →H'
H'==H?✓ IDENTITY PROVEN
Only the entity with the private key could produce S. No private key was transmitted. Math proves possession.
Why MITM cannot fake this
Attacker has the cert (it's public). But they do not have the server's private key.

When the client asks "sign this handshake hash to prove you're the real server", the attacker cannot produce a valid signature S.

If they substitute their own cert, it won't be signed by a trusted CA → rejected.

The private key never leaves the server disk. That's the entire security model.

In regular TLS only the server authenticates. In mTLS both sides do. Used inside Kubernetes so services can't impersonate each other even if the cluster network is compromised.

Step 0 / 7
⚙️
Service A
user-service
Client cert
Has cert
⚙️
Service B
order-service
Server cert
Has cert
🔐
Citadel
Istio internal CA
Signs both
Issues 24h certs
mTLS flow
Press Next to see mutual auth between two pods...
How internal certs are issued
Istio Citadel is an internal CA. On cluster startup it generates a root cert. On pod startup it issues each pod a short-lived (24h) cert with a SPIFFE identity. All pods trust the same internal root. Certs auto-rotate — app code never touches any of it.
What SPIFFE ID looks like
spiffe://cluster.local/ns/default/sa/user-service

Encodes: cluster, namespace, service account. Gives each pod a cryptographic identity. AuthorizationPolicy lets you say "only user-service may call order-service".
Regular TLS vs mTLS
AspectTLSmTLS
Server auth
Client auth✗ anonymous✓ cert required
Client needs certNoYes
App code changes?NoNo — Envoy sidecar handles it
Use casePublic webService mesh, zero-trust