Commit Graph
5 Commits
Author SHA1 Message Date
Marc-André Bombeck ed71f334fe fix(ci): fail loud when the GeoLite2 fetch is asked for but produces no MMDBs
When `MAXMIND_LICENSE_KEY` is unset the script still drops the `.empty`
marker and exits 0 — that path is the deliberate maintainer override and
the runtime resilience layer in `src/lib/geo.ts` carries it. When the
key IS set, every failure mode now bubbles up:

- A curl error (401 wrong-key, 403 throttle, 5xx upstream, network
  drop) returns the curl exit code instead of swallowing it into a
  fallback `.empty` marker. The diagnostic names the most common
  causes so the operator can act without re-reading the script.
- A tar extraction failure or a missing MMDB inside the tarball
  returns 1 with a matching message.
- A final assertion at end-of-script confirms both
  `GeoLite2-City.mmdb` and `GeoLite2-ASN.mmdb` ended up non-empty in
  the output directory; otherwise exit 1.

Smoke-tested locally: no-key invocation still exits 0 with the `.empty`
marker; a deliberately bad key now exits 22 with the curl 401 surfaced.
2026-05-16 09:03:07 +02:00
Marc-André Bombeck b5866694c8 fix(ci): isolate the GeoLite2 fetch under a private temp dir so find never walks shared /tmp
The previous resilience pass switched curl errors into a graceful
fallback path but the script still ran `find` against `dirname
$tmp_tarball` (`/tmp` on Linux runners). GitHub Actions runners
populate `/tmp` with `systemd-private-*` directories that are mode
700; `find` walks them, returns exit code 1 on permission-denied,
and under `set -e` aborts the script before our fallback branches
can fire.

Switch each edition to a per-call `mktemp -d` working directory. The
tarball downloads + extracts + finds all happen inside an isolated
directory the script owns. `find` never crosses into shared `/tmp`
state, so permission-denied stops being a class of failure.

Also drop the post-fetch cleanup `find` for date-stamped extraction
dirs — those now live inside `$work_dir` which gets `rm -rf`'d in
every branch.

Verified locally with both a populated download and a bogus key on
a Linux container; both paths exit 0, the bogus-key path writes
`.empty`, the populated path skips the marker.
2026-05-15 22:16:58 +02:00
Marc-André Bombeck 76783431e3 fix(ci): make the GeoLite2 build step tolerate missing keys and MaxMind-side download failures
Build on the previous fetch-script softening (674c6b5b) by hardening
the download path itself, so a transient MaxMind-side issue never
takes the release pipeline down. The runtime gate stays the only
place that surfaces `offline geo unavailable` to the maintainer.

`fetch_edition` now uses `curl --fail-with-body` and captures the
exit code instead of letting `set -e` abort the script. Any non-zero
result — 401 EULA-pending, 403 throttled, 5xx server hiccup, 404
edition-removed, network timeout — flips into the same fallback shape
as the missing-key path: write the `.empty` marker, log a clear
reason on stderr, and return 0 so the second edition still gets its
chance. Tarball-extract and MMDB-locate failures route through the
same fallback. After both fetches, the marker state is summarised:
marker present means at least one edition fell back and the runtime
resolver will use ipwho.is; marker absent means both MMDBs landed
cleanly. The pre-fetch `rm -f "$OUT_DIR/.empty"` from the previous
commit still seeds the clean state; the per-edition fallback re-adds
the marker on demand.

`set -euo pipefail` stays, but every recoverable path either captures
the exit code (`|| curl_exit=$?`) or guards with `if !`, so the
strict-mode abort never reaches a fallback branch.

This unblocks PR #169, where the MAXMIND_LICENSE_KEY secret is set
but MaxMind's account-side GeoLite2 EULA acceptance has not
propagated yet and the download endpoint returns 401. The build no
longer crashes on `curl: (22) The requested URL returned error: 401`.
2026-05-15 22:12:22 +02:00
Marc-André Bombeck 674c6b5b70 fix(ci): soften the GeoLite2 fetch guard so the build never blocks on a missing license key
Replace the hard exit 1 in the `Fetch GeoLite2 databases` step with a
`::warning::` line plus a `.empty` marker in `assets/geolite2/`. The
Dockerfile COPY still has a non-empty source, the build continues, and
the runtime detection layer (next commit) picks up the marker on first
geo lookup and emits a one-shot admin notification. The maintainer is
no longer forced to wire `MAXMIND_LICENSE_KEY` before every release.

`scripts/fetch-geolite2.sh` mirrors the same behaviour: when the key is
unset it drops the marker and exits 0 cleanly; when the key is present
it clears any stale marker before downloading so a freshly populated
directory is not misread as the fallback state.
2026-05-15 22:05:32 +02:00
Marc-André Bombeck 50150e35ef feat(geo): bundle GeoLite2-City and add an offline-first lookup path
Rewrites src/lib/geo.ts so lookupIpLocation reads from the bundled
MaxMind GeoLite2-City MMDB first and falls back to the existing
ipwho.is path only for IPs the offline DB cannot resolve. Adds a
sibling lookupIpAsn that returns { asn, carrier } from the
GeoLite2-ASN MMDB so the admin login-overview can grow a carrier
chip alongside the auth-provider chip.

The MMDB files are too large to vendor in git. scripts/fetch-geolite2.sh
downloads them at build time using a MaxMind license key; the
Dockerfile copies assets/geolite2/ into /opt/geolite2/. When the
license key is absent or the files are missing the image still
builds and the resolver falls back to ipwho.is (v1.4.26 behaviour).

GeoLite2 is CC BY-SA 4.0 — attribution lives on the new public
/about page and in docs/audit/v1427-summary.md.
2026-05-15 16:36:55 +02:00