mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 05:25:40 +09:00
feat(admin): add optional expiry to admin IP bans (#3163)
This commit is contained in:
1 parent
1664050ef7
commit
e9167d96ec
88 files changed
+462
-139
No files matched your search
@@ -14,7 +14,7 @@ Fluxer builds each permission name from `ban:`, the list name with each hyphen w
|
||||
|
||||
## Blocklist types
|
||||
|
||||
`ip` and `email` gate account access and registration. `phrase`, `url`, `url-domain`, `file-sha`, `avatar-hash`, and `profile-substring` gate what an account may post, link, upload, or display. `email` stores nothing but the value. An `ip` row also stores a ban kind, a reason, an expiry, and its creation time.
|
||||
`ip` and `email` gate account access and registration. `phrase`, `url`, `url-domain`, `file-sha`, `avatar-hash`, and `profile-substring` gate what an account may post, link, upload, or display. `email` stores nothing but the value. An `ip` row also stores a ban kind, a reason, an expiry, and its creation time. An `ip` row with an expiry stops applying when it expires, and Fluxer then deletes it.
|
||||
|
||||
| Value | Description |
|
||||
| --- | --- |
|
||||
@@ -104,7 +104,7 @@ One entry of the blocklist catalogue returned by [List blocklists](#list-blockli
|
||||
|
||||
<sup>1</sup> `ip` names `ip`, `email` names `email`, `url-domain` names `domain`, `phrase` names `phrase`, `url` names `url`, `file-sha` names `sha256_hex`, `avatar-hash` names `hashes`, and `profile-substring` names `substrings`
|
||||
|
||||
<sup>2</sup> Empty for `ip`, `email`, and `phrase`. `url` names `category`, `severity`, `source_url`, and `notes`. `url-domain` adds `match_subdomains`, `file-sha` adds `content_type`, and `avatar-hash` adds `reason`. `profile-substring` names `scope`, `reason`, and `notes`
|
||||
<sup>2</sup> `ip` names `duration_hours`. Empty for `email` and `phrase`. `url` names `category`, `severity`, `source_url`, and `notes`. `url-domain` adds `match_subdomains`, `file-sha` adds `content_type`, and `avatar-hash` adds `reason`. `profile-substring` names `scope`, `reason`, and `notes`
|
||||
|
||||
<sup>3</sup> True only for `profile-substring`
|
||||
|
||||
@@ -112,7 +112,7 @@ One entry of the blocklist catalogue returned by [List blocklists](#list-blockli
|
||||
|
||||
<sup>5</sup> True only for `avatar-hash` and `profile-substring`
|
||||
|
||||
<sup>6</sup> True for `url`, `url-domain`, `file-sha`, `avatar-hash`, and `profile-substring`, which is exactly the set whose `fields` array is non-empty
|
||||
<sup>6</sup> True for `url`, `url-domain`, `file-sha`, `avatar-hash`, and `profile-substring`
|
||||
|
||||
### Example
|
||||
|
||||
@@ -163,7 +163,7 @@ One stored row of one blocklist. Every field is present on every entry, and a fi
|
||||
|
||||
<sup>6</sup> Non-null only on `url-domain`
|
||||
|
||||
<sup>7</sup> Non-null only on `ip`. An address added through [Add blocklist entry](#add-blocklist-entry) is written as a permanent ban, so it reads back with the reason `platform_admin_enforcement` and a null `expires_at`
|
||||
<sup>7</sup> Non-null only on `ip`. An address added through [Add blocklist entry](#add-blocklist-entry) reads back with the reason `platform_admin_enforcement`. Its `expires_at` is null unless it was added with a positive `duration_hours`. A row past its `expires_at` is not listed
|
||||
|
||||
<sup>8</sup> Both null on `email` and `phrase`, which store nothing but the value. `created_by_user_id` is also null on `ip`
|
||||
|
||||
@@ -200,9 +200,12 @@ The decision [Check blocklist entry](#check-blocklist-entry) returns for one val
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| banned<sup>1</sup> | boolean | Whether the value is currently blocked by the named blocklist |
|
||||
| expires_at<sup>2</sup> | ?ISO8601 timestamp | When the matching ban expires |
|
||||
|
||||
<sup>1</sup> Reports the decision the enforcement path would make for this value
|
||||
|
||||
<sup>2</sup> Non-null only on `ip` when the matching ban has an expiry. Null when the ban is permanent, when nothing matches, and on every other blocklist
|
||||
|
||||
:::note[A block and a row are different things]
|
||||
A value can be blocked with no row of its own, as a single address covered by a stored CIDR range is. A row can exist while the value reads as not blocked, as an exempt address or an exempt email domain does.
|
||||
:::
|
||||
@@ -244,6 +247,7 @@ Each field is a string, except `hashes` and `substrings`, which are `array[strin
|
||||
| source_url?<sup>4</sup> | string | The feed or evidence URL (1-2048 characters) |
|
||||
| reason?<sup>7</sup> | string | The reason (1-1024 characters) |
|
||||
| notes?<sup>8</sup> | string | The internal note (1-1024 characters) |
|
||||
| duration_hours?<sup>9</sup> | integer | Hours until the row expires (0-8760, default 0) |
|
||||
|
||||
<sup>3</sup> Required by `profile-substring`. Every other blocklist strips `scope`
|
||||
|
||||
@@ -257,6 +261,8 @@ Each field is a string, except `hashes` and `substrings`, which are `array[strin
|
||||
|
||||
<sup>8</sup> Accepted only by `url`, `url-domain`, `file-sha`, `avatar-hash`, and `profile-substring`
|
||||
|
||||
<sup>9</sup> Accepted only by `ip`. `0` writes a permanent row. A positive value writes a row that stops applying after that many hours, and Fluxer then deletes it. Adding an address again replaces its expiry, so a permanent row can become temporary and a temporary row can become permanent
|
||||
|
||||
## List blocklists
|
||||
|
||||
<RouteHeader method="GET" path="/v1/admin/blocklists" auditReason />
|
||||
@@ -375,7 +381,7 @@ The response has no body, so it does not report the canonical form that was stor
|
||||
|
||||
Fluxer checks later requests against the written rows. For every list except `email`, other nodes see the rows after a short propagation delay. No Gateway Dispatch is emitted.
|
||||
|
||||
Fluxer records one [Admin audit entry](/admin-api/#admin-audit-entry-object) per written value, with that value in its metadata. It records an entry for a refused `ip` too, under the action `ban_ip_skipped_exempt`, before it returns the 400.
|
||||
Fluxer records one [Admin audit entry](/admin-api/#admin-audit-entry-object) per written value, with that value in its metadata. An `ip` added with a positive `duration_hours` adds the metadata keys `duration_hours` and `expires_at`. Fluxer records an entry for a refused `ip` too, under the action `ban_ip_skipped_exempt`, before it returns the 400.
|
||||
|
||||
### Rate limit
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@ The error code determines which supplementary members a failure has, and most co
|
||||
|
||||
- `ip_address` is the normalised client address.
|
||||
- `appeal_email` is the address an appeal is sent to.
|
||||
- `appeals_supported` is `true` only for the permanent ban.
|
||||
- `ban_kind` is `permanent` or `temporary_24h`.
|
||||
- `appeals_supported` is `true` for both kinds of ban.
|
||||
- `ban_kind` is `permanent` or `temporary_24h`. `temporary_24h` covers every ban that records an expiry, whatever its length.
|
||||
- `expires_at` is an ISO 8601 timestamp when the ban records an expiry, and `null` otherwise, including on every permanent ban.
|
||||
|
||||
## Validation failure codes
|
||||
@@ -462,7 +462,7 @@ Your IP address {ipAddress} has been permanently blocked from the Fluxer API by
|
||||
|
||||
### `GLOBAL_IP_TEMPORARILY_BANNED`
|
||||
|
||||
Your IP address {ipAddress} has been temporarily blocked from the Fluxer API for 24 hours because of abusive or unusual access patterns
|
||||
Your IP address {ipAddress} has been temporarily blocked from the Fluxer API
|
||||
|
||||
### `GONE`
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ The operation deletes the ban together with the address and email blocks it had.
|
||||
|
||||
It does not restore the removed membership, the guild profile fields and roles that membership had, or the deleted messages. The unbanned account can rejoin through an ordinary invite.
|
||||
|
||||
An instance-wide IP ban is a separate mechanism this operation does not touch. Every request from an address it covers is still refused with 403 `GLOBAL_IP_BANNED` for a permanent ban and 403 `GLOBAL_IP_TEMPORARILY_BANNED` for a 24 hour one.
|
||||
An instance-wide IP ban is a separate mechanism this operation does not touch. Every request from an address it covers is still refused with 403 `GLOBAL_IP_BANNED` for a permanent ban and 403 `GLOBAL_IP_TEMPORARILY_BANNED` for a temporary one.
|
||||
|
||||
### Rate limit
|
||||
|
||||
|
||||
Reference in new issue
Block a user