fix(plex): advertise the plex.direct name, not the bare VIP (server vanished from the plex.tv account)
gate / gate (pull_request) Successful in 1m48s
gate / conformance (pull_request) Has been skipped

customConnections held two hand-typed URLs and neither could ever complete, so
Plex had nothing publishable and the server disappeared from the account:

  - https://192.168.2.24:32400 -- the server's cert is
    *.130072f20c334d6c8f63d0dfef87309b.plex.direct with NO IP SAN, so bare-IP
    https fails TLS for every client. Measured: a continuous storm of
    "CERT: incomplete TLS handshake ... certificate unknown" from real LAN
    clients arriving through the plex-l4-tcp VIP.
  - https://plex.home.keithjackson.dev:32400 -- nothing listens; the Gateway
    serves that host on 443 (measured http=000 against 192.168.2.20:32400).

With no completable connection plex.tv published nothing ("Mapped - Not
Published"), Plex fell back to Relay, and clients reported "not authorized".

Fix: advertise <dashed-ip>.<serverhash>.plex.direct -- public DNS that resolves
to the embedded address, so it points at the LAN VIP and matches the wildcard
cert. Verified with full TLS validation (no curl -k): 200 + verify OK, where
the bare-IP form fails to connect.

Declared as ADVERTISE_IP so git owns the value instead of a web form: the
image's 40-plex-first-run writes it into customConnections on every pod start,
outside the claim guard.

Does NOT fix off-LAN access -- the advertised address is RFC1918 and the WAN
tcp/32400 forward remains unverified. ManualPortMappingMode=1 with an absent
port is left as-is (no GitOps path exists for it).

Touches clusters/** -> sensitive paths -> needs the operator's
/approve-override, deliberately not self-posted (CLAUDE.md 1).

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
jakman85andClaude Opus 5 committed 2026-10-04 02:53:01 -05:00
1 parent c95b1c7121
commit 1f4552d409
5 files changed
+226 -9

No files matched your search

+27 -1
View File
@@ -101,7 +101,7 @@ data:
# tier that lists it), crafty (CRAFTY_L4_VIP), apps-wave-0 (nextcloud-db + semaphore-db:
# S3_BACKUP_ENDPOINT, CNPG_BACKUP_PATH; pihole + pihole-dns: PIHOLE_DNS_VIP, LAN_CIDR, plus
# GATEWAY_VIP/UNIFI_L4_VIP/CRAFTY_L4_VIP read BY pihole to author its LAN split-horizon answers;
# plex-remote: PLEX_L4_VIP),
# plex-remote: PLEX_L4_VIP; plex: PLEX_ADVERTISE_URL),
# velero (S3_BACKUP_ENDPOINT, S3_BACKUP_REGION,
# VELERO_BACKUP_BUCKET, VELERO_BACKUP_PREFIX), apps-vms (UNIFI_L4_VIP). A ${TOKEN} in an
# UNSUBSTITUTED path would ship the literal string into the cluster.
@@ -190,6 +190,32 @@ data:
# prod -> 192.168.2.24 (next free after pihole-dns .23 in prod's .20-.30 pool; see clusters/prod)
PLEX_L4_VIP: "192.168.1.57" # Plex: tcp/32400 remote access (NOT the web UI, which is L7)
# PLEX_ADVERTISE_URL — the connection URL Plex publishes to plex.tv ("Custom server access URLs",
# the ADVERTISE_IP env in catalog/plex.yaml, which the image writes into the `customConnections`
# preference on every pod start). Read catalog/plex.yaml's ADVERTISE_IP block for the full why;
# the short version is the only thing that matters when editing this line:
#
# IT MUST BE A `<dashed-ip>.<serverhash>.plex.direct` NAME, NEVER A BARE IP. Plex serves 32400
# with a `*.<serverhash>.plex.direct` wildcard certificate and NO IP SAN, so `https://<ip>:32400`
# fails TLS validation for every client. That exact mistake is what broke this service on
# 2026-10-04: with no completable connection, plex.tv published nothing, the server vanished from
# the account and clients reported "not authorized". The dashed name is public DNS that resolves
# to the embedded address, so it points at the LAN VIP AND matches the cert.
#
# WHY IT IS A WHOLE URL AND NOT BUILT FROM ${PLEX_L4_VIP}: postBuild substitution is plain
# envsubst — it cannot turn 192.168.2.24 into 192-168-2-24 — and `<serverhash>` is this server's
# certificate identity, derivable from nothing in this tree. Re-read it from the live cert
# (`openssl s_client -connect <vip>:32400 | openssl x509 -noout -subject`) if Plex ever reissues.
#
# The dashed IP here MUST track ${PLEX_L4_VIP} above. They are two halves of one address and
# nothing checks that they agree.
#
# dev -> PLACEHOLDER, non-functional. dev is decommissioned and has no claimed Plex server, so
# there is no real hash to put here; the base default only has to be a defined, correctly
# shaped string (StrictPostBuildSubstitutions makes an UNDEFINED var a hard failure).
# prod -> the real 192.168.2.24 + live server hash; see clusters/prod/kustomization.yaml.
PLEX_ADVERTISE_URL: "https://192-168-1-57.0000000000000000000000000000000f.plex.direct:32400"
# The FLAT LAN this cluster's nodes and LB VIPs live on, in CIDR form. Consumed as the ingress
# peer of the PiHole DNS CiliumNetworkPolicy (apps/wave-0/pihole-dns.yaml): a resolver reachable
# from `world` is an open resolver and an amplification reflector, so the allow is scoped to this
+10
View File
@@ -496,6 +496,16 @@ patches:
- op: replace
path: /data/PLEX_L4_VIP
value: "192.168.2.24"
# PLEX_ADVERTISE_URL — the prod VIP above in Plex's `plex.direct` form, which is the ONLY form
# that validates: the server's cert is `*.130072f20c334d6c8f63d0dfef87309b.plex.direct` with no
# IP SAN (read off the live listener 2026-10-04), so a bare `https://192.168.2.24:32400` fails
# TLS for every client — the 2026-10-04 outage. Measured with full verification, no `curl -k`:
# this URL answers /identity with 200 and TLS verify OK; the bare-IP form fails to connect.
# THE DASHED IP MUST MATCH /data/PLEX_L4_VIP ABOVE (192.168.2.24 -> 192-168-2-24); nothing
# enforces that. The hash is server identity, not a secret, and changes only if Plex reissues.
- op: replace
path: /data/PLEX_ADVERTISE_URL
value: "https://192-168-2-24.130072f20c334d6c8f63d0dfef87309b.plex.direct:32400"
# The prod flat LAN. This is the 192.168.2.0/24 segment the SOHO ROUTER at 192.168.2.1 owns —
# NOT pfSense's 192.168.1.0/24, which is a different network and not in the LAN-DNS path.
# It scopes the ingress allow for PiHole's DNS listener to LAN clients only.