#!/usr/bin/env bash # # Refresh the per-country CIDR lists that functions_geo.php reads. # # geo-update.sh pt # one or more ISO country codes # geo-update.sh # falls back to $GEO_ALLOW_COUNTRIES # # Source is ipdeny.com, which publishes one aggregated zone file per country # from the RIR allocation data. Allocations move slowly, so monthly is plenty: # # 17 4 3 * * /srv/sites//shared/platform-tools/bin/geo-update.sh pt # # The files are committed to the repo. This script only refreshes them, so a # deploy on a box that cannot reach ipdeny still has working data — it is just # as current as the last commit. That is the whole reason for committing them. # # Nothing is overwritten unless the download passed every check: a truncated # file, an HTML error page served with 200, or a suspiciously short list would # otherwise turn into "no visitor is in Portugal" — which, with GEO_FAIL_CLOSED, # locks the country out of its own site. set -euo pipefail DATA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/data/geo" V4_URL="https://www.ipdeny.com/ipblocks/data/countries" V6_URL="https://www.ipdeny.com/ipv6/ipaddresses/blocks" # A real country zone has hundreds of ranges. Anything under this is a bad # download, not a small country -- the smallest national allocations still run # to dozens of blocks, and we would rather keep yesterday's file. MIN_V4_LINES=20 MIN_V6_LINES=1 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 mkdir -p "$DATA_DIR" tmp="$(mktemp -d)" trap 'rm -rf "$tmp"' EXIT status=0 fetch_zone() { local cc="$1" family="$2" url="$3" min="$4" local out="$DATA_DIR/${cc}.${family}" local work="$tmp/${cc}.${family}" if ! curl -fsS --max-time 60 -o "$work" "$url"; then echo " ${cc}.${family}: download falhou -- fica o ficheiro anterior" >&2 return 1 fi # Every line must be a bare CIDR. An HTML error page fails here, which is # the check that actually matters: those arrive with a 200. if grep -qvE '^[0-9a-fA-F:.]+/[0-9]{1,3}$' "$work"; then echo " ${cc}.${family}: conteúdo não é uma lista de CIDR -- ignorado" >&2 return 1 fi local n n=$(wc -l < "$work") if [ "$n" -lt "$min" ]; then echo " ${cc}.${family}: só $n linhas (mínimo $min) -- ignorado" >&2 return 1 fi sort -V -o "$work" "$work" if [ -f "$out" ] && cmp -s "$work" "$out"; then echo " ${cc}.${family}: sem alterações ($n)" return 0 fi mv "$work" "$out" echo " ${cc}.${family}: actualizado ($n ranges)" } for cc in "${countries[@]}"; do cc="$(echo "$cc" | tr '[:upper:]' '[:lower:]')" [[ "$cc" =~ ^[a-z]{2}$ ]] || { echo " '$cc' não é um código ISO -- ignorado" >&2; status=1; continue; } echo "$cc:" fetch_zone "$cc" v4 "$V4_URL/${cc}.zone" "$MIN_V4_LINES" || status=1 # IPv6 is published separately and some countries have no file at all; a # missing v6 list is not an error, the v4 list still gates those visitors. fetch_zone "$cc" v6 "$V6_URL/${cc}.zone" "$MIN_V6_LINES" || true done exit $status