Files
caddy/modules
1fed032234 caddyhttp: shield specific hostnames from a covering wildcard's client auth (#7920)
* caddyhttp: shield specific hostnames from a covering wildcard's client auth (@sillyzir)

A connection policy for a wildcard hostname (e.g. *.example.com with
client_auth) is first-match by SNI, so it also applied client
authentication to more specific hostnames served by their own site
blocks (public.example.com) — sites that never asked for mTLS.

Two cases produce the shielding empty policy that fixes this:

  - site blocks whose TLS config yields a connection policy with no
    settings (previously discarded as having no effect);
  - site blocks with no TLS connection policy at all — the reported
    case — for which an empty policy is now synthesized.

Either way the empty policy is hoisted directly above the first
client-auth-bearing policy whose wildcard SNI covers the hostname, so
first-match shields it from the client-auth requirement.

Deliberately scoped to client authentication: other wildcard policy
settings, such as certificate selection, are ones a covered hostname
generally WANTS to inherit (see tls_automation_wildcard_shadowing);
sni matchers that fail to decode emit an adapt warning instead of
being silently skipped.

Fixes #7860

* caddyhttp: shield preserves the wildcard policy's other settings (@sillyzir)

The hoisted shield was an empty policy, and connection policies are
first-match: it lifted the client-auth requirement but also suppressed
every other setting the covering wildcard policy carried (certificate
selection, protocol bounds, ALPN). Hoist a copy of the covering policy
with only client_authentication removed instead, so the shielded
hostname keeps inheriting the rest.

The new adapt test gives the wildcard policy protocols and alpn in
addition to client_auth and asserts the shield carries both while
dropping only client authentication; the existing test (client_auth
only) is unchanged, which is exactly why it could not catch this.

* caddyhttp: shield each covering wildcard policy separately (@sillyzir)

The hoisting loop stopped at the first client-auth policy covering any of a
site block's hostnames and gave the shield that block's whole SNI matcher, so
hostnames covered by a *different* wildcard matched it too. Because connection
policies are first-match, those hostnames then took the wrong policy's
settings and lost their own client-authentication requirement entirely.

Map each hostname to the first covering policy individually and hoist one
shield per covering policy, matching only the hostnames it covers. The
existing policies' wildcard SNI names are decoded once up front, and the
shields are inserted back to front so an insertion cannot shift the index of a
covering policy still to be shielded.

* chore: fumpt (@steadytao)
* chore: fix master lint (@steadytao)

---------

Co-authored-by: SillyZir <269283839+SillyZir@users.noreply.github.com>
Co-authored-by: Zen Dodd <mail@steadytao.com>
2026-08-12 15:09:58 +10:00
..