Add text format, CORS, and an importer so other instances can use the list
docker-build-push / build-push (push) Successful in 4s

Mastodon has no RBL/DNSBL support — nothing in it consults an outside
list when deciding whether to accept a signup, confirmed against v4.7.1
(the only outbound lookup in that path is EmailMxValidator's MX
resolution, which feeds the local email_domain_blocks table). So a
shared blocklist has to be imported rather than queried live, the same
way Garden Fence and other community lists propagate. These are the
pieces that make ours importable.

- ?format=txt on both per-list endpoints: one entry per line under a '#'
  comment header, the form blocklists are traded in and the form a cron
  job can consume without a JSON parser. The combined /api/blocks stays
  JSON-only — a text rendering would mix CIDRs with domains in one file,
  defeating the point. An unrecognised ?format is a 400 rather than a
  silent fallback to JSON, and the enabled-gate still runs first so a
  disabled deployment 404s either way.
- Access-Control-Allow-Origin: * on blocklist responses, so browser-side
  admin tooling can read them. The data is already public.
- bin/yttrx-blocklist-import: standard-library-only importer another
  admin can run against their own instance. Dry run unless --apply;
  422 from Mastodon counts as "already blocked", not an error.

The importer defaults IP entries to sign_up_requires_approval rather
than the sign_up_block yttrx uses itself. The entries are whole ASN
routes, which suits yttrx because a flagged signup here is nearly always
datacenter/VPN/proxy space — that calibration isn't transferable, so an
importer gets the reviewable severity by default and has to opt into the
hard one.
This commit is contained in:
pmb committed 2026-09-13 00:59:12 -07:00
1 parent af49aab79c
commit 8966de2a1d
5 files changed
+331 -9

No files matched your search

+49
View File
@@ -224,6 +224,54 @@ endpoints, all `GET`, all accepting an optional `?limit=N`:
}
```
### Plain-text format
Both per-list endpoints also take `?format=txt`, which renders one entry per
line under a `#` comment header — the form blocklists are normally traded in,
and the form another admin's cron job can consume without a JSON parser:
```bash
curl -s 'https://hooks.yttrx.com/api/blocks/ip-ranges?format=txt' | grep -v '^#'
curl -s 'https://hooks.yttrx.com/api/blocks/email-domains?format=txt' | grep -v '^#'
```
The combined `/api/blocks` is JSON-only: a text rendering would put CIDRs and
domains in one file, and the point of the text form is that it pipes straight
into something expecting one kind of entry. Responses carry
`Access-Control-Allow-Origin: *` so browser-side tooling can read them.
### Importing it elsewhere (`bin/yttrx-blocklist-import`)
Mastodon has **no RBL/DNSBL support** — nothing in it consults an outside list
when deciding whether to accept a signup. (The only outbound lookup in that
path is `EmailMxValidator`'s MX resolution, and that feeds the *local*
`email_domain_blocks` table.) So another instance can't point at this feed
live; it has to import, the same way [Garden Fence](https://gardenfence.github.io)
and other community lists propagate.
`bin/yttrx-blocklist-import` does that — standard library only, no install:
```bash
# dry run (the default — nothing is written)
./bin/yttrx-blocklist-import --instance https://your.instance --token "$TOKEN"
# actually write the blocks
./bin/yttrx-blocklist-import --instance https://your.instance --token "$TOKEN" --apply
```
The token needs `admin:write:ip_blocks` and `admin:write:email_domain_blocks`
(the script's `--help` has a Rails-console snippet that mints one). Entries
already present come back `422` and are counted as "already blocked", not as
errors. `--min-signups N` skips one-off entries, `--ips-only`/`--domains-only`
take one list, `--limit` caps each.
**On severity:** the IP entries are whole networks, because on yttrx a flagged
signup is almost always datacenter/VPN/proxy space where the next attempt comes
from a neighbouring address. That's calibrated to yttrx's traffic, not someone
else's, so the importer defaults to `sign_up_requires_approval` — a flagged
signup lands in the importer's moderation queue rather than being rejected —
even though yttrx runs these at `sign_up_block` itself. `--ip-severity
sign_up_block` opts into the harder behaviour.
**Off by default.** These paths are served *without authentication*, so they
404 unless `BLOCKS_API_ENABLED=true` is set — publishing which ranges and
domains are already blocked also tells a would-be spam signup which ones to
@@ -268,6 +316,7 @@ only once the block actually landed on the Mastodon side (a failed
| `.env.example` | Config template (copy to `.env`, never commit) |
| `nginx/hooks.yttrx.com.conf` | nginx site for admin.yttrx.com (webhook, healthz, blocklist API) |
| `bin/welcomebot-logs`, `bin/welcomebot-signups`, `bin/welcomebot-suspicious` | Ops CLIs installed to `/root/bin` on admin.yttrx.com |
| `bin/yttrx-blocklist-import` | Importer other admins can run to pull this list into their own instance |
| `bin/backfill-block-cidr.py` | One-shot: reconstruct `block_cidr` for rows predating it (run 2026-09-13) |
| `test_local.py` | Offline smoke test (no network) |