BitBootcampBitBootcamp ← all explainers 🔄 Review ⚙ Settings

Drill 77 — The Certificate That Expired at Midnight

Theme: Networking & DNS · Format: 20-min drill · Difficulty: core

The story

A trading platform runs an internal settlement-api that every other service calls over mutual TLS: the ledger, the risk engine, the reconciliation batch, the customer-statement generator. It is not internet-facing, so nobody thinks of it as "a website with a certificate." It is just an address other services trust. It has served traffic without drama for two years.

The certificate on that endpoint was issued for 825 days by the internal certificate authority, back when the service was stood up. Renewal was a manual step — a calendar reminder on an engineer who has since changed teams — and the reminder went with them. There is no expiry monitoring on internal certs; the platform's uptime checks ping the TCP port and confirm something is listening, which it always is.

At exactly 00:00:00, the certificate's notAfter timestamp passes. Nothing crashes. No process restarts, no error is logged by the API itself — it is happily holding a port open with a certificate that is now, by the clock, invalid. But the next client that opens a connection completes the TCP handshake, receives the certificate, checks the expiry, and aborts the TLS handshake with certificate has expired. Then the next client. Then all of them. Within seconds every caller of settlement-api is failing at the same instant, retrying, and failing again — a clean, total, simultaneous outage of a service that "was up the whole time."

The overnight settlement run stalls. The on-call for the ledger is paged for connection errors and starts debugging the ledger. The port is open. The host is healthy. The logs on the API side say nothing is wrong.

⏸ Stop here and ask the room: the server never crashed, the port stayed open, and the host was healthy the whole time. So why did every single client fail at the exact same second — and why did it take hours to find?

Run it — facilitator script

Timing for a 20-minute session: 2 min story · 10 min staged questions · 3 min reveal · 5 min transfer.

Why did every client fail simultaneously, instead of degrading gradually?

Why didn't any monitor catch it — the port was being checked?

Certs expire on a known date — why is this a monitoring problem and not a calendar problem?

The reveal

Two hours in, someone runs openssl s_client -connect settlement-api:8443 by hand and sees Verify return code: 10 (certificate has expired). The cert expired at midnight; the manual renewal was owned by an engineer who left. The immediate fix is a five-minute reissue — but the lesson is that a total, all-clients-at-once outage sat undiagnosed for two hours because nothing watched the one number that was knowable weeks in advance. The team added expiry monitoring on every cert (paging at 30, 14, and 7 days out), moved renewal to automated ACME issuance via cert-manager, built a cert inventory with a named owner per cert, and cut certificate lifetimes to days so renewal has to be automated — a manual process could never keep up.

Why this happens — the mechanics

A TLS certificate carries a hard notAfter timestamp, and validity is checked by the client during the handshake, not by the server. That inverts the usual failure geometry: the server that owns the expired cert has no error to log — it offers the cert it has and the far end walks away. So the one party that knows which cert it is serving emits no signal, while every party that fails has only a generic "handshake failed" to go on. The failure is also perfectly synchronized: unlike disk-fills or memory leaks that degrade on a curve, expiry is a single shared instant, so a fleet of clients transitions from 100% success to 100% failure with no ramp to alert on partway down.

The organizational root is a seam. An internal, non-web service doesn't feel like "a certificate you have to renew," so it falls outside the web-team's cert playbook, and its renewal becomes a private calendar entry rather than a monitored, owned, automated process. Best practice — mutual TLS on internal traffic — was applied without the matching signal, and the expiry date, though machine-readable and known years in advance, was watched by nobody. This is the same green-console-versus-reality gap as the silent-failure drills: the fix is to make the guarantee structural (automated renewal, short lifetimes) and to monitor the leading indicator (days-until-expiry) instead of trusting a human to remember.

Diagrams

Problem — how the failure unfolds:

sequenceDiagram
    autonumber
    participant C as Client svc
    participant API as settlement-api
    participant M as Monitors
    participant Ops as On-call
    Note over API: Cert notAfter =<br/>00:00, valid until then
    C->>API: mutual TLS call at 2359
    API-->>C: 200 OK
    Note over API: 00:00 - cert expires<br/>server logs nothing
    C--xAPI: handshake - cert expired
    Note over M: TCP port check -<br/>still "listening", green
    Note over C,API: Every client fails at once<br/>100% to 0%, no ramp
    Note over Ops: Found 2h later by hand<br/>openssl, not a monitor

Solution — where the guardrails go:

flowchart TD
    A["Cert issued with<br/>notAfter date"] --> B["Cert inventory:<br/>every cert has an owner"]
    B --> C["Expiry monitor:<br/>days-until-expiry SLI"]
    C -.-> AL["Alarm 30 / 14 / 7<br/>days before expiry"]
    A --> D["Automated renewal<br/>ACME / cert-manager"]
    D --> E["Short-lived certs<br/>force renewal to automate"]
    E -.-> AL
    AL --> F["Renewed before expiry<br/>no outage"]

What good looks like

The lesson

An expiry date is a known-in-advance, machine-readable fact — so a cert outage is never a surprise, it's an unmonitored countdown. Watch days-until-expiry and automate renewal so the guarantee doesn't depend on a human remembering; a health check that proves a port is open proves nothing about whether the handshake still succeeds.

Named concepts: TLS certificate expiry, certificate-expiry monitoring, automated renewal (ACME), short-lived certificates.

Real-world echoes

Transfer

Which certificates in your stack — especially internal, service-to-service, and CA-issued ones that don't feel like "websites" — would take you down the instant they expire, and can you name today how many days each has left? "We'd get an alert" only counts if you can point at the monitor.

Make it a ticket: build a certificate inventory with an owner per cert, add a days_until_expiry monitor that pages at 30/14/7 days plus a handshake-completing synthetic probe, and move at least one critical internal cert onto automated ACME renewal this sprint.

Related