#!/usr/bin/env bash # # Refresh the iCloud Private Relay egress ranges that functions_geo.php reads. # # geo-relay-update.sh pt # one or more ISO country codes # geo-relay-update.sh # falls back to $GEO_ALLOW_COUNTRIES # # Why this exists: Private Relay sends Safari traffic out through Apple's egress # addresses, which are registered where Apple registered them and not where the # person is. Against the ipdeny lists a subscriber in Aveiro looks foreign, gets # refused, and never hears back. Apple publishes which country each egress range # serves, so the gate can ask the right question about it. # # It writes relay-.v4 / relay-.v6 — same format as the ipdeny lists, read # by the same code. **Only the countries you ask for**, and that is the point: # the published list is 42k IPv4 ranges and 246k IPv6 (855 KB and 6.7 MB), while # Portugal is 611 of them. The whole file would be a 6.7 MB commit every week and # a 6.7 MB parse on every request, to answer a question about one country. # # Keeping it per-country is also what stops this becoming a Relay whitelist: a # range serving France is simply never written, so a French relay user is # refused by the normal path exactly like any other foreign address. # # Apple adds ranges as it adds capacity, so weekly is reasonable: # # 23 4 * * 1 /srv/sites//shared/platform-tools/bin/geo-relay-update.sh pt # # The files are committed, same as the ipdeny data and for the same reason: a # deploy on a box that cannot reach Apple still has working data — as current as # the last commit. set -euo pipefail DATA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/data/geo" URL="${ICLOUD_RELAY_URL:-https://mask-api.icloud.com/egress-ip-ranges.csv}" # Apple serves relay capacity for a country from at least a few dozen ranges. # Fewer than this means the country code was wrong, the format changed, or the # download was truncated — and overwriting a good file with it would quietly # turn every relay visitor back into a foreigner. MIN_ROWS=10 countries=("$@") if [ ${#countries[@]} -eq 0 ]; then read -r -a countries <<< "${GEO_ALLOW_COUNTRIES//[,;]/ }" fi if [ ${#countries[@]} -eq 0 ]; then echo "uso: $(basename "$0") [cc...] (ou defina GEO_ALLOW_COUNTRIES)" >&2 exit 2 fi tmp="$(mktemp -d)" trap 'rm -rf "$tmp"' EXIT echo "iCloud Private Relay: a descarregar $URL" if ! curl -fsS --max-time 60 "$URL" -o "$tmp/raw.csv"; then echo "FALHOU o download — os ficheiros anteriores mantêm-se." >&2 exit 1 fi failed=0 for cc in "${countries[@]}"; do cc="$(echo "$cc" | tr '[:upper:]' '[:lower:]')" [ -n "$cc" ] || continue up="$(echo "$cc" | tr '[:lower:]' '[:upper:]')" # O formato da Apple é: cidr,countryCode,region,city, — só as duas # primeiras colunas interessam, e uma linha que não seja exactamente isso é # descartada em vez de adivinhada: uma mudança de formato tem de aparecer # como "poucas gamas" e parar o script, não como um ficheiro meio lido. awk -F',' -v want="$up" ' $1 ~ /\// && toupper($2) == want { if ($1 ~ /:/) { print $1 > v6 } else { print $1 > v4 } } ' v4="$tmp/$cc.v4" v6="$tmp/$cc.v6" "$tmp/raw.csv" || true touch "$tmp/$cc.v4" "$tmp/$cc.v6" rows4=$(wc -l < "$tmp/$cc.v4" | tr -d ' ') rows6=$(wc -l < "$tmp/$cc.v6" | tr -d ' ') total=$((rows4 + rows6)) if [ "$total" -lt "$MIN_ROWS" ]; then echo "FALHOU $up: só $total gamas (mínimo $MIN_ROWS). Ficheiros anteriores mantidos." >&2 failed=1 continue fi # Ordenados no disco para um git diff entre duas actualizações se conseguir # ler. O módulo volta a ordená-los ao carregar, porque esta ordem é textual # e a procura precisa da ordem dos bytes empacotados. sort -u "$tmp/$cc.v4" -o "$tmp/$cc.v4" sort -u "$tmp/$cc.v6" -o "$tmp/$cc.v6" mkdir -p "$DATA_DIR" mv "$tmp/$cc.v4" "$DATA_DIR/relay-$cc.v4" mv "$tmp/$cc.v6" "$DATA_DIR/relay-$cc.v6" echo "OK $up: $rows4 gamas IPv4, $rows6 IPv6 → $DATA_DIR/relay-$cc.{v4,v6}" done exit "$failed"