#!/usr/bin/env bash # Every FIFA-facing TLS service must present the SAME certificate. # # check-tls-parity.sh [ ...] # # WHY # # FIFA 17's ProtoSSL caches the server certificate it saw for a backend. The # redirector is the first TLS connection of a session, so whatever cert it # presents becomes the one the client expects from that backend. If a later # service — roster, UTAS — presents a different cert, that handshake fails. # # Python's socketserver makes this invisible: a handshake failure at accept() # raises ssl.SSLError, which subclasses OSError and is silently swallowed by # _handle_request_noblock. No request log, no stderr, nothing. The observable # symptom is a client that logs in fine and then cannot download the FUT Squad # Update, with every server looking healthy. # # This cost two live gates. The Rust redirector was pointed at the repo's # fifa17-recon/tools/redir_cert.pem while the running container serves a # different cert baked into its image — so the A/B had two variables, TLS # implementation AND certificate identity, when it was meant to have one. # # Run this before arming any gate that puts a new TLS implementation in front # of the client. set -uo pipefail [[ $# -ge 2 ]] || { echo "usage: check-tls-parity.sh [...]" >&2; exit 2; } fingerprint() { timeout 8 openssl s_client -connect "$1" /dev/null \ | openssl x509 -noout -fingerprint -sha256 2>/dev/null | cut -d= -f2 } ref=""; ref_target=""; rc=0 for target in "$@"; do fp="$(fingerprint "$target")" if [[ -z "$fp" ]]; then printf " %-24s %s\n" "$target" "NO TLS / unreachable" rc=1 continue fi printf " %-24s %s\n" "$target" "$fp" if [[ -z "$ref" ]]; then ref="$fp"; ref_target="$target" elif [[ "$fp" != "$ref" ]]; then rc=1 fi done if [[ $rc -eq 0 ]]; then echo "OK: all $# services present the same certificate" else echo "REFUSING: certificate mismatch across the FIFA-facing stack (reference: $ref_target)" >&2 echo " A client that caches the first cert it sees will fail on the others." >&2 fi exit $rc