tls_check: an opt-in TLS hardening check, with a badge on the H1 composite - #1296
Merged
Conversation
MDA2AV
force-pushed
the
feat/tls-section
branch
from
August 24, 2026 14:33
17da565 to
3443a3b
Compare
MDA2AV
force-pushed
the
feat/tls-validation
branch
from
August 24, 2026 14:56
4b2d714 to
d071aec
Compare
…osite
Opted into with a meta.json field:
"tls_check": true
It needs a TLS listener on :9000 reading /certs-tls, a directory mounted for
that entry alone. Nothing is measured; passing earns a badge on the HTTP/1.1
composite, and only there -- the check covers :8081-class HTTP/1.1 TLS, so a
badge earned on it must not follow the entry into h2 and h3 views it says
nothing about.
certificate rotation the pair at /certs-tls is replaced under a
running server; the new certificate must be
served without a restart and still answer.
Measured first: caddy, bun and h2o-mruby all
keep serving the old one.
rotation keeps serving 30 requests across the swap, all must succeed
SNI handshakes with a server name and without
session resumption reported, not required
close_notify closed at the TLS layer, not just the socket
vulnerability suite testssl.sh -U: Heartbleed, ROBOT, POODLE,
SWEET32, LUCKY13 and 14 more. HIGH or CRITICAL
fails.
the shared TLS checks certificate identity, TLS 1.3, AEAD, ALPN,
obsolete protocols and weak ciphers
The dedicated port and private directory are the point of the design: the
check rotates certificates under a running server, and doing that to the
shared /certs would move the ground under json-tls, static-tls and every h2
profile in the same run. /certs is verified byte identical across a run that
rotates twice.
aspnet-minimal opts in and implements it. RotatingCertificate re-reads the
pair when its mtime moves, behind Kestrel's ServerCertificateSelector, which
runs per handshake. 114 passed, 0 failed; rotation lands in 1s and 30/30
requests survive the swap.
MDA2AV
force-pushed
the
feat/tls-section
branch
from
August 24, 2026 15:53
3443a3b to
2b71bca
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A new
tlstest: validation-only, opt-in, HTTP/1.1 only. Nothing is measured and no score moves — it is a hardening bar an entry chooses to be held to, and passing it earns a second badge on the H1 composite.Built on top of #1292, which it reuses.
Why opt-in
Almost every check needs the entry to have done something deliberate. Binding a certificate once at startup — what nearly every entry does today — fails the first one. Measured before building any of this:
The checks
/certsis replaced under a running server; the new cert must be served without a restart and still answer-U— Heartbleed, ROBOT, POODLE, SWEET32, LUCKY13 and 14 more, ~30s. Any HIGH/CRITICAL failsRotation is the centrepiece: a certificate is renewed roughly every 60 days in production, and a server needing a restart to pick one up is a weaker server. The usual way to pass is a per-handshake callback — Kestrel's
ServerCertificateSelector, Go'sGetCertificate, rustls'ResolvesServerCert.Registration
A new
VALIDATION_ONLYtable inprofiles.sh, notPROFILES.validate.shaccepts it intests; the benchmark harness and leaderboard catalog ignore it, since there is no load generator behind it.aspnet-minimal opts in and implements it
RotatingCertificatere-reads the PEM pair when its mtime moves, behindServerCertificateSelector, which Kestrel calls per handshake. The mtime check is throttled to 1s — a stat is cheap next to a handshake but not next to a resumed one, and a second of staleness is nothing against a 60-day renewal.113 passed, section clean.
The badge
Shown only on the H1 composite (
view === 'composite' && scope === 'h1') — the section covers :8081, so a badge earned there must not follow the entry into h2/h3 views it says nothing about. Verified across all four view combinations.Two details worth stating
Each verdict answers only for its own checks. aspnet-minimal fails an unrelated intermittent
/fortunesassertion in the same run, and neither badge depends on it — a flaky check elsewhere says nothing about whether an entry rotates a certificate.Certificates are restored on the way out, including when a check fails midway, and the replacement carries the mode of the pair it replaces. A 0600 file a non-root container cannot read looks exactly like a server that ignored the rotation — the same trap that produced false failures in #1272.