fix(fifa17-tls): carry the advertised IP in the roster/redirector cert SAN

The FUT hub failed to load with "An error occurred downloading the FUT Squad
Update" because the client dials the roster (https://<advertise>:8081) and the
redirector BY IP, while the served certificate carried DNS SANs only
(winter15.gosredirector.ea.com + wildcards). The client aborts that handshake with
fatal certificate_unknown. Root cause and evidence in
docs/FIFA17_FUT_SQUAD_UPDATE_TLS.md (commit 082246c): a wire capture shows the client
offering TLS1.2 with RSA suites, the server selecting them, then rejecting the cert —
and autopatch demonstrably patched both ProtoSSL gates in that process, so this
validation path is NOT one of the two the client-side patch covers. The SAN is the fix.

Three generators produced the cert and none put the advertised IP in the SAN:

* docker entrypoint.sh — the production path. The advertised IP is a RUNTIME value
  (OPENFUT_ADVERTISE), unknown at image-build time, so the cert is now reconciled at
  startup: reissued with IP:$ADV,IP:127.0.0.1 in the SAN only when the current cert
  lacks it. That makes a restart reuse the same cert (no per-start fingerprint churn,
  which would otherwise recreate the Aug-13 surprise) and self-heal if $ADV changes.
* Dockerfile — installs openssl unconditionally so the entrypoint can reissue at
  runtime (previously it was dropped with the apt lists), and bakes a loopback-IP
  baseline cert so a plain `docker build` still yields a usable image.
* openfut-fut.sh — the local orchestrator. ensure_cert now defaults the SAN IP to this
  host's primary LAN IP (OPENFUT_ADVERTISE overrides) and reissues when the cert lacks
  it, instead of only generating when the file is absent.

Verified without the client, which is the strongest evidence obtainable here: a
verifying TLS client checking the cert BY IP rejects the old DNS-only cert ("IP address
mismatch, certificate is not valid for '10.10.0.120'") and accepts the new
IP-bearing cert; and the entrypoint reconcile is idempotent end to end — an old cert is
reissued to carry IP:$ADV, a simulated restart leaves the fingerprint unchanged, and
the final SAN carries both the advertised and loopback IPs.

Live confirmation needs the production container rebuilt with OPENFUT_ADVERTISE set
(operator-gated); production is otherwise untouched.

entrypoint.sh carries unrelated pre-existing uncommitted work (env-based component
selection) that is not on any branch; only the cert-reconcile block is committed here,
and that work is left intact in the working tree.
This commit is contained in:
funman300
2026-08-18 15:57:19 +00:00
parent 750d6c2e18
commit fbc0da2a1b
3 changed files with 53 additions and 10 deletions
+18 -3
View File
@@ -32,11 +32,26 @@ c() { printf ' %s\n' "$*"; }
up() { ss -tlnp 2>/dev/null | grep -q ":$1 "; }
ensure_cert() {
[ -s "$CERT" ] && [ -s "$KEY" ] && return 0
echo "[*] generating self-signed TLS cert (redirector MITM; ProtoSSL verify is patched)"
# The cert MUST carry the address the client dials in its SAN, or the roster
# HTTPS handshake is rejected with fatal certificate_unknown and the FUT hub
# fails to load (docs/FIFA17_FUT_SQUAD_UPDATE_TLS.md): the client dials the
# roster/redirector by IP and that path validates the SAN against it. Default to
# this host's primary LAN IP so a client on another machine works;
# OPENFUT_ADVERTISE overrides. Reissue when absent OR when the current cert lacks
# that IP, so this self-heals rather than serving a stale DNS-only cert.
local adv ip_sans regen=0
adv="${OPENFUT_ADVERTISE:-$(ip route get 1.1.1.1 2>/dev/null | awk '{print $7; exit}')}"
ip_sans="IP:127.0.0.1"; [ -n "$adv" ] && ip_sans="IP:$adv,IP:127.0.0.1"
if [ ! -s "$CERT" ] || [ ! -s "$KEY" ]; then
regen=1
elif [ -n "$adv" ] && ! openssl x509 -in "$CERT" -noout -ext subjectAltName 2>/dev/null | grep -qF "IP Address:$adv"; then
regen=1
fi
[ "$regen" = 0 ] && return 0
echo "[*] issuing self-signed TLS cert (SAN includes $ip_sans; redirector MITM; ProtoSSL verify is patched)"
openssl req -x509 -newkey rsa:2048 -nodes -keyout "$KEY" -out "$CERT" -days 3650 \
-subj "/CN=winter15.gosredirector.ea.com" \
-addext "subjectAltName=DNS:winter15.gosredirector.ea.com,DNS:*.gosredirector.ea.com,DNS:*.ea.com,IP:127.0.0.1" \
-addext "subjectAltName=DNS:winter15.gosredirector.ea.com,DNS:*.gosredirector.ea.com,DNS:*.ea.com,$ip_sans" \
>/dev/null 2>&1
}