diff --git a/scripts/check-tls-parity.sh b/scripts/check-tls-parity.sh new file mode 100755 index 0000000..4e94060 --- /dev/null +++ b/scripts/check-tls-parity.sh @@ -0,0 +1,57 @@ +#!/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