diff --git a/fifa17-recon/docker/fifa17-python/Dockerfile b/fifa17-recon/docker/fifa17-python/Dockerfile index 1795178..5218184 100644 --- a/fifa17-recon/docker/fifa17-python/Dockerfile +++ b/fifa17-recon/docker/fifa17-python/Dockerfile @@ -37,17 +37,25 @@ RUN set -eu; \ COPY data/ /app/data/ -# Redirector TLS cert (CN/SAN = winter15.gosredirector.ea.com). ProtoSSL -# cert-verify is patched client-side, so a self-signed cert is fine. The pair is -# git-ignored (*.pem/*.key); regenerate if absent so a fresh checkout builds -# without extra steps. +# Redirector/roster TLS cert (CN/SAN = winter15.gosredirector.ea.com). ProtoSSL +# cert-verify is patched client-side, so a self-signed cert is fine — but the +# client dials the roster and redirector BY IP, and that path still checks the +# SAN against the dialed address (it is NOT covered by the two patched gates), so +# a cert without a matching IP SAN is rejected with fatal certificate_unknown +# (docs/FIFA17_FUT_SQUAD_UPDATE_TLS.md). The advertised LAN IP is a RUNTIME value, +# unknown here, so this bakes only a loopback-IP baseline and the entrypoint +# reissues with IP:$OPENFUT_ADVERTISE at start. +# +# openssl therefore has to remain in the image for the entrypoint, not be dropped +# with the apt lists. The pair is git-ignored (*.pem/*.key); regenerate if absent +# so a fresh checkout builds without extra steps. +RUN apt-get update && apt-get install -y --no-install-recommends openssl && \ + rm -rf /var/lib/apt/lists/* RUN if [ ! -s tools/redir_cert.pem ] || [ ! -s tools/redir_key.pem ]; then \ - apt-get update && apt-get install -y --no-install-recommends openssl && \ openssl req -x509 -newkey rsa:2048 -nodes \ -keyout tools/redir_key.pem -out tools/redir_cert.pem \ -days 3650 -subj "/CN=winter15.gosredirector.ea.com" \ - -addext "subjectAltName=DNS:winter15.gosredirector.ea.com,DNS:*.gosredirector.ea.com,DNS:*.ea.com" && \ - rm -rf /var/lib/apt/lists/*; \ + -addext "subjectAltName=DNS:winter15.gosredirector.ea.com,DNS:*.gosredirector.ea.com,DNS:*.ea.com,IP:127.0.0.1"; \ fi # Bake a dataset manifest so every image is self-identifying. diff --git a/fifa17-recon/docker/fifa17-python/entrypoint.sh b/fifa17-recon/docker/fifa17-python/entrypoint.sh index faf8caf..3ba37a8 100644 --- a/fifa17-recon/docker/fifa17-python/entrypoint.sh +++ b/fifa17-recon/docker/fifa17-python/entrypoint.sh @@ -28,6 +28,26 @@ export POW_CONTENT_ADDR="${POW_CONTENT_ADDR:-$BIND:8080}" echo "[openfut] bind=$BIND advertise=$ADV" +# The TLS cert every responder serves must carry the ADVERTISED IP in its SAN. +# The client dials the roster (:8081) and redirector by that IP, and that path +# validates the cert's SAN against the dialed address — it is NOT covered by the +# two client-side ProtoSSL gates autopatch patches, so a cert lacking IP:$ADV is +# rejected with fatal certificate_unknown and the FUT hub fails with "An error +# occurred downloading the FUT Squad Update" (docs/FIFA17_FUT_SQUAD_UPDATE_TLS.md). +# The advertised IP is unknown at image-build time, so reconcile it here: reissue +# only when the current cert does not already carry it, so a restart reuses the +# same cert (no per-start fingerprint churn) and this self-heals if $ADV changes. +CERT=redir_cert.pem KEY=redir_key.pem +if ! openssl x509 -in "$CERT" -noout -ext subjectAltName 2>/dev/null | grep -qF "IP Address:$ADV"; then + echo "[openfut] reissuing TLS cert with SAN IP:$ADV (was missing it)" + 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:$ADV,IP:127.0.0.1" \ + >/dev/null 2>&1 \ + && echo "[openfut] cert SAN now: $(openssl x509 -in "$CERT" -noout -ext subjectAltName 2>/dev/null | tail -1 | tr -s ' ')" \ + || { echo "[openfut] FATAL: could not reissue TLS cert" >&2; exit 1; } +fi + # name script extra-env declare -a SERVERS=( "lsx|lsx_responder_v2.py|OPENFUT_LSX_EVENT_COUNT=100000" diff --git a/fifa17-recon/tools/openfut-fut.sh b/fifa17-recon/tools/openfut-fut.sh index b4a78b8..e54b194 100755 --- a/fifa17-recon/tools/openfut-fut.sh +++ b/fifa17-recon/tools/openfut-fut.sh @@ -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 }