Restricted-egress & legacy-trust hosts (offline range updates)
unmask keeps the verified-crawler IP ranges (Googlebot, bingbot, GPTBot, ChatGPT-User,
ClaudeBot, Amazonbot, …) fresh by pulling a feed over HTTPS. Two kinds of hosts
cannot make that pull: machines whose trust store predates ISRG Root X1
(very old enterprise distros — the log shows
x509: certificate signed by unknown authority and unmask doctor
warns about crawler IP ranges), and machines with no outbound HTTPS at all.
This page is the runbook for both.
What works with no sync at all
Every release ships a built-in snapshot of all vendor ranges inside the binary, and the
release pipeline refuses to ship a stale one — so upgrading on your normal cadence
keeps the ranges reasonably fresh on its own. Freshness still matters: vendors add ranges,
and features that verify crawlers by address stand down (with a doctor
warning) once the newest available data is more than 30 days old.
The offline update (0.1.33+)
# 1) on any machine that CAN reach unmask.sh: curl -O https://unmask.sh/dl/feed/iprange/bypass-iprange-all.json # 2) transfer the file to the restricted host (scp, USB, whatever fits) scp bypass-iprange-all.json restricted-host:/tmp/ # 3) import it -- no network access, no temporary HTTP server: unmask update-iprange -file /tmp/bypass-iprange-all.json # 4) native mode only: re-render and reload nginx unmask render-nginx && nginx -t && nginx -s reload
The running daemon notices the refreshed files by itself within seconds (0.1.33+), so a forward-auth install needs nothing beyond step 3; a native install adds the render + reload so the ranges land in nginx's own maps.
service unmask restart (or systemctl restart unmask)
before render-nginx — otherwise the next settings save quietly
re-renders the old ranges over your update. (Those versions also lack -file;
point update-iprange -url at a loopback-only temporary HTTP server instead.)
Automating it
# on a bastion that reaches both sides, e.g. weekly:
curl -sO https://unmask.sh/dl/feed/iprange/bypass-iprange-all.json \
&& scp -q bypass-iprange-all.json restricted-host:/tmp/ \
&& ssh restricted-host 'unmask update-iprange -file /tmp/bypass-iprange-all.json \
&& unmask render-nginx && nginx -t && nginx -s reload'
The feed is signed (0.1.33+)
Every feed document is published with a detached ed25519 signature
(bypass-iprange-all.json.sig), produced on a separate signing host — the
hub that serves the feed never holds the key. The daemon verifies the signature
automatically whenever it is present, and refuses a document whose signature does not
match. You can check a transferred file by hand before importing it:
unmask sign-feed -verify /tmp/bypass-iprange-all.json # needs the .sig next to it
This is also what makes an insecure-transport escape hatch offerable: with
sync_insecure_tls: true (or update-iprange -insecure-tls) the
transport certificate check is waived, and in exchange the signature becomes
mandatory — an unsigned or badly signed document is refused outright. Content
verification stands in for the transport verification you switched off. Fixing the trust
store is still the better answer where you can.
Why there is no “skip certificate verification” switch
The ranges this feed carries are trust data: an address inside them bypasses the challenge entirely. Fetching them over an unverified connection would let anyone on the network path inject their own addresses into your bypass list — a clean takeover of the one mechanism that must never be takeable. So unmask offers no insecure-TLS toggle for this feed. The supported answers are the offline import above, or fixing the trust store itself (adding the ISRG Root X1 root certificate to the system CA bundle, which also un-breaks every other modern TLS endpoint on that host).