From 74c4ed9959ad5736f2858f019d7434e18906f9ad Mon Sep 17 00:00:00 2001 From: Marcus Summers Date: Thu, 30 Apr 2026 13:08:36 -0400 Subject: [PATCH] wiki: 2026-04-30 update to ISP SNI filtering article MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-diagnoses today's notes.majorshouse.com outage. Original framing was "ISP filter expanded to include 'notes'" β€” but the actual root cause was a stale A record pointing at 136.54.3.248 (not majorlab's current home IP). Corrects the comparison table to show CNAMEs to apex resolve to 136.56.0.55, and recommends a Cloudflare-proxied CNAME as the durable shape so the apex follows home IP automatically and ISP-level SNI weirdness is bypassed at the same time. Includes the working CF API payload used to flip the record, and an audit checklist for any new *.majorshouse.com subdomain. Co-Authored-By: Claude Opus 4.7 (1M context) --- 05-troubleshooting/isp-sni-filtering-caddy.md | 88 ++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/05-troubleshooting/isp-sni-filtering-caddy.md b/05-troubleshooting/isp-sni-filtering-caddy.md index 8fcd85c..c0a7fb0 100644 --- a/05-troubleshooting/isp-sni-filtering-caddy.md +++ b/05-troubleshooting/isp-sni-filtering-caddy.md @@ -5,7 +5,7 @@ category: general tags: [isp, sni, caddy, tls, dns, cloudflare] status: published created: 2026-04-02 -updated: 2026-04-02 +updated: 2026-04-30 --- # ISP SNI Filtering & Caddy Troubleshooting @@ -29,3 +29,89 @@ notes.majorshouse.com { ``` Once the hostname was changed to one without the "wiki" keyword, the TLS handshake completed successfully. + +--- + +## πŸ” 2026-04-30 Update β€” Stale A Record + Cloudflare Proxy Fix + +The hostname rename held for ~4 weeks. On 2026-04-30 the wiki went down with a TLS handshake failure on `notes.majorshouse.com`. The on-the-spot framing was "ISP filter expanded to include 'notes'" β€” but Cloudflare DNS audit showed a different (and arguably worse) root cause: **the `notes` A record was pointing at `136.54.3.248`, an IP that is not majorlab's current home IP.** Whichever host responds at that address either does not run Caddy or does not know about the `notes.majorshouse.com` SNI, so the TLS handshake was rejected with `internal_error 80`. + +### Re-diagnosis + +```bash +# Cert + Caddy + mkdocs all healthy on majorlab +$ ssh majorlab 'systemctl is-active caddy; ss -tlnp | grep :443' +active +LISTEN 0 4096 *:443 users:(("caddy",pid=1549,fd=7)) + +# Loopback-served TLS works fine β€” cert valid Mar 11 β†’ Jun 9 2026 +$ ssh majorlab 'curl -sS -o /dev/null -w "%{http_code}\n" --resolve notes.majorshouse.com:443:127.0.0.1 https://notes.majorshouse.com/' +200 + +# External TLS handshake gets rejected with internal_error +$ openssl s_client -servername notes.majorshouse.com -connect 136.54.3.248:443 +… SSL alert number 80 (internal_error) … +``` + +### The smoking-gun comparison + +Other `*.majorshouse.com` services worked because they were CNAMEs to the apex, which resolves to majorlab's actual home IP: + +| Subdomain | DNS shape | Final IP | Status | +|---|---|---|---| +| `notes.majorshouse.com` | **A β†’ `136.54.3.248`** (stale) | `136.54.3.248` (wrong host) | ❌ TLS rejected | +| `git.majorshouse.com` | CNAME β†’ `majorshouse.com.` | `136.56.0.55` (majorlab) | βœ… | +| `n8n.majorshouse.com` | CNAME β†’ `majorshouse.com.` | `136.56.0.55` (majorlab) | βœ… | +| `matrix.majorshouse.com` | CNAME β†’ `majorshouse.com.` | `136.56.0.55` (majorlab) | βœ… | + +None of the working subdomains were proxied through Cloudflare (`proxied=false` on all of them); they simply had the right IP. The `notes` A record was the only one pointing somewhere wrong β€” most likely a stale value from a prior ISP / IP change that never got cleaned up. + +### βœ… Fix β€” switch `notes` to a Cloudflare-proxied CNAME + +Rather than just correcting the A record (which would silently break again the next time the home IP changes), the fix is a CNAME to the apex with proxy on. That gives two protections in one move: it always tracks the apex (so home IP changes propagate automatically) and it puts the wiki behind Cloudflare's edge (so any future ISP-side weirdness like the original `wiki` SNI filter is also bypassed). + +```bash +# via Cloudflare API (token from ansible-vault: vault_cloudflare_api_token) +PUT /zones/{ZONE_ID}/dns_records/{NOTES_RECORD_ID} +{ + "type": "CNAME", + "name": "notes.majorshouse.com", + "content": "majorshouse.com", + "ttl": 1, + "proxied": true, + "comment": "switched Aβ†’CNAME proxied to bypass stale IP / ISP SNI filter" +} +``` + +Or via the dashboard: + +1. Cloudflare β†’ `majorshouse.com` zone β†’ DNS β†’ Records +2. Edit the `notes` record: Type `CNAME`, Target `majorshouse.com`, Proxy `Proxied` (orange cloud) +3. Save + +External clients now hit Cloudflare edge IPs (`104.21.x.x` / `172.67.x.x`) which TLS-terminate at the edge and tunnel back to majorlab's apex IP. ACME on majorlab keeps working β€” Cloudflare passes the HTTP-01 challenge through on port 80. Caddy's `notes.majorshouse.com {}` block needs no change. + +Verify (response should show `server: cloudflare` and `via: 1.0 Caddy`): + +```bash +curl -sSI https://notes.majorshouse.com/ +``` + +### Why a Cloudflare-proxied CNAME is the durable shape + +- **Apex follows the home IP automatically.** Update the apex A record once when the ISP changes; every subdomain inherits it without per-record fixes. +- **TLS handshake is offloaded to CF.** Any ISP-level SNI weirdness (the original `wiki` ban; theoretical future bans) becomes irrelevant β€” external clients SNI=`notes.majorshouse.com` to Cloudflare, which the ISP doesn't filter. +- **Free.** Cloudflare's free tier covers proxy + TLS termination. + +### Audit checklist for any home-hosted `*.majorshouse.com` subdomain + +- [ ] DNS record is a **CNAME** to `majorshouse.com.`, not an A record to a literal home IP. +- [ ] Cloudflare proxy (orange cloud, `proxied=true`) enabled on the record β€” at minimum for any subdomain where TLS reachability matters. +- [ ] Caddy entry on majorlab references the public hostname; `reverse_proxy` stays on the localhost port. +- [ ] HTTPS verified from outside the LAN (phone on cellular is sufficient) within the first hour after the change. +- [ ] If an A record is genuinely required (e.g. it must NOT go through CF), document why in the deploy notes for that service. + +### Related + +- [[majwiki-setup-and-pipeline]] β€” full wiki deploy pipeline; the DNS step there should reference this fix +- [[Network-Overview]] β€” fleet IP table